163 lines
5.4 KiB
C#
163 lines
5.4 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Threading.Tasks;
|
|
using TMPro;
|
|
using UnityEngine;
|
|
using UnityEngine.UI;
|
|
|
|
namespace QuestAppLauncher
|
|
{
|
|
public class AppEntry : MonoBehaviour
|
|
{
|
|
// Sprite gameobject
|
|
public GameObject sprite = null;
|
|
public Image image = null;
|
|
public AspectRatioFitter aspectRatioFitter = null;
|
|
|
|
// TMP text
|
|
public TextMeshProUGUI text;
|
|
|
|
// Scroll view transform
|
|
public RectTransform scrollViewTransform;
|
|
|
|
// App entry contents
|
|
public string packageId;
|
|
public string appName;
|
|
public string externalIconPath = null;
|
|
public int installedApkIndex = -1;
|
|
public bool isRenameMode;
|
|
public bool dynamicallyLoadIcon;
|
|
|
|
const int MAX_FRAME_DELAYS = 35;
|
|
private float lastPos = -1f;
|
|
private bool isIconLoaded = false;
|
|
private int curFrameDelay = 0;
|
|
private int maxFrameDelays = 0;
|
|
private float maxDistance = 0f;
|
|
private System.Random rand = new System.Random();
|
|
|
|
// Start is called before the first frame update
|
|
void Start()
|
|
{
|
|
// Calculate max distance outside the visible scroll area, beyond which we'll unload the icon to reduce memory
|
|
this.maxDistance = 1.5f * this.scrollViewTransform.rect.size.y;
|
|
|
|
// Calculate the maximum frame delays when populating this icon; used to stagger loading of icons in the visible scroll area
|
|
this.maxFrameDelays = this.rand.Next(MAX_FRAME_DELAYS);
|
|
}
|
|
|
|
// Update is called once per frame
|
|
async void Update()
|
|
{
|
|
if (!this.dynamicallyLoadIcon || null == this.scrollViewTransform ||
|
|
(this.externalIconPath == null && this.installedApkIndex == -1))
|
|
{
|
|
return;
|
|
}
|
|
|
|
var localPos = this.scrollViewTransform.InverseTransformPoint(this.transform.position).y;
|
|
if (Mathf.Abs(localPos) < this.maxDistance)
|
|
{
|
|
// Don't bother loading icons while we're scrolling
|
|
if (this.lastPos == -1)
|
|
{
|
|
this.lastPos = localPos;
|
|
return;
|
|
}
|
|
|
|
if (this.lastPos != localPos)
|
|
{
|
|
this.lastPos = localPos;
|
|
return;
|
|
|
|
}
|
|
|
|
// We're within max distance, so show the icon after some delay (to stagger loading of icons)
|
|
if (this.curFrameDelay < this.maxFrameDelays)
|
|
{
|
|
this.curFrameDelay++;
|
|
return;
|
|
}
|
|
this.maxFrameDelays = this.rand.Next(MAX_FRAME_DELAYS);
|
|
this.curFrameDelay = 0;
|
|
|
|
if (!this.isIconLoaded)
|
|
{
|
|
UnloadIcon();
|
|
await LoadIcon();
|
|
this.isIconLoaded = true;
|
|
}
|
|
}
|
|
else if (this.isIconLoaded)
|
|
{
|
|
// We're outside max distance, so unload the icon
|
|
UnloadIcon();
|
|
this.isIconLoaded = false;
|
|
}
|
|
}
|
|
|
|
public async Task LoadIcon()
|
|
{
|
|
var result = await AppProcessor.GetAppIconAsync(this.externalIconPath, this.installedApkIndex, 1024 * 1024);
|
|
var image = result.Item1;
|
|
var imageWidth = result.Item2;
|
|
var imageHeight = result.Item3;
|
|
|
|
Texture2D texture = null;
|
|
|
|
if (null == image)
|
|
{
|
|
Debug.LogFormat("Error loading icon: Path: {0}, Index: {1}", this.externalIconPath, this.installedApkIndex);
|
|
return;
|
|
}
|
|
|
|
// Set the icon image
|
|
if (imageWidth == 0 || imageHeight == 0)
|
|
{
|
|
texture = new Texture2D(2, 2, TextureFormat.RGBA32, false);
|
|
texture.filterMode = FilterMode.Trilinear;
|
|
texture.anisoLevel = 16;
|
|
texture.LoadImage(image);
|
|
}
|
|
else
|
|
{
|
|
texture = new Texture2D(imageWidth, imageHeight, TextureFormat.ARGB32, false);
|
|
texture.filterMode = FilterMode.Trilinear;
|
|
texture.anisoLevel = 16;
|
|
texture.LoadRawTextureData(image);
|
|
texture.Apply();
|
|
}
|
|
|
|
var rect = new Rect(0, 0, texture.width, texture.height);
|
|
this.image.sprite = Sprite.Create(texture, rect, new Vector2(0.5f, 0.5f));
|
|
this.image.color = Color.white;
|
|
|
|
// Preserve icon's aspect ratio
|
|
this.aspectRatioFitter.aspectRatio = (float)texture.width / (float)texture.height;
|
|
}
|
|
|
|
private void UnloadIcon()
|
|
{
|
|
// We always manually create the sprite's texture object,
|
|
// so we must explicitly destroy it.
|
|
if (null != this.image.sprite)
|
|
{
|
|
if (null != this.image.sprite.texture)
|
|
{
|
|
DestroyImmediate(image.sprite.texture);
|
|
}
|
|
|
|
DestroyImmediate(this.image.sprite);
|
|
this.image.sprite = null;
|
|
this.image.color = Color.clear;
|
|
}
|
|
}
|
|
|
|
private void OnDestroy()
|
|
{
|
|
Debug.Log("AppEntry.OnDestroy");
|
|
UnloadIcon();
|
|
}
|
|
}
|
|
} |