From 4fa3de6dec48d2994aa95540ebab1728dafec9b6 Mon Sep 17 00:00:00 2001 From: tverona1 Date: Thu, 8 Aug 2019 23:17:37 -0700 Subject: [PATCH] Async processing of apps - Async processing of apps - Async load of icon image --- Assets/Scripts/GridPopulation.cs | 172 +++++++++++++++----------- ProjectSettings/ProjectSettings.asset | 5 +- 2 files changed, 100 insertions(+), 77 deletions(-) diff --git a/Assets/Scripts/GridPopulation.cs b/Assets/Scripts/GridPopulation.cs index c913c9d..a6240fd 100644 --- a/Assets/Scripts/GridPopulation.cs +++ b/Assets/Scripts/GridPopulation.cs @@ -3,6 +3,7 @@ using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading.Tasks; using UnityEngine; using UnityEngine.UI; using UnityEngine.XR; @@ -24,7 +25,7 @@ namespace QuestAppLauncher public string AutoTabName; public string Tab1Name; public string Tab2Name; - public bool IsOverride; + public string IconPath; } // File name of app name overrides @@ -64,9 +65,6 @@ namespace QuestAppLauncher // Tab prefab public GameObject prefabTab; - // Reference to executing populate routine - private Coroutine populateCoroutine; - // Built-in tab names private const string Tab_Quest = "Quest"; private const string Tab_Go = "Go/Gear"; @@ -77,7 +75,7 @@ namespace QuestAppLauncher #region MonoBehaviour handler - void Start() + async void Start() { // Set high texture resolution scale to minimize aliasing XRSettings.eyeTextureResolutionScale = 2.0f; @@ -86,7 +84,7 @@ namespace QuestAppLauncher Core.AsyncInitialize(); // Populate the grid - StartPopulate(); + await PopulateAsync(); } void Update() @@ -206,36 +204,47 @@ namespace QuestAppLauncher return size; } - public void StartPopulate() - { - // Ensure we only exeucte on populate routine at a time - if (null != this.populateCoroutine) - { - StopCoroutine(this.populateCoroutine); - } - - this.populateCoroutine = this.StartCoroutine(Populate()); - } - /// /// Populate the grid from installed apps /// /// - private IEnumerator Populate() + private async Task PopulateAsync() { - var persistentDataPath = UnityEngine.Application.persistentDataPath; - Debug.Log("Persistent data path: " + persistentDataPath); - // Load configuration Config config = new Config(); ConfigPersistence.LoadConfig(config); + // Process apps in background + var apps = await Task.Run(() => + { + AndroidJNI.AttachCurrentThread(); + + try + { + return ProcessApps(config); + } + finally + { + AndroidJNI.DetachCurrentThread(); + } + }); + + // Populate the panel content + await PopulatePanelContentAsync(config, apps); + } + + private Dictionary ProcessApps(Config config) + { + var persistentDataPath = UnityEngine.Application.persistentDataPath; + Debug.Log("Persistent data path: " + persistentDataPath); + + // Dictionary to hold package name -> app index, app name + var apps = new Dictionary(); + var excludedPackageNames = new HashSet(); + using (AndroidJavaClass unity = new AndroidJavaClass("com.unity3d.player.UnityPlayer")) using (AndroidJavaObject currentActivity = unity.GetStatic("currentActivity")) { - // Dictionary to hold package name -> app index, app name - var apps = new Dictionary(); - var excludedPackageNames = new HashSet(); // Get # of installed apps int numApps = currentActivity.Call("getSize"); @@ -296,7 +305,6 @@ namespace QuestAppLauncher apps.Add(packageName, new ProcessedApp { PackageName = packageName, Index = i, AutoTabName = tabName, AppName = appName }); Debug.LogFormat("[{0}] package: {1}, name: {2}, auto tab: {3}", i, packageName, appName, tabName); - yield return null; } // Override app names, if any @@ -376,7 +384,6 @@ namespace QuestAppLauncher AutoTabName = autoTabName ?? apps[entry[0]].AutoTabName, Tab1Name = tab1 ?? apps[entry[0]].Tab1Name, Tab2Name = tab2 ?? apps[entry[0]].Tab2Name, - IsOverride = true }; } } @@ -385,12 +392,9 @@ namespace QuestAppLauncher Debug.Log("Did not find: " + appNameOverrideFilePath); } - yield return null; - // Load list of app icon overrides // This is a list of jpg images stored as packageName.jpg. var iconOverridePath = persistentDataPath; - var iconOverrides = new Dictionary(); if (Directory.Exists(iconOverridePath)) { foreach (var iconFileName in Directory.GetFiles(iconOverridePath, IconOverrideExtSearch)) @@ -398,26 +402,22 @@ namespace QuestAppLauncher var entry = Path.GetFileNameWithoutExtension(iconFileName); if (apps.ContainsKey(entry)) { - Debug.Log("Found file: " + iconFileName); - iconOverrides[entry] = Path.Combine(iconOverridePath, iconFileName); - } + Debug.Log("Found icon override: " + iconFileName); - yield return null; + ProcessedApp newProcessedApp = apps[entry]; + newProcessedApp.IconPath = Path.Combine(iconOverridePath, iconFileName); + apps[entry] = newProcessedApp; + } } } - - yield return null; - - // Populate the panel content - yield return PopulatePanelContent(currentActivity, config, apps, iconOverrides); } + + return apps; } - private IEnumerator PopulatePanelContent( - AndroidJavaObject currentActivity, + private async Task PopulatePanelContentAsync( Config config, - Dictionary apps, - Dictionary iconOverrides) + Dictionary apps) { // Set up tabs var topTabs = new List(); @@ -483,24 +483,24 @@ namespace QuestAppLauncher foreach (var app in apps.OrderBy(key => key.Value.AppName)) { // Add to all tab - yield return AddCellToGrid(app.Value, gridContents[Tab_All].transform, iconOverrides, currentActivity); + await AddCellToGridAsync(app.Value, gridContents[Tab_All].transform); // Add to auto (built-in) tabs if (gridContents.ContainsKey(app.Value.AutoTabName)) { - yield return AddCellToGrid(app.Value, gridContents[app.Value.AutoTabName].transform, iconOverrides, currentActivity); + await AddCellToGridAsync(app.Value, gridContents[app.Value.AutoTabName].transform); } // Add to tab1 if (null != app.Value.Tab1Name && gridContents.ContainsKey(app.Value.Tab1Name)) { - yield return AddCellToGrid(app.Value, gridContents[app.Value.Tab1Name].transform, iconOverrides, currentActivity); + await AddCellToGridAsync(app.Value, gridContents[app.Value.Tab1Name].transform); } // Add to tab2 if (null != app.Value.Tab2Name && gridContents.ContainsKey(app.Value.Tab2Name)) { - yield return AddCellToGrid(app.Value, gridContents[app.Value.Tab2Name].transform, iconOverrides, currentActivity); + await AddCellToGridAsync(app.Value, gridContents[app.Value.Tab2Name].transform); } } } @@ -562,9 +562,39 @@ namespace QuestAppLauncher } } - private IEnumerator AddCellToGrid(ProcessedApp app, Transform transform, - Dictionary iconOverrides, - AndroidJavaObject currentActivity) + private byte[] GetAppIcon(string iconPath, int appIndex) + { + byte[] bytesIcon = null; + bool useApkIcon = true; + if (null != iconPath) + { + // Use overridden icon + try + { + bytesIcon = File.ReadAllBytes(iconPath); + useApkIcon = false; + } + catch (Exception e) + { + // Fall back to using the apk icon + Debug.Log(string.Format("Error reading app icon from file [{0}]: {1}", iconPath, e.Message)); + } + } + + if (useApkIcon) + { + // Use built-in icon from the apk + using (AndroidJavaClass unity = new AndroidJavaClass("com.unity3d.player.UnityPlayer")) + using (AndroidJavaObject currentActivity = unity.GetStatic("currentActivity")) + { + bytesIcon = (byte[])(Array)currentActivity.Call("getIcon", appIndex); + } + } + + return bytesIcon; + } + + private async Task AddCellToGridAsync(ProcessedApp app, Transform transform) { // Create new instances of our app info prefabCell var newObj = (GameObject)Instantiate(this.prefabCell, transform); @@ -574,44 +604,36 @@ namespace QuestAppLauncher appEntry.packageId = app.PackageName; appEntry.appName = app.AppName; - // Get app icon - byte[] bytesIcon = null; - bool useApkIcon = true; - if (iconOverrides.ContainsKey(app.PackageName)) + // Get app icon in background + var bytesIcon = await Task.Run(() => { - // Use overridden icon + AndroidJNI.AttachCurrentThread(); + try { - bytesIcon = File.ReadAllBytes(iconOverrides[app.PackageName]); - useApkIcon = false; + return GetAppIcon(app.IconPath, app.Index); } - catch (Exception e) + finally { - // Fall back to using the apk icon - Debug.Log(string.Format("Error reading app icon from file [{0}]: {1}", iconOverrides[app.PackageName], e.Message)); + AndroidJNI.DetachCurrentThread(); } - } - - if (useApkIcon) - { - // Use built-in icon from the apk - bytesIcon = (byte[])(Array)currentActivity.Call("getIcon", app.Index); - } + }); // Set the icon image - var image = newObj.transform.Find("AppIcon").GetComponentInChildren(); - var texture = new Texture2D(2, 2, TextureFormat.RGB24, false); - texture.filterMode = FilterMode.Trilinear; - texture.anisoLevel = 16; - texture.LoadImage(bytesIcon); - var rect = new Rect(0, 0, texture.width, texture.height); - image.sprite = Sprite.Create(texture, rect, new Vector2(0.5f, 0.5f)); + if (null != bytesIcon) + { + var image = newObj.transform.Find("AppIcon").GetComponentInChildren(); + var texture = new Texture2D(2, 2, TextureFormat.RGB24, false); + texture.filterMode = FilterMode.Trilinear; + texture.anisoLevel = 16; + texture.LoadImage(bytesIcon); + var rect = new Rect(0, 0, texture.width, texture.height); + image.sprite = Sprite.Create(texture, rect, new Vector2(0.5f, 0.5f)); + } // Set app name in text var text = newObj.transform.Find("AppName").GetComponentInChildren(); text.text = app.AppName; - - yield return null; } } #endregion diff --git a/ProjectSettings/ProjectSettings.asset b/ProjectSettings/ProjectSettings.asset index a5271c8..8c1c6d7 100644 --- a/ProjectSettings/ProjectSettings.asset +++ b/ProjectSettings/ProjectSettings.asset @@ -120,7 +120,7 @@ PlayerSettings: 16:10: 1 16:9: 1 Others: 1 - bundleVersion: 0.5 + bundleVersion: 0.6 preloadedAssets: [] metroInputSource: 0 wsaTransparentSwapchain: 0 @@ -658,7 +658,8 @@ PlayerSettings: scriptingRuntimeVersion: 1 gcIncremental: 0 gcWBarrierValidation: 0 - apiCompatibilityLevelPerPlatform: {} + apiCompatibilityLevelPerPlatform: + Android: 3 m_RenderingPath: 1 m_MobileRenderingPath: 1 metroPackageName: Template_3D