/// <summary>
        /// Opens the rename dialog
        /// </summary>
        /// <param name="entry"></param>
        public async void Rename(AppEntry appTarget)
        {
            Debug.Assert(null != this.appToRename, "App to rename is null");
            var  filePath    = appTarget.externalIconPath;
            bool cleanupFile = false;

            // If icon path is null, extract icon from apk
            if (null == filePath)
            {
                filePath    = Path.Combine(AssetsDownloader.GetOrCreateDownloadPath(), this.appToRename.packageId + ".jpg");
                cleanupFile = true;

                var bytes = appTarget.sprite.GetComponent <Image>().sprite.texture.EncodeToJPG();
                using (var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None,
                                                       bufferSize: 4096, useAsync: true))
                {
                    await fileStream.WriteAsync(bytes, 0, bytes.Length);
                };
            }

            await Task.Run(() =>
            {
                AndroidJNI.AttachCurrentThread();

                try
                {
                    // Add to json file
                    AddToRenameJsonFile(this.appToRename.packageId, appTarget.appName);

                    // Add icon to zip
                    using (AndroidJavaClass unity = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
                        using (AndroidJavaObject currentActivity = unity.GetStatic <AndroidJavaObject>("currentActivity"))
                        {
                            var renameIconPackFilePath = Path.Combine(UnityEngine.Application.persistentDataPath, AppProcessor.RenameIconPackFileName);
                            currentActivity.CallStatic("addFileToZip", renameIconPackFilePath, filePath, this.appToRename.packageId + ".jpg");
                        }
                }
                finally
                {
                    AndroidJNI.DetachCurrentThread();
                }
            });

            if (cleanupFile && File.Exists(filePath))
            {
                File.Delete(filePath);
            }

            // Reload the scene
            await SceneManager.LoadSceneAsync(SceneManager.GetActiveScene().name);
        }
        /// <summary>
        /// Entry point for app processing: Applies app name overrides (from appnames.txt/json) and app icons (from individual jpgs or icon packs).
        /// Handles extraction of icon packs if zip file modified. Returns a list of processed apps.
        /// </summary>
        /// <param name="config">Application config</param>
        /// <param name="isRenameMode">Whether in rename mode. If so, returns all apps found, not just installed ones.</param>
        /// <returns>Dictionary of processed apps</returns>
        public static Dictionary <string, ProcessedApp> ProcessApps(Config config, bool isRenameMode = false)
        {
            var persistentDataPath = UnityEngine.Application.persistentDataPath;

            Debug.Log("Persistent data path: " + persistentDataPath);

            // Dictionary to hold package name -> app index, app name
            var apps = new Dictionary <string, ProcessedApp>(StringComparer.OrdinalIgnoreCase);
            var excludedPackageNames = new HashSet <string>(StringComparer.OrdinalIgnoreCase);

            using (AndroidJavaClass unity = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
                using (AndroidJavaObject currentActivity = unity.GetStatic <AndroidJavaObject>("currentActivity"))
                {
                    // Get # of installed apps
                    int numApps = currentActivity.Call <int>("getSize");
                    Debug.Log("# installed apps: " + numApps);

                    var processedLastTimeUsed = false;
                    if (config.sortMode.Equals(Config.Sort_MostRecent, StringComparison.OrdinalIgnoreCase))
                    {
                        // Process last time used for each app
                        currentActivity.Call("processLastTimeUsed", LastUsedLookbackDays);
                        processedLastTimeUsed = true;
                    }

                    if (!isRenameMode)
                    {
                        // Add current package name to excludedPackageNames to filter it out
                        excludedPackageNames.Add(currentActivity.Call <string>("getPackageName"));
                    }

                    // This is a file containing packageNames that will be excluded.
                    // Skip this if in rename mode.
                    var excludedPackageNamesFilePath = Path.Combine(persistentDataPath, ExcludedPackagesFile);
                    if (!isRenameMode && File.Exists(excludedPackageNamesFilePath))
                    {
                        Debug.Log("Found file: " + excludedPackageNamesFilePath);
                        string[] excludedPackages = File.ReadAllLines(excludedPackageNamesFilePath);
                        foreach (string excludedPackage in excludedPackages)
                        {
                            if (!string.IsNullOrEmpty(excludedPackage))
                            {
                                excludedPackageNames.Add(excludedPackage);
                            }
                        }
                    }

                    // Get installed package and app names
                    for (int i = 0; i < numApps; i++)
                    {
                        var packageName  = currentActivity.Call <string>("getPackageName", i);
                        var appName      = currentActivity.Call <string>("getAppName", i);
                        var lastTimeUsed = processedLastTimeUsed ? currentActivity.Call <long>("getLastTimeUsed", i) : 0;

                        if (excludedPackageNames.Contains(packageName))
                        {
                            // Skip excluded package
                            Debug.LogFormat("Skipping Excluded [{0}] Package: {1}, name: {2}", i, packageName, appName);
                            continue;
                        }

                        // Determine app type (Quest, Go or 2D)
                        string tabName;
                        if (currentActivity.Call <bool>("is2DApp", i))
                        {
                            if (!config.show2D)
                            {
                                // Skip 2D apps
                                Debug.LogFormat("Skipping 2D [{0}] Package: {1}, name: {2}", i, packageName, appName);
                                continue;
                            }

                            tabName = Tab_2D;
                        }
                        else if (currentActivity.Call <bool>("isQuestApp", i))
                        {
                            tabName = Tab_Quest;
                        }
                        else
                        {
                            tabName = Tab_Go;
                        }

                        apps.Add(packageName, new ProcessedApp {
                            PackageName = packageName, Index = i,
                            AutoTabName = tabName, AppName = appName, LastTimeUsed = lastTimeUsed
                        });
                        Debug.LogFormat("[{0}] package: {1}, name: {2}, auto tab: {3}", i, packageName, appName, tabName);
                    }

                    // Process app name overrides files (both downloaded & manually created)
                    ProcessAppNameOverrideFiles(isRenameMode, apps, AssetsDownloader.GetOrCreateDownloadPath());
                    ProcessAppNameOverrideFiles(isRenameMode, apps, persistentDataPath);

                    // Extract icon packs (both downloaded & manually created)
                    ExtractIconPacks(currentActivity, AssetsDownloader.GetOrCreateDownloadPath());
                    ExtractIconPacks(currentActivity, persistentDataPath);

                    // Process extracted icons (both downloaded & manually created)
                    ProcessExtractedIcons(isRenameMode, apps, AssetsDownloader.GetOrCreateDownloadPath());
                    ProcessExtractedIcons(isRenameMode, apps, persistentDataPath);

                    // Process any individual icons
                    var iconOverridePath = persistentDataPath;
                    if (Directory.Exists(persistentDataPath))
                    {
                        ProcessIconsInPath(apps, persistentDataPath);
                    }
                }

            return(apps);
        }