Initial checkin

Initial checkin of Quest App Launcher
This commit is contained in:
tverona1
2019-07-16 23:46:19 -07:00
parent 6b98242a9b
commit fe3f38ee64
933 changed files with 105478 additions and 1 deletions
+21
View File
@@ -0,0 +1,21 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AppEntry : MonoBehaviour
{
public string packageId;
public string appName;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 53f2e028e4291b741960616a445cf2ba
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+242
View File
@@ -0,0 +1,242 @@
/************************************************************************************
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
{
/// <summary>
/// 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.
///
/// </summary>
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
/// <summary>
/// Static method for lauching an Android app
/// </summary>
/// <param name="packageId"></param>
static public void LaunchApp(string packageId)
{
using (AndroidJavaClass up = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
using (AndroidJavaObject ca = up.GetStatic<AndroidJavaObject>("currentActivity"))
using (AndroidJavaObject packageManager = ca.Call<AndroidJavaObject>("getPackageManager"))
{
AndroidJavaObject launchIntent = null;
try
{
launchIntent = packageManager.Call<AndroidJavaObject>("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();
}
}
}
}
/// <summary>
/// Populate the grid from installed apps
/// </summary>
/// <returns></returns>
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<AndroidJavaObject>("currentActivity"))
{
// Dictionary to hold package name -> app index, app name
var packageNameToAppName = new Dictionary<string, (int Index, string AppName)>();
// Get # of installed apps
int numApps = currentActivity.Call<int>("getSize");
Debug.Log("# installed apps: " + numApps);
// Get current package name (to filter this out))
var currentPackageName = currentActivity.Call<string>("getPackageName");
// 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);
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<string, string>();
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<sbyte[]>("getIcon", app.Value.Index);
}
// Set the icon image
var image = newObj.transform.Find("AppIcon").GetComponentInChildren<Image>();
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<TextMeshProUGUI>();
text.text = app.Value.AppName;
yield return null;
}
}
}
#endregion
}
}
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0d75ad87b4f70d345becda5841f29b7a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+49
View File
@@ -0,0 +1,49 @@
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class OnInteraction : MonoBehaviour
{
protected Material oldHoverMat;
public Material yellowMat;
public Material backIdle;
public Material backACtive;
public UnityEngine.UI.Text outText;
public void OnHoverEnter(Transform t)
{
var appEntry = t.gameObject.GetComponent("AppEntry") as AppEntry;
if (null != appEntry)
{
// Enable border
EnableBorder(t, true);
}
}
public void OnHoverExit(Transform t)
{
var appEntry = t.gameObject.GetComponent("AppEntry") as AppEntry;
if (null != appEntry)
{
// Disable border
EnableBorder(t, false);
}
}
public void OnSelected(Transform t)
{
var appEntry = t.gameObject.GetComponent("AppEntry") as AppEntry;
if (null != appEntry)
{
// Launch app
Debug.Log("Launching: " + appEntry.appName + " (package id: " + appEntry.packageId + ")");
QuestAppLauncher.GridPopulation.LaunchApp(appEntry.packageId);
}
}
void EnableBorder(Transform t, bool enable)
{
var border = t.Find("Border");
border?.gameObject.SetActive(enable);
}
}
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 054c7813b6beaab439c025873fec9b94
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+64
View File
@@ -0,0 +1,64 @@
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using UnityEngine.EventSystems;
public class ScrollRectOverride : ScrollRect, IMoveHandler, IPointerClickHandler, IScrollHandler
{
private const float speedMultiplier = 15f;
private float cellHeight = 0f;
void Start()
{
this.cellHeight = this.transform.GetComponentInChildren<GridLayoutGroup>().cellSize.y;
}
void Update()
{
// Get vector from either left or right thumbstick
var moveVector = OVRInput.Get(OVRInput.Axis2D.PrimaryThumbstick);
if (moveVector.x == 0 && moveVector.y == 0)
{
moveVector = OVRInput.Get(OVRInput.Axis2D.SecondaryThumbstick);
}
// Scroll by a fixed amount proportional to thumbstick position on each frame
// and map this to a fraction of the total viewport size:
// moveVector.y: The thumbstick vertical position normalized to [-1,1].
// Time.deltaTime: The time delta since last frame
// speedMultiplier: Just a multiplier to get a good scrolling speed.
// So, moveVector.y * Time.deltaTime * speedMultiplier = the amount to scroll in "units"
// proportional to thumbstick position since last frame.
// this.cellHeight / this.content.sizeDelta.y = cell height / total content height.
float verticalIncrement = moveVector.y * Time.deltaTime * speedMultiplier * this.cellHeight / this.content.sizeDelta.y;
this.verticalNormalizedPosition = Mathf.Clamp01(this.verticalNormalizedPosition + verticalIncrement);
}
public void OnPointerClick(PointerEventData e)
{
}
public override void OnBeginDrag(PointerEventData eventData)
{
}
void IMoveHandler.OnMove(AxisEventData e)
{
}
void IScrollHandler.OnScroll(PointerEventData eventData)
{
}
void OnMouseDrag()
{
}
void OnMouseUp()
{
}
void OnMouseDown()
{
}
}
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6c51693ac45aa2d4480f322a70ef9a67
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: