Last active
November 17, 2025 10:53
-
-
Save panreel/3fb125360a7eed5cd2409eda9a3aefbc to your computer and use it in GitHub Desktop.
Unity/C# button callback utility to initiate APK download and installation on Meta Quest devices.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System.Collections; | |
| using System.IO; | |
| using UnityEngine; | |
| using UnityEngine.Networking; | |
| public class ApkDownloaderInstaller : MonoBehaviour | |
| { | |
| // Replace this with your real APK link (must be direct download) | |
| private const string ApkUrl = "https://github.com/Igalia/wolvic/releases/download/v1.8.2/Wolvic-oculusvr-arm64-gecko-metaStore-release.apk"; | |
| private string localPath; | |
| // Called by your UI Button | |
| public void OnInstallButton() | |
| { | |
| StartCoroutine(DownloadAndInstall()); | |
| } | |
| private IEnumerator DownloadAndInstall() | |
| { | |
| localPath = Path.Combine(Application.persistentDataPath, "MyApp.apk"); | |
| Debug.Log($"[Installer] Downloading to: {localPath}"); | |
| if (File.Exists(localPath)) | |
| File.Delete(localPath); | |
| using (UnityWebRequest www = UnityWebRequest.Get(ApkUrl)) | |
| { | |
| www.downloadHandler = new DownloadHandlerFile(localPath); | |
| yield return www.SendWebRequest(); | |
| if (www.result != UnityWebRequest.Result.Success) | |
| { | |
| Debug.LogError("[Installer] Download failed: " + www.error); | |
| yield break; | |
| } | |
| } | |
| Debug.Log("[Installer] Download complete. Launching installer..."); | |
| #if UNITY_ANDROID && !UNITY_EDITOR | |
| TryInstallApk(localPath); | |
| #else | |
| Debug.LogWarning("[Installer] Only works on Android device."); | |
| #endif | |
| } | |
| #if UNITY_ANDROID && !UNITY_EDITOR | |
| private void TryInstallApk(string apkPath) | |
| { | |
| using (var unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer")) | |
| using (var currentActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity")) | |
| { | |
| try | |
| { | |
| // ---- STEP 1: Get File URI via FileProvider ---- | |
| var file = new AndroidJavaObject("java.io.File", apkPath); | |
| var fileProvider = new AndroidJavaClass("androidx.core.content.FileProvider"); | |
| var uri = fileProvider.CallStatic<AndroidJavaObject>( | |
| "getUriForFile", | |
| currentActivity, | |
| currentActivity.Call<string>("getPackageName") + ".provider", | |
| file | |
| ); | |
| // ---- STEP 2: Build install Intent ---- | |
| var intent = new AndroidJavaObject("android.content.Intent", "android.intent.action.VIEW"); | |
| intent.Call<AndroidJavaObject>("setDataAndType", uri, "application/vnd.android.package-archive"); | |
| int FLAG_GRANT_READ_URI_PERMISSION = 1; | |
| int FLAG_ACTIVITY_NEW_TASK = 0x10000000; | |
| intent.Call<AndroidJavaObject>("addFlags", FLAG_GRANT_READ_URI_PERMISSION); | |
| intent.Call<AndroidJavaObject>("addFlags", FLAG_ACTIVITY_NEW_TASK); | |
| // ---- STEP 3: Request permission if needed ---- | |
| bool canInstall = false; | |
| int sdkInt = new AndroidJavaClass("android.os.Build$VERSION").GetStatic<int>("SDK_INT"); | |
| if (sdkInt >= 26) | |
| { | |
| var pkgManager = currentActivity.Call<AndroidJavaObject>("getPackageManager"); | |
| canInstall = pkgManager.Call<bool>("canRequestPackageInstalls"); | |
| if (!canInstall) | |
| { | |
| // open settings to grant permission | |
| var settingsIntent = new AndroidJavaObject( | |
| "android.content.Intent", | |
| "android.settings.MANAGE_UNKNOWN_APP_SOURCES" | |
| ); | |
| var uriClass = new AndroidJavaClass("android.net.Uri"); | |
| var pkgUri = uriClass.CallStatic<AndroidJavaObject>( | |
| "parse", | |
| "package:" + currentActivity.Call<string>("getPackageName") | |
| ); | |
| settingsIntent.Call<AndroidJavaObject>("setData", pkgUri); | |
| settingsIntent.Call<AndroidJavaObject>("addFlags", FLAG_ACTIVITY_NEW_TASK); | |
| currentActivity.Call("startActivity", settingsIntent); | |
| Debug.LogWarning("[Installer] Please enable 'Install unknown apps' and press the button again."); | |
| return; | |
| } | |
| } | |
| // ---- STEP 4: Launch the installer ---- | |
| currentActivity.Call("startActivity", intent); | |
| Debug.Log("[Installer] Installer launched."); | |
| } | |
| catch (AndroidJavaException e) | |
| { | |
| Debug.LogError("[Installer] Exception: " + e.Message); | |
| } | |
| } | |
| } | |
| #endif | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment