/// <summary> /// Helper method to download assets asynchronously. After completion, scene is automatically reloaded /// if new assets have been downloaded. /// </summary> /// <param name="config">Current config</param> /// <param name="downloadProgress">Download progress interface - used to indicate download progress</param> /// <returns></returns> public static async Task DownloadAssetsAsync(Config config, IDownloadProgress downloadProgress = null, bool forceCheck = false) { // Start background thread Task.Run(async() => { // Mutual exclusion while loading assets await AssetsDownloader.semaphoreSlim.WaitAsync(); // Attach / detatch JNI. Required for any calls into JNI from background threads. AndroidJNI.AttachCurrentThread(); try { // Download assets from repos. AssetsDownloader assetsDownloader = new AssetsDownloader(); return(await assetsDownloader.DownloadFromReposAsync(config, downloadProgress, forceCheck)); } finally { AssetsDownloader.semaphoreSlim.Release(); AndroidJNI.DetachCurrentThread(); } }).ContinueWith((downloadedAssets) => { if (downloadedAssets.Result) { // We downloaded new assets, so re-load the scene Debug.Log("Downloaded new assets. Re-populating panel"); SceneManager.LoadSceneAsync(SceneManager.GetActiveScene().name); } }); }
/// <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> /// Populate the grid from installed apps /// </summary> /// <returns></returns> private async Task PopulateAsync(bool isRenameMode = false) { // Load configuration var config = ConfigPersistence.LoadConfig(); // Set skybox if (!isRenameMode) { this.skyboxHandler.SetSkybox(config.background); } // Process apps in background var apps = await Task.Run(() => { AndroidJNI.AttachCurrentThread(); try { return(AppProcessor.ProcessApps(config, isRenameMode)); } finally { AndroidJNI.DetachCurrentThread(); } }); // Download updates in the background if (!isRenameMode && config.autoUpdate && !GlobalState.Instance.CheckedForUpdate) { GlobalState.Instance.CheckedForUpdate = true; AssetsDownloader.DownloadAssetsAsync(config, this.downloadStatusIndicator); } // Populate the panel content if (!isRenameMode) { await PopulatePanelContentAsync(config, apps); } else { await PopulateRenamePanelContentAsync(config, apps); } }
/// <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); }
public void UpdateAssetsNow() { AssetsDownloader.DownloadAssetsAsync(config, this.downloadStatusIndicator, true); }