Compare commits
2 Commits
fe3929b41c
...
8d1b57d7ed
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8d1b57d7ed | ||
|
|
836419484c |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -2,11 +2,11 @@ apply plugin: "com.android.application"
|
|||||||
apply plugin: "org.jetbrains.kotlin.android"
|
apply plugin: "org.jetbrains.kotlin.android"
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace "dev.yuzu.gca"
|
namespace 'dev.yuzu.gca'
|
||||||
compileSdk 35
|
compileSdk 35
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "dev.yuzu.gca"
|
applicationId 'dev.yuzu.gca'
|
||||||
minSdk 24
|
minSdk 24
|
||||||
targetSdk 35
|
targetSdk 35
|
||||||
versionCode 8
|
versionCode 8
|
||||||
|
|||||||
@@ -1,35 +1,28 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
|
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
|
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||||
<application
|
<application android:name=".MainApplication" android:label="Yuzu GCA v0.1.0" android:allowBackup="false" android:supportsRtl="true" android:theme="@style/Theme.AppCompat.DayNight">
|
||||||
android:name=".MainApplication"
|
<meta-data android:name="expo.modules.updates.ENABLED" android:value="true"/>
|
||||||
android:label="Yuzu GCA v0.1.0"
|
<meta-data android:name="expo.modules.updates.EXPO_RUNTIME_VERSION" android:value="@string/expo_runtime_version"/>
|
||||||
android:allowBackup="false"
|
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_CHECK_ON_LAUNCH" android:value="ALWAYS"/>
|
||||||
android:supportsRtl="true"
|
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_LAUNCH_WAIT_MS" android:value="3000"/>
|
||||||
android:theme="@style/Theme.AppCompat.DayNight">
|
<meta-data android:name="expo.modules.updates.EXPO_UPDATE_URL" android:value="https://git.childish-ghost.com/LukeMackin/Yuzu-GCA/raw/branch/main/ota/expo-manifest.json"/>
|
||||||
|
<activity android:name=".MainActivity" android:label="Yuzu GCA v0.1.0" android:launchMode="singleTask" android:exported="true" android:screenOrientation="portrait" android:windowSoftInputMode="adjustResize">
|
||||||
<activity
|
|
||||||
android:name=".MainActivity"
|
|
||||||
android:label="Yuzu GCA v0.1.0"
|
|
||||||
android:launchMode="singleTask"
|
|
||||||
android:exported="true">
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN"/>
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
<category android:name="android.intent.category.LAUNCHER"/>
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW"/>
|
||||||
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
|
<category android:name="android.intent.category.BROWSABLE"/>
|
||||||
|
<data android:scheme="dev.yuzu.gca"/>
|
||||||
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
<provider android:name="androidx.core.content.FileProvider" android:authorities="${applicationId}.fileprovider" android:exported="false" android:grantUriPermissions="true">
|
||||||
<provider
|
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths"/>
|
||||||
android:name="androidx.core.content.FileProvider"
|
|
||||||
android:authorities="${applicationId}.fileprovider"
|
|
||||||
android:exported="false"
|
|
||||||
android:grantUriPermissions="true">
|
|
||||||
<meta-data
|
|
||||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
|
||||||
android:resource="@xml/file_paths" />
|
|
||||||
</provider>
|
</provider>
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
||||||
@@ -1,5 +1,24 @@
|
|||||||
package dev.yuzu.gca
|
package dev.yuzu.gca
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
|
import com.facebook.react.PackageList
|
||||||
|
import com.facebook.react.ReactApplication
|
||||||
|
import com.facebook.react.ReactNativeHost
|
||||||
|
import com.facebook.react.ReactPackage
|
||||||
|
import com.facebook.react.defaults.DefaultReactNativeHost
|
||||||
|
import com.facebook.soloader.SoLoader
|
||||||
|
|
||||||
class MainApplication : Application()
|
class MainApplication : Application(), ReactApplication {
|
||||||
|
override val reactNativeHost: ReactNativeHost =
|
||||||
|
object : DefaultReactNativeHost(this) {
|
||||||
|
override fun getPackages(): List<ReactPackage> =
|
||||||
|
PackageList(this).packages.apply { add(PackageInstallerPackage()) }
|
||||||
|
override fun getJSMainModuleName() = "index"
|
||||||
|
override fun getUseDeveloperSupport() = false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate() {
|
||||||
|
super.onCreate()
|
||||||
|
SoLoader.init(this, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package dev.yuzu.gca
|
||||||
|
|
||||||
|
import android.app.PendingIntent
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.pm.PackageInstaller
|
||||||
|
import com.facebook.react.bridge.*
|
||||||
|
import java.io.File
|
||||||
|
import java.security.MessageDigest
|
||||||
|
|
||||||
|
class PackageInstallerModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
|
||||||
|
override fun getName() = "PackageInstallerModule"
|
||||||
|
|
||||||
|
@ReactMethod
|
||||||
|
fun installApk(filePath: String, expectedSha256: String, promise: Promise) {
|
||||||
|
try {
|
||||||
|
val context = reactApplicationContext
|
||||||
|
val apkFile = File(filePath)
|
||||||
|
if (!apkFile.exists()) { promise.reject("NOT_FOUND", "APK not found"); return }
|
||||||
|
|
||||||
|
if (!context.packageManager.canRequestPackageInstalls()) {
|
||||||
|
promise.reject("PERMISSION", "Install permission not granted"); return
|
||||||
|
}
|
||||||
|
|
||||||
|
// SHA256 verify
|
||||||
|
val sha256 = MessageDigest.getInstance("SHA-256")
|
||||||
|
apkFile.inputStream().use { input ->
|
||||||
|
val buf = ByteArray(8192); var read: Int
|
||||||
|
while (input.read(buf).also { read = it } != -1) sha256.update(buf, 0, read)
|
||||||
|
}
|
||||||
|
if (sha256.digest().joinToString("") { "%02x".format(it) } != expectedSha256) {
|
||||||
|
promise.reject("SHA256", "SHA256 mismatch"); return
|
||||||
|
}
|
||||||
|
|
||||||
|
val pi = context.packageManager.packageInstaller
|
||||||
|
val sessionId = pi.createSession(PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL))
|
||||||
|
val session = pi.openSession(sessionId)
|
||||||
|
try {
|
||||||
|
apkFile.inputStream().use { input ->
|
||||||
|
session.openWrite("package", 0, apkFile.length()).use { output ->
|
||||||
|
input.copyTo(output); session.fsync(output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val launchIntent = context.packageManager.getLaunchIntentForPackage(context.packageName)
|
||||||
|
?: run { promise.reject("NO_INTENT", "No launch intent"); return }
|
||||||
|
session.commit(PendingIntent.getActivity(context, 0, launchIntent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE).intentSender)
|
||||||
|
promise.resolve("installed")
|
||||||
|
} finally { session.close() }
|
||||||
|
} catch (e: Exception) { promise.reject("ERROR", e.message) }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package dev.yuzu.gca
|
||||||
|
|
||||||
|
import com.facebook.react.ReactPackage
|
||||||
|
import com.facebook.react.bridge.NativeModule
|
||||||
|
import com.facebook.react.bridge.ReactApplicationContext
|
||||||
|
import com.facebook.react.uimanager.ViewManager
|
||||||
|
|
||||||
|
class PackageInstallerPackage : ReactPackage {
|
||||||
|
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> =
|
||||||
|
listOf(PackageInstallerModule(reactContext))
|
||||||
|
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> = emptyList()
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user