/************************************************************************************ Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. See SampleFramework license.txt for license terms. Unless required by applicable law or agreed to in writing, the sample code is provided “AS IS” WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the license for specific language governing permissions and limitations under the license. ************************************************************************************/ using System.Collections; using System.Collections.Generic; using System.Linq; using System.IO; using UnityEngine; using UnityEngine.UI; using UnityEngine.XR; using System; using Oculus.Platform; using TMPro; namespace QuestAppLauncher { /// /// Usage: demonstrate how to use overlay layers for a paneled UI system /// On Mobile, we support both Cylinder layer and Quad layer /// Press any button: it will cycle [world geometry Quad]->[overlay layer Quad]->[world geometry cylinder]->[overlay layer cylinder] /// On PC, only Quad layer is supported /// Press any button: it will cycle [world geometry Quad]->[overlay layer Quad] /// /// You should be able to observe sharper and less aliased image when switch from world geometry to overlay layer. /// /// public class GridPopulation : MonoBehaviour { // File name of app name overrides const string AppNameOverrideFile = "appnames.txt"; // Extension search for icon overrides const string IconOverrideExtSearch = "*.jpg"; // Grid content game object public GameObject gridContent; // App info GameObject (a cell in the grid content) public GameObject prefab; #region MonoBehaviour handler void Start() { // Set high texture resolution scale to minimize aliasing XRSettings.eyeTextureResolutionScale = 2.0f; // Initialize the core platform Core.AsyncInitialize(); // Populate the grid StartCoroutine(Populate()); } void Update() { } #endregion #region Private Functions /// /// Static method for lauching an Android app /// /// static public void LaunchApp(string packageId) { using (AndroidJavaClass up = new AndroidJavaClass("com.unity3d.player.UnityPlayer")) using (AndroidJavaObject ca = up.GetStatic("currentActivity")) using (AndroidJavaObject packageManager = ca.Call("getPackageManager")) { AndroidJavaObject launchIntent = null; try { launchIntent = packageManager.Call("getLaunchIntentForPackage", packageId); ca.Call("startActivity", launchIntent); // Quest doesn't like multiple VR apps running simultaneously. Kill ourselves. UnityEngine.Application.Quit(); } catch (System.Exception e) { Debug.Log(string.Format("Failed to launch app {0}: {1}", packageId, e.Message)); } finally { if (null != launchIntent) { launchIntent.Dispose(); } } } } /// /// Populate the grid from installed apps /// /// IEnumerator Populate() { var persistentDataPath = UnityEngine.Application.persistentDataPath; Debug.Log("Persistent data path: " + persistentDataPath); 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 packageNameToAppName = new Dictionary(); // Get # of installed apps int numApps = currentActivity.Call("getSize"); Debug.Log("# installed apps: " + numApps); // Get current package name (to filter this out)) var currentPackageName = currentActivity.Call("getPackageName"); // Get installed package and app names for (int i = 0; i < numApps; i++) { var packageName = currentActivity.Call("getPackageName", i); var appName = currentActivity.Call("getAppName", i); if (packageName.Equals(currentPackageName)) { // Skip current package continue; } packageNameToAppName.Add(packageName, (i, appName)); Debug.Log("[" + i + "] package: " + packageName + ", name: " + appName); } yield return null; // Override app names, if any // This is just a file with comma-separated packageName,appName pairs var appNameOverrideFilePath = Path.Combine(persistentDataPath, AppNameOverrideFile); if (File.Exists(appNameOverrideFilePath)) { Debug.Log("Found file: " + appNameOverrideFilePath); string[] overriddenNames = File.ReadAllLines(appNameOverrideFilePath); foreach (string overriddenName in overriddenNames) { var entry = overriddenName.Split(','); if (2 == entry.Length && packageNameToAppName.ContainsKey(entry[0])) { packageNameToAppName[entry[0]] = (packageNameToAppName[entry[0]].Index, entry[1]); } } } else { 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)) { var entry = Path.GetFileNameWithoutExtension(iconFileName); if (packageNameToAppName.ContainsKey(entry)) { Debug.Log("Found file: " + iconFileName); iconOverrides[entry] = Path.Combine(iconOverridePath, iconFileName); } } } yield return null; // Populate grid with app information (name & icon) // Sort by app name foreach (var app in packageNameToAppName.OrderBy(key => key.Value.AppName)) { // Create new instances of our app info prefab var newObj = (GameObject)Instantiate(prefab, gridContent.transform); // Set app entry info var appEntry = newObj.GetComponent("AppEntry") as AppEntry; appEntry.packageId = app.Key; appEntry.appName = app.Value.AppName; // Get app icon byte[] bytesIcon = null; bool useApkIcon = true; if (iconOverrides.ContainsKey(app.Key)) { // Use overridden icon try { bytesIcon = File.ReadAllBytes(iconOverrides[app.Key]); useApkIcon = false; } catch (Exception e) { // Fall back to using the apk icon Debug.Log(string.Format("Error reading app icon from file [{0}]: {1}", iconOverrides[app.Key], e.Message)); } } if (useApkIcon) { // Use built-in icon from the apk bytesIcon = (byte[])(Array)currentActivity.Call("getIcon", app.Value.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.Resize(144, 100, TextureFormat.RGB24, false); 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.Value.AppName; yield return null; } } } #endregion } }