This website is under construction!

How to Download, Install, Check version, uninstall and Launch APKs in Unity: A Complete Guide

In mobile game development, especially when targeting Android devices, managing APK files—installing, updating, and uninstalling—becomes essential for delivering seamless experiences. However, implementing these features can sometimes be a daunting task for Unity developers. That’s where the APKUtils package comes in handy. It provides an easy way to check if an app is installed, launch it, uninstall it, or check the app version directly from Unity.

In this guide, we’ll walk you through setting up and using the APKUtils.cs class for managing APK files in Unity. Along with the code, we’ll also show you how to configure the Android ManifestGradle, and related files to ensure smooth integration.

As Unity developers, we are often tasked with creating apps that require APK management. This includes ensuring that apps are installed, checking the installed app’s version, launching the app from Unity, and sometimes even uninstalling it. Without a proper system in place, managing APK files on Android can get tricky.

Unity doesn’t natively provide APK management utilities, so developers must either implement their own solution or use third-party tools. With APKUtils, we’ve created an all-in-one package that simplifies APK management tasks.

Before diving into the implementation, let’s quickly go over the features this package provides:

  1. Check if the app is installed – Ensure that the app is already installed before performing other operations.

  2. Get the app version – Retrieve the version name of an installed APK.

  3. Install an APK – Allow APK installations directly from Unity.

  4. Uninstall an APK – Manage app removal via the system’s package manager.

  5. Launch the APK – Start the installed app right from within Unity.

  6. Request install permissions – Ensure the app has permission to install APKs on Android 8.0 and above.

  7. Handle file permissions – Request appropriate permissions to access external storage for reading APK files.

First, let’s import the APKUtils.cs file into your Unity project. This script contains methods to check if an app is installed, get its version, install, uninstall, and launch apps. You can place this script in a folder like Assets/Scripts/ to keep it organized.

Here is a breakdown of the most important methods in the script:

This method extracts the package name from an APK file. It uses Android’s PackageManager to get the packageName:

public static string GetPackageNameFromApk(string apkPath)
{

#if UNITY_ANDROID
        using (AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
        using (AndroidJavaObject context = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity"))
        using (AndroidJavaObject packageManager = context.Call<AndroidJavaObject>("getPackageManager"))
        {
            // Call the Android PackageManager to get the package information
            AndroidJavaObject packageInfo = packageManager.Call<AndroidJavaObject>("getPackageArchiveInfo", apkPath, 0);

            if (packageInfo != null)
            {
                // Get the package name
                string packageName = packageInfo.Get<string>("packageName");
                return packageName;
            }
            else
            {
                Debug.LogError("Failed to retrieve package info from APK.");
                return null;
            }
        }
#else
        Debug.LogError("This method is only supported on Android.");
        return null;
#endif
}

It checks whether a particular app (using its package name) is installed on the device:

public static bool IsAppInstalled(string packageName)
{

#if UNITY_ANDROID
        try
        {
            using (AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
            using (AndroidJavaObject currentActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity"))
            using (AndroidJavaObject packageManager = currentActivity.Call<AndroidJavaObject>("getPackageManager"))
            {
                // Check if the package is installed
                AndroidJavaObject packageInfo = packageManager.Call<AndroidJavaObject>("getPackageInfo", packageName, 0);

                return packageInfo != null; // If no exception, the package is installed
            }
        }
        catch (AndroidJavaException e)
        {
            if (e.Message.Contains("PackageManager$NameNotFoundException"))
            {
                // The package is not installed
                return false;
            }
            else
            {
                // An unexpected error occurred
                Debug.LogError("Error checking package installation: " + e.Message);
                return false;
            }
        }
#else
        Debug.LogError("App installation status check is only supported on Android.");
        return false;
#endifr
}

This method retrieves the version of the installed app using the package name:

public static string GetInstalledAppVersion(string packageName)
{

#if UNITY_ANDROID
        using (AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
        using (AndroidJavaObject currentActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity"))
        using (AndroidJavaObject packageManager = currentActivity.Call<AndroidJavaObject>("getPackageManager"))
        {
            try
            {
                AndroidJavaObject packageInfo = packageManager.Call<AndroidJavaObject>("getPackageInfo", packageName, 0);
                return packageInfo.Get<string>("versionName");
            }
            catch (AndroidJavaException)
            {
                Debug.LogError($"Failed to get version name for package: {packageName}");
                return null;
            }
        }
#else
        Debug.LogError("Getting app version is only supported on Android.");
        return null;
#endif
}

This function installs an APK from the specified path using FileProvider to share the APK URI:

public static void InstallApk(string apkPath, Action<bool> result)
{ string packageName = ApkUtils.GetPackageNameFromApk(apkPath);


 if (string.IsNullOrEmpty(packageName))
 {

     Debug.LogError("Failed to extract package name from APK." + apkPath);
     return;
 }


 try
 {
     AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
     AndroidJavaObject currentActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");

     // Ensure the app has permission to read external storage
     //  RequestExternalStoragePermission();


     // Use FileProvider to get URI for APK file
     AndroidJavaClass fileProvider = new AndroidJavaClass("androidx.core.content.FileProvider");
     AndroidJavaObject context = currentActivity.Call<AndroidJavaObject>("getApplicationContext");
     AndroidJavaObject file = new AndroidJavaObject("java.io.File", apkPath);

     string authority = context.Call<string>("getPackageName") + ".fileprovider";
     AndroidJavaObject uri = fileProvider.CallStatic<AndroidJavaObject>("getUriForFile", context, authority, file);

     // Intent to install APK
     AndroidJavaObject intent = new AndroidJavaObject("android.content.Intent", "android.intent.action.VIEW");
     intent.Call<AndroidJavaObject>("setDataAndType", uri, "application/vnd.android.package-archive");
     intent.Call<AndroidJavaObject>("addFlags", 1);  // FLAG_GRANT_READ_URI_PERMISSION
     intent.Call<AndroidJavaObject>("addFlags", 268435456); // FLAG_ACTIVITY_NEW_TASK


     //  Debug.Log(" System box popup");

     currentActivity.Call("startActivity", intent);
 }
 catch (System.Exception e)
 {
     Debug.LogError("Error during APK installation: " + e.Message);
 }
}

It uninstalls an app by using an Intent to trigger the uninstallation process:

public static void UninstallApp(string packageName, Action<bool> result)
{
#if UNITY_ANDROID
        if (ApkUtils.IsAppInstalled(packageName))
        {
            //  Debug.Log($"Uninstalling {packageName}");
            try
            {

                // Obtain the UnityPlayer activity
                AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
                AndroidJavaObject currentActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");

                // Create the Intent to uninstall the package
                AndroidJavaObject intent = new AndroidJavaObject("android.content.Intent", "android.intent.action.DELETE");

                // Create Uri object for the package name
                AndroidJavaClass uriClass = new AndroidJavaClass("android.net.Uri");
                AndroidJavaObject uri = uriClass.CallStatic<AndroidJavaObject>("parse", "package:" + packageName);

                // Set the Uri data to the intent
                intent.Call<AndroidJavaObject>("setData", uri);

                // Set necessary flags for starting a new activity
                intent.Call<AndroidJavaObject>("addFlags", 268435456); // FLAG_ACTIVITY_NEW_TASK


                // Start the activity with the intent to uninstall
                currentActivity.Call("startActivity", intent);

                // Debug.Log("Uninstall intent launched for package: " + packageName);
            }
            catch (System.Exception e)
            {
                Debug.LogError("Failed to uninstall APK: " + e.Message);
            }
        }
        else
        {
            Debug.Log(" Package name not found");
        }

#else
        Debug.LogError("Application uninstalling is only supported on Android.");
#endif
}

Launches an app directly from Unity using the package name:

public static void LaunchApp(string packageName)
{
#if UNITY_ANDROID
        if (ApkUtils.IsAppInstalled(packageName))
        {
            // Debug.Log("Inside the Launch game method: " + packageName + " is installed.");

            using (AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
            using (AndroidJavaObject currentActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity"))
            using (AndroidJavaObject packageManager = currentActivity.Call<AndroidJavaObject>("getPackageManager"))
            {
                AndroidJavaObject launchIntent = packageManager.Call<AndroidJavaObject>("getLaunchIntentForPackage", packageName);

                if (launchIntent != null)
                {
                    launchIntent.Call<AndroidJavaObject>("setFlags", 268435456); // Intent.FLAG_ACTIVITY_NEW_TASK


                    currentActivity.Call("startActivity", launchIntent);

                    Debug.Log("App launched successfully.");
                }
                else
                {
                    Debug.LogError($"No launch intent found for package {packageName}. The app might not have a launcher activity.");
                }
            }
        }
        else
        {
            Debug.LogError($"App with package name {packageName} is not installed.");

        }
#else
        Debug.LogError("Game launching is only supported on Android.");
#endif
}

Unity doesn’t automatically request all the permissions you might need, especially on Android 8.0 (API level 26) and above. APKUtils includes a method for checking whether the app has install permissions and requesting it if necessary.

    public static bool HasInstallPermission()
    {
#if UNITY_ANDROID
        if (!AndroidVersionIsAtLeast(26))
        {
            // If the Android version is below 26, permission is not needed
            return true;
        }

        AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
        AndroidJavaObject currentActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");

        AndroidJavaObject packageManager = currentActivity.Call<AndroidJavaObject>("getPackageManager");
        bool hasInstallPermission = packageManager.Call<bool>("canRequestPackageInstalls");

        return hasInstallPermission;
#else
        return false;
#endif
    }

    public static void RequestInstallPermission()
    {
#if UNITY_ANDROID


        AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
        AndroidJavaObject currentActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");

        if (AndroidVersionIsAtLeast(26))
        {
            AndroidJavaObject packageManager = currentActivity.Call<AndroidJavaObject>("getPackageManager");
            string packageName = currentActivity.Call<string>("getPackageName");

            bool hasInstallPermission = packageManager.Call<bool>("canRequestPackageInstalls");

            if (!hasInstallPermission)
            {
                // Create an Intent for the settings page
                AndroidJavaObject intent = new AndroidJavaObject("android.content.Intent",
                    "android.settings.MANAGE_UNKNOWN_APP_SOURCES");

                // Create Uri object for the package
                AndroidJavaClass uriClass = new AndroidJavaClass("android.net.Uri");
                AndroidJavaObject uri = uriClass.CallStatic<AndroidJavaObject>("parse", "package:" + packageName);

                // Set the Uri data to the intent
                intent.Call<AndroidJavaObject>("setData", uri);

                // Start the activity
                currentActivity.Call("startActivity", intent);
            }
        }
#endif
    }
// Optional: Request for the external storage permission if needed.
     public static void RequestExternalStoragePermission()
     {

         //Check and request External storage permission in Android.
         if (!UnityEngine.Android.Permission.HasUserAuthorizedPermission(UnityEngine.Android.Permission.ExternalStorageWrite))
         {
           //  DebugUI.Instance.SetUp("Request External Storage Permissions");
             //   GUI.Label(new Rect(10, 10, 200, 20), "Request External Storage Permissions");
             UnityEngine.Android.Permission.RequestUserPermission(UnityEngine.Android.Permission.ExternalStorageWrite);
         }
     }
    private static bool AndroidVersionIsAtLeast(int version)
    {
#if UNITY_ANDROID
        AndroidJavaClass versionClass = new AndroidJavaClass("android.os.Build$VERSION");
        int sdkInt = versionClass.GetStatic<int>("SDK_INT");
        return sdkInt >= version;
#else
        return false;
#endif
    }

You can call these methods at runtime to ensure your app can install other APKs on the device.

To interact with APKs and manage installations, you need to configure your Android Manifest file. The manifest needs to declare necessary permissions and setup for FileProvider to allow APK installations.

Here’s how you can modify the manifest file (Assets/Plugins/Android/AndroidManifest.xml) to include the required permissions and provider for APK handling:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.unity3d.player"
    xmlns:tools="http://schemas.android.com/tools">

  <!-- Required Permissions -->
  <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
  <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
  <uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES"/>

  <!-- Package visibility for Android 11 and above -->
  <queries>
    <intent>
      <action android:name="android.intent.action.MAIN" />
      <category android:name="android.intent.category.LAUNCHER" />
    </intent>
  </queries>

  <application>
    <!-- Main launcher activity -->
    <activity
        android:name="com.unity3d.player.UnityPlayerActivity"
        android:theme="@style/UnityThemeSelector">
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
      <meta-data android:name="unityplayer.UnityActivity" android:value="true" />
    </activity>

    <!-- FileProvider configuration -->
    <provider
      android:name="androidx.core.content.FileProvider"
      android:authorities="com.Srawan.ApkUtils.fileprovider"
      android:exported="false"
      android:grantUriPermissions="true">
      <meta-data
          android:name="android.support.FILE_PROVIDER_PATHS"
          android:resource="@xml/file_paths" />
    </provider>

  </application>
</manifest>

Make sure to adjust the file paths according to your project structure.

Note: In the FileProvider configuration, update the android:authorities attribute by replacing "com.Srawan.ApkUtils" it with your app’s actual package name. The final value should follow this format:

android:authorities="yourPackageName.fileprovider"

For the APKUtils package to work smoothly with Android, you’ll need to make some adjustments to your Gradle files. These configurations allow Unity to package the APKUtils functionalities correctly.

  1. gradleTemplate.properties: Make sure this file is placed in Assets/Plugins/Android/. If not present, create one with the following content:
org.gradle.jvmargs=-Xmx**JVM_HEAP_SIZE**M
org.gradle.parallel=true
unityStreamingAssets=**STREAMING_ASSETS**
**ADDITIONAL_PROPERTIES**
android.useAndroidX=true
android.enableJetifier=true
  1. launcherTemplate.gradle: These files should be placed in Assets/Plugins/Android/. These are necessary to configure the build settings for APK handling. Make sure to customize these based on your project needs.
apply plugin: 'com.android.application'

dependencies {
    implementation project(':unityLibrary')
    }

android {
    ndkPath "**NDKPATH**"

    compileSdkVersion **APIVERSION**
    buildToolsVersion '**BUILDTOOLS**'

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_11
        targetCompatibility JavaVersion.VERSION_11
    }

    defaultConfig {
        minSdkVersion **MINSDKVERSION**
        targetSdkVersion **TARGETSDKVERSION**
        applicationId '**APPLICATIONID**'
        ndk {
            abiFilters **ABIFILTERS**
        }
        versionCode **VERSIONCODE**
        versionName '**VERSIONNAME**'
    }

    aaptOptions {
        noCompress = **BUILTIN_NOCOMPRESS** + unityStreamingAssets.tokenize(', ')
        ignoreAssetsPattern = "!.svn:!.git:!.ds_store:!*.scc:!CVS:!thumbs.db:!picasa.ini:!*~"
    }**SIGN**

    lintOptions {
        abortOnError false
    }

    buildTypes {
        debug {
            minifyEnabled **MINIFY_DEBUG**
            proguardFiles getDefaultProguardFile('proguard-android.txt')**SIGNCONFIG**
            jniDebuggable true
        }
        release {
            minifyEnabled **MINIFY_RELEASE**
            proguardFiles getDefaultProguardFile('proguard-android.txt')**SIGNCONFIG**
        }
    }**PACKAGING_OPTIONS****PLAY_ASSET_PACKS****SPLITS**
**BUILT_APK_LOCATION**
    bundle {
        language {
            enableSplit = false
        }
        density {
            enableSplit = false
        }
        abi {
            enableSplit = true
        }
    }
}**SPLITS_VERSION_CODE****LAUNCHER_SOURCE_BUILD_SETUP**
  1. mainTemplate.gradle
apply plugin: 'com.android.library'
**APPLY_PLUGINS**

dependencies {
  implementation 'androidx.core:core:1.6.0'
    implementation fileTree(dir: 'libs', include: ['*.jar'])
**DEPS**}

android {
    ndkPath "**NDKPATH**"

    compileSdkVersion **APIVERSION**
    buildToolsVersion '**BUILDTOOLS**'

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_11
        targetCompatibility JavaVersion.VERSION_11
    }

    defaultConfig {
        minSdkVersion **MINSDKVERSION**
        targetSdkVersion **TARGETSDKVERSION**
        ndk {
            abiFilters **ABIFILTERS**
        }
        versionCode **VERSIONCODE**
        versionName '**VERSIONNAME**'
        consumerProguardFiles 'proguard-unity.txt'**USER_PROGUARD**
    }

    lintOptions {
        abortOnError false
    }

    aaptOptions {
        noCompress = **BUILTIN_NOCOMPRESS** + unityStreamingAssets.tokenize(', ')
        ignoreAssetsPattern = "!.svn:!.git:!.ds_store:!*.scc:!CVS:!thumbs.db:!picasa.ini:!*~"
    }**PACKAGING_OPTIONS**
}
**IL_CPP_BUILD_SETUP**
**SOURCE_BUILD_SETUP**
**EXTERNAL_SOURCES**

Ensure these files are aligned with your project’s configuration.

Step 4: Make a post-build script

Located in the Assets/Editor/BuildPostProcessor/ folder, this XML file defines the paths from which files can be shared using Android’s FileProvider. Here’s the content of the file_paths.xml:

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
  <external-path name="external_files" path="."/>
</paths>
  • <external-path>: This element defines the external directory that will be made accessible to other apps. The path="." part means the entire external storage directory is shared, allowing access to files stored there.

This C# script is placed inside the Assets/Editor/BuildPostProcessor/ folder. It implements Unity’s IPostGenerateGradleAndroidProject interface, which allows you to hook into the build process after Unity has generated the Android Gradle project.

Here’s the content of the PostBuildScript.cs:

using System.IO;
using UnityEngine;
using UnityEditor.Android;
public class PostBuildScript : IPostGenerateGradleAndroidProject
{
    public int callbackOrder => 0;

    public void OnPostGenerateGradleAndroidProject(string path)
    {
        // Path to the source file_paths.xml
        string sourceFilePath = Path.Combine(Application.dataPath,"Editor" ,"BuildPostProcessor", "file_paths.xml");

        // Destination folder in the Android project
        string destinationFolderPath = Path.Combine(path, "src", "main", "res", "xml");

        // Ensure the destination directory exists
        if (!Directory.Exists(destinationFolderPath))
        {
            Directory.CreateDirectory(destinationFolderPath);
        }

        // Copy the file_paths.xml to the destination
        string destinationFilePath = Path.Combine(destinationFolderPath, "file_paths.xml");
        File.Copy(sourceFilePath, destinationFilePath, true);

        Debug.Log("file_paths.xml successfully copied to Android project."+path);
    }
}

Explanation of the Post-Build Script

  • IPostGenerateGradleAndroidProject: This interface is part of Unity’s post-build pipeline for Android projects. It allows you to hook into the build process and perform tasks after Unity generates the Android Gradle project, but before the APK is built.

  • callbackOrder: This property determines when the callback should run relative to other post-build callbacks. Setting it to 0 ensures that this callback runs first.

  • OnPostGenerateGradleAndroidProject: This method is triggered after the Android Gradle project is generated. The script does the following:

    • It specifies the source path of the file_paths.xml, which is stored in the Assets/Editor/BuildPostProcessor/ folder.

    • It defines the target directory in the Android Gradle project where the file_paths.xml should be copied (src/main/res/xml).

    • It checks whether the target directory exists and creates it if necessary.

    • It then copies the file_paths.xml from the source to the destination folder in the generated Android project.

This post-build process automates the task of adding the file_paths.xml to the Android project, saving you from manually managing the file after every build.

Why is this Setup Necessary?

  • File Sharing: Android uses the FileProvider to securely share files between apps. The file_paths.xml defines which directories are accessible to other apps.

  • Automation: By using a post-build script, we ensure that the file_paths.xml is always copied to the correct location without requiring manual intervention. This streamlines the development process and ensures consistency.

To keep everything organized, we will organize the project into a specific folder structure. Here’s an overview:

markdownCopyEditMyUnityProject/
├── Assets/
│   ├── Plugins/
│   │   └── Android/
│   │       ├── AndroidManifest.xml
│   │       ├── gradleTemplate.properties
│   │       ├── launcherTemplate.gradle
│   │       └── mainTemplate.gradle
│   │
│   ├── Scripts/
│   │   ├── Example.cs
│   │   └── ApkUtils.cs
│   ├── Editor/
│   │   └── BuildPostProcessor/
│   │       ├── PostBuildScript.cs
│   │       └── file_paths.xml
│   └── Scenes/
│       └── MainScene.unity
└── ProjectSettings/
    ├── ProjectSettings.asset
    └── ... (other project settings)

With APKUtils configured, you can now easily manage APKs in your Unity project. Below are some examples of how to use the package:

Here’s the content of the Example.cs:

using System;
using System.Collections;
using System.IO;
using TMPro;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.SceneManagement;

public class Example : MonoBehaviour
{
    // Start is called once before the first execution of Update after the MonoBehaviour is created
    private string _downloadUrl = "https://download1652.mediafire.com/0ze6c598oeggeP5_rg4RKgw2EXJowte8y
    O3Zo7mU4-JSHcjtyo0b9ey1VbxYfo1L-1M0OiNKPL73uitHcSinKOXQfdmqW_7bJM2Vl4vSv3rKyqT6j_2_uwwuZKPggd2TZj0
    M_DcXcre_NKIILbjNq6Nk4KBQXr8g7poTKEKZ4R8/grel6demphrfwcs/mi-calculator-15-2-11.apk";
    private string _downloadFolderPath;

    private string _apkpath;
    private string packageName;
    [SerializeField]
    private TextMeshProUGUI _debug;

    private void Start()
    {

        _downloadFolderPath = Path.Combine(Application.persistentDataPath, "DownloadedGame");
        _apkpath = Path.Combine(_downloadFolderPath, Path.GetFileName(_downloadUrl));

    }

    public void DownloadBtn()
    {
        // Recreate the download folder.
        if (Directory.Exists(_downloadFolderPath))
        {
            Directory.Delete(_downloadFolderPath, true);
        }
        Directory.CreateDirectory(_downloadFolderPath);


        _apkpath = Path.Combine(_downloadFolderPath, Path.GetFileName(_downloadUrl));
        StartCoroutine(DownloadFileCoroutine(_downloadUrl, _apkpath, (onsucess) =>
        {
            _debug.text = onsucess.ToString();
        },
        (onerror) =>
        {
            _debug.text = onerror.ToString();
        }));
    }
    public void CheckPermision()
    {
        _debug.text = ApkUtils.HasInstallPermission() ? "it has permission to install apps from unknown sources" : "It doesnot have permission to install app from unknown source";
    }
    public void RequestPermission()
    {
        _debug.text = "Requesting permission...";
        ApkUtils.RequestInstallPermission();
    }

    public void InstallBtn()
    {
        if (!File.Exists(_apkpath))
        {
            _debug.text = "Apk not found in path: " + _apkpath;
        }

        ApkUtils.InstallApk(_apkpath, (result) =>
        {
            if (result == true)
            {
                _debug.text = "App sucessfully installed";
            }
            else
            {
                _debug.text = "App not Installed";
            }
        });
    }
    public void UninstallBtn()
    {
        if (string.IsNullOrEmpty(packageName))
        {
            _debug.text = "Package name is empty. Please again download apk or click on get Pkg from path btn";
            return;
        }

        ApkUtils.UninstallApp(packageName, (result) =>
        {
            if (result == true)
            {
                _debug.text = "App Uninstalled sucessfully";
            }
            else
            {
                _debug.text = "App is not Uninstalled";
            }
        });
    }
    public void LaunchBtn()
    {
        if (string.IsNullOrEmpty(packageName))
        {
            _debug.text = "Package name is empty. Please again download apk or click on Print PkgName from APK btn";
            return;
        }
        if (!ApkUtils.IsAppInstalled(packageName))
        {
            _debug.text = packageName + " the app with this package name is not installed";
            return;
        }
        _debug.text = " launching app...";
        ApkUtils.LaunchApp(packageName);
    }
    public void GetPackageNameFormApkBtn()
    {

        if (!File.Exists(_apkpath))
        {
            _debug.text = "Apk not found in path: " + _apkpath + " Please Download";
            return;
        }

        packageName = ApkUtils.GetPackageNameFromApk(_apkpath);
        _debug.text = "Package name is " + packageName;
    }
    public void CheckInstalledBtn()
    {
        _debug.text = ApkUtils.IsAppInstalled(packageName) ? "App is installed on this device" : "App is not installed on this device";
    }
    public void CurrentVersion()
    {
        if (string.IsNullOrEmpty(packageName))
        {
            _debug.text = "Package name is empty. Please again download apk or click on Print PkgName from APK btn";
            return;
        }
        if (!ApkUtils.IsAppInstalled(packageName))
        {
            _debug.text = packageName + " the app with this package name is not installed";
            return;
        }
        _debug.text = $" Version is {ApkUtils.GetInstalledAppVersion(packageName)}";
    }
    private IEnumerator DownloadFileCoroutine(string url, string destinationPath, Action<string> onSuccess, Action<string> onError)
    {

        using (UnityWebRequest webRequest = UnityWebRequest.Get(url))
        {
            try
            {
                var dh = new DownloadHandlerFile(destinationPath);
                dh.removeFileOnAbort = true;
                webRequest.downloadHandler = dh;

                webRequest.SendWebRequest();
            }
            catch (SystemException error)
            {
                onError?.Invoke(error.Message);
            }
            while (!webRequest.isDone)
            {
                float percent = webRequest.downloadProgress * 100;
                _debug.text = "Downloading..." + percent.ToString() + "%";
                yield return null;
            }

            if (webRequest.result == UnityWebRequest.Result.Success)
            {
                onSuccess?.Invoke("File downloaded successfully");
            }
            else
            {
                onError?.Invoke(webRequest.error);
                Debug.Log("Error Occured");
            }
        }


    }

    public void RestartScene()
    {
        SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
    }

}

Managing APKs in Unity can be complex, but with APKUtils, you get a simple and powerful solution to handle APK installations, uninstallation, version checking, and launching directly from Unity. This package makes it easier for you to manage APK files across Android versions, reducing the amount of manual work required for these tasks.

To get started, make sure to import the APKUtils.cs script, update your Android Manifest and Gradle files, and integrate the provided methods into your project.

By using APKUtils, you can streamline your APK management tasks, saving time and improving the overall efficiency of your mobile app development process.

🚀 Check out the GitHub repository for the complete source code and setup guide: Apk-Utils