Unity は Android インターフェースを呼び出してアプリケーションリストを取得します

I.はじめに

        Android Studio でアプリケーション アイコン、アプリケーション名、アプリケーション パッケージ名などを含むアプリケーション リスト インターフェイスをカプセル化して取得し、Unity が呼び出すための AAR パッケージをエクスポートします。Unity は C# スクリプトを通じて AAR 内にカプセル化されたインターフェイスを呼び出し、アプリケーションを表示しますUGUI によるリスト管理。

2. Androidインタラクティブ機能パッケージ

        Android Studio を使用して、Unity による後続の呼び出しのために、アプリケーション リストの取得に関連する呼び出しインターフェイスを AAR パッケージにカプセル化します。

1. 新しい Android プロジェクトを作成する

        ここでは詳細には触れません。「Android WebView の使用」を参照してください。

2. モジュールを作成する

         ここでは詳細には触れません。「Android WebView の使用」を参照してください。

3. Javaクラスの作成

(1) メソッド呼び出しクラスを作成する

        このクラスは、Unity が呼び出すメソッドを作成するために使用されます。具体的なコードは次のとおりです。

import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.provider.Settings;

import java.util.ArrayList;
import java.util.List;

public class ObtainApplicationListUtils {
    private final String tag = "ObtainApplicationListUtils";
    private Context mContext;
    private PackageManager packageManager;
    private static ObtainApplicationListUtils mObtainApplicationListUtils = null;

    public ObtainApplicationListUtils(Context context) {
        this.mContext = context;
        packageManager = mContext.getPackageManager();
    }


    public static ObtainApplicationListUtils getInstance(Context context){
        if (mObtainApplicationListUtils == null) {
            mObtainApplicationListUtils = new ObtainApplicationListUtils(context);
        }
        return mObtainApplicationListUtils;
    }

    //获取所有应用列表
    public List<AppInfo> getAllApplicationList(boolean isFilterSystem){
        List<AppInfo> allAppInfoList = new ArrayList<>();
        if(allAppInfoList != null){
            allAppInfoList.clear();
        }
        AppInfo appInfo = null;

        List<PackageInfo> packageInfoList = packageManager.getInstalledPackages(0);
        for(PackageInfo packageInfo : packageInfoList){
            appInfo = new AppInfo();
            appInfo.setAppName(packageManager.getApplicationLabel(packageInfo.applicationInfo).toString());
            appInfo.setPackageName(packageInfo.applicationInfo.packageName);
            appInfo.setAppIcon(AppIconConverter.getAppIcon(packageInfo,packageManager));
            int flags = packageInfo.applicationInfo.flags;
            //判断是否是属于系统的应用,过滤系统应用
            if((flags & ApplicationInfo.FLAG_SYSTEM) != 0 && isFilterSystem){

            }
            else{
                allAppInfoList.add(appInfo);
            }
        }
        
        return allAppInfoList;
    }

    //获取特定应用列表
    public List<AppInfo> getSpecificApplicationList(boolean isFilterSystem){
        List<AppInfo> specificAppInfoList = new ArrayList<>();
        if(specificAppInfoList != null){
            specificAppInfoList.clear();
        }

        AppInfo appInfo = null;

        List<PackageInfo> packageInfoList = packageManager.getInstalledPackages(0);
        for(PackageInfo packageInfo : packageInfoList){
            appInfo = new AppInfo();
            appInfo.setAppName(packageManager.getApplicationLabel(packageInfo.applicationInfo).toString());
            appInfo.setPackageName(packageInfo.applicationInfo.packageName);
            appInfo.setAppIcon(AppIconConverter.getAppIcon(packageInfo,packageManager));
            int flags = packageInfo.applicationInfo.flags;
            //判断是否是属于系统的应用,过滤系统应用
            if((flags & ApplicationInfo.FLAG_SYSTEM) != 0 && isFilterSystem){

            }
            else{
                //过滤其他应用,只保留指定包名格式的应用
                if(appInfo.getPackageName().contains("被保留应用的包名前两部分,即com.xx")){
                    specificAppInfoList.add(appInfo);
                }
            }
        }

        return specificAppInfoList;
    }

    //通过包名打开应用
    public void startApp(String packageName) {
        try {
            Intent intent = packageManager.getLaunchIntentForPackage(packageName);
            mContext.startActivity(intent);
        } catch (Exception exception) {}
    }

    //打开系统设置
    public void openSystemSettings(){
        try {
            Intent intent = new Intent(Settings.ACTION_SETTINGS);
            mContext.startActivity(intent);
        }catch (Exception exception){}
    }
}

(2) アプリケーションアイコン変換クラスの作成

        アプリケーション アイコンを取得した後、Drawable 形式でデータを取得します。これは、ビットマップに変換してから、Unity による後続の呼び出しのために再度バイト配列に変換する必要があります。アイコン取得時の注意点:Androidはパッケージ名でアプリ情報を取得し、アプリを開きます

import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;

import java.io.ByteArrayOutputStream;

public class AppIconConverter {

    //获取应用图标
    public static byte[] getAppIcon(PackageInfo packageInfo,PackageManager packageManager){
        try{
            Drawable drawable;
            drawable = packageInfo.applicationInfo.loadIcon(packageManager);
            Bitmap bitmap = drawableToBitmap(drawable);
            byte[] appIcon;
            appIcon = bitmapToByte(bitmap);
            return appIcon;
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }

    //Drawable转Bitmap
    private static Bitmap drawableToBitmap(Drawable drawable){
        //取drawable的长宽
        int width = drawable.getIntrinsicWidth();
        int height = drawable.getIntrinsicHeight();
        //取drawable的颜色格式
        Bitmap.Config config = Bitmap.Config.ARGB_8888;
        //创建对应Bitmap
        Bitmap bitmap = Bitmap.createBitmap(width,height,config);
        //建立对应Bitmap的画布
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0,0,width,height);
        //把drawable内容画到画布中
        drawable.draw(canvas);
        return bitmap;
    }

    //Bitmap转Byte
    private static byte[] bitmapToByte(Bitmap bitmap){
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
        return stream.toByteArray();
    }
}

(3) アプリケーション情報格納クラスの作成

        アプリケーション名、パッケージ名、アイコンなどの情報を保存するために使用されます。

public class AppInfo {
    public String appName;    //应用名称
    public String packageName;//应用包名
    public byte[] appIcon;    //应用图标,这里以字节数组形式存储,便于Unity端进行解析

    public AppInfo(){
        appName = "";
        packageName = "";
        appIcon = null;
    }

    public void setAppName(String _appName){
        this.appName = _appName;
    }

    public void setPackageName(String _packageName){
        this.packageName = _packageName;
    }
    public String getPackageName(){
        return this.packageName;
    }

    public void setAppIcon(byte[] _appIcon){
        this.appIcon = _appIcon;
    }
}

4. AndroidManifest.xml ファイル構成

        次のように、不完全なアプリケーション アクセスを回避するためのアクセス許可を追加します。

<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>

5. ビルド変数を変更してリリースします

        「ビルド」-->「ビルド バリアントの選択...」をクリックし、開いたインターフェイスでモジュールのビルド バリアントをリリースとして選択します。

6. AAR パッケージの難読化処理

(1) build.gradleの修正

        モジュールで、build.gradle ファイルを見つけて開き、buildTypes の release ブロックの minifyEnabled を true に変更します。 

(2) 混乱を招くファイル変更

        モジュールで、proguard-rules.pro ファイルを見つけて開き、次のように記述します。

-keep class 包名.ObtainApplicationListUtils{public <methods>;}
-keepclasseswithmembers class 包名.AppInfo{<fields>;}

        最初の行は、ObtainApplicationListUtils クラスのパブリック メソッドが難読化されていないことを示し、2 行目は、AppInfo クラスのメンバー (変数) が難読化されていないことを示しています。これらはすべて Unity 側で呼び出されます。

7. AAR パッケージをビルドする

        モジュールを選択し、「ビルド」-->「モジュールの作成」をクリックするか、直接「ビルド」-->「プロジェクトの再構築」をクリックします。

        コンパイルが完了すると、コンパイルされた AAR パッケージが module -->build-->outputs-->aar に表示されます。

3. ユニティコール

1. AAR パッケージの配置

        AAR パッケージを Unity の「アセット」-->「プラグイン」-->「Android」パスに直接ドラッグ アンド ドロップします。

2. C# スクリプトを作成し、AAR パッケージを呼び出します。

(1) コールクラス

        AARパッケージ内の関数インターフェースを呼び出すには、以下の内容を記述します。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ObtainApplicationListManager : MonoBehaviour
{
    public GameObject appItemPrefab;//每一个应用信息预制件
    public Transform[] appListParent; //应用列表父物体

    private AndroidJavaObject getApplicationListUtils;
    private List<AppInfo> appInfoLists = new List<AppInfo>();

    //应用信息结构体
    public struct AppInfo
    {
        public string appName;
        public string packageName;
        public byte[] appIcon;

        public AppInfo(string _appName,string _packageName,byte[] _appIcon)
        {
            appName = "";
            packageName = "";
            appIcon = null;
        }

        public void SetAppName(string _appName)
        {
            appName = _appName;
        }

        public void SetPackageName(string _packageName)
        {
            packageName = _packageName;
        }

        public void SetAppIcon(byte[] _appIcon)
        {
            appIcon = _appIcon;
        }
    }

    private void Awake()
    {
        AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
        AndroidJavaObject currentActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
        AndroidJavaClass getApplicationListUtilsClass = new AndroidJavaClass("包名.ObtainApplicationListUtils");
        getApplicationListUtils = getApplicationListUtilsClass.CallStatic<AndroidJavaObject>("getInstance", currentActivity);
    }
    // Start is called before the first frame update
    void Start()
    {
        //开始默认设置所有应用列表
        SetAllApplicationList();
    }

    IEnumerator AppListSpawn(int action)
    {
        yield return 0;

        //查找已存在的应用列表,若有,销毁
        GameObject[] apps;
        apps = GameObject.FindGameObjectsWithTag("App");
        if (apps != null)
        {
            for(int i = 0; i < apps.Length; i++)
            {
                Destroy(apps[i]);
            }
            apps = null;
        }

        //在UI界面展示应用列表
        for (int i = 0; i < appInfoLists.Count; i++)
        {
            int index = i;//对于每一个i重新开辟内存空间,在按钮添加委托事件时防止自动闭包
            
            //生成应用UI
            GameObject app = Instantiate(appItemPrefab);
            app.tag = "App";
            app.transform.SetParent(appListParent[action]);
            app.transform.localScale = Vector3.one;

            //设置应用信息
            var appItem = app.GetComponent<AppItem>();
            if (appItem != null)
            {
                //设置应用名称
                appItem.appNameText.text = appInfoLists[i].appName;
                //设置应用图标
                Texture2D texture2D = new Texture2D(100, 100);
                texture2D.LoadImage(appInfoLists[i].appIcon);
                appItem.appIcon.texture = texture2D;
                
                //设置应用图标点击事件,打开自身
                appItem.openAppButton.onClick.AddListener(delegate ()
                {
                    OpenApplication(appInfoLists[index].packageName);
                });
            }
        }
    }

    /// <summary>
    /// 获取除系统应用以外的所有应用列表
    /// </summary>
    /// <param name="isFilterSystem"> 是否过滤系统应用 </param>
    /// <returns></returns>
    public List<AppInfo> GetAllApplicationList(bool isFilterSystem)
    {
        AndroidJavaObject[] appInfoList = getApplicationListUtils.Call<AndroidJavaObject>("getAllApplicationList", isFilterSystem).Call<AndroidJavaObject[]>("toArray");
        List<AppInfo> appInfos = new List<AppInfo>();
        foreach(AndroidJavaObject obj in appInfoList)
        {
            appInfos.Add(UnpackAppInfoObj(obj));
        }
        return appInfos;
    }

    /// <summary>
    /// 获取除系统应用以外的指定应用列表
    /// </summary>
    /// <param name="isFilterSystem"> 是否过滤系统应用 </param>
    /// <returns></returns>
    public List<AppInfo> GetSpecificApplicationList(bool isFilterSystem)
    {
        AndroidJavaObject[] appInfoList = getApplicationListUtils.Call<AndroidJavaObject>("getSpecificApplicationList", isFilterSystem).Call<AndroidJavaObject[]>("toArray");
        List<AppInfo> appInfos = new List<AppInfo>();
        foreach (AndroidJavaObject obj in appInfoList)
        {
            appInfos.Add(UnpackAppInfoObj(obj));
        }
        return appInfos;
    }

    /// <summary>
    /// 应用信息赋值
    /// </summary>
    /// <param name="appInfoObj"> 安卓端AppInfo </param>
    /// <returns></returns>
    private AppInfo UnpackAppInfoObj(AndroidJavaObject appInfoObj)
    {
        AppInfo appInfo = new AppInfo();
        appInfo.SetAppName(appInfoObj.Get<string>("appName"));
        appInfo.SetPackageName(appInfoObj.Get<string>("packageName"));
        appInfo.SetAppIcon(appInfoObj.Get<byte[]>("appIcon"));
        return appInfo;
    }

    /// <summary>
    /// 打开应用
    /// </summary>
    /// <param name="packageName"> 应用包名 </param>
    public void OpenApplication(string packageName)
    {
        getApplicationListUtils.Call("startApp", packageName);
    }

    /// <summary>
    /// 设置除系统应用以外的所有应用列表
    /// </summary>
    public void SetAllApplicationList()
    {
        //获取应用列表
        appInfoLists = GetAllApplicationList(true);

        //在UI界面展示应用列表
        StartCoroutine(AppListSpawn(0));
    }

    /// <summary>
    /// 设置除系统应用以外的指定应用列表
    /// </summary>
    public void SetSpecificApplicationList()
    {
        //获取应用列表
        appInfoLists = GetSpecificApplicationList(true);

        //在UI界面展示应用列表
        StartCoroutine(AppListSpawn(1));
    }

    /// <summary>
    /// 打开系统设置
    /// </summary>
    public void OpenSystemSettings()
    {
        getApplicationListUtils.Call("openSystemSettings");
    }

    /// <summary>
    /// 退出
    /// </summary>
    public void ExitApp()
    {
        Application.Quit();
    }
}

(2) UI情報クラス

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class AppItem : MonoBehaviour
{
    public Text appNameText;    //应用名称显示文本
    public Button openAppButton;//打开应用按钮
    public RawImage appIcon;    //应用图标
}

(3) スライダー初期化クラス

        この場合、すべてのアプリケーションを表示するビューと、指定されたパッケージ名のアプリケーションを表示するビューの 2 つの部分が存在します。一方のビューが表示された後に、一方のビュー内のスライダーが再度表示されるのを防ぐために、スライダーは表示されません。前回のスライド操作により最初は一番上の位置にある問題があるため、ここで次のようにその位置を初期化します。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class ScrollbarInitialize : MonoBehaviour
{
    //public Scrollbar scrollbar;
    public RectTransform rectTransform;

    private void OnEnable()
    {
        //scrollbar.value = 0;
        rectTransform.anchoredPosition = new Vector2(rectTransform.anchoredPosition.x, 0);
    }
}

3.UGUIのデザイン

(1) レイアウト概要

        UI レイアウトには 2 つのトグルが含まれており、これらはそれぞれ、ObtainApplicationListManager スクリプトの SetAllApplicationList メソッドと SetSpecificApplicationList メソッドを呼び出して、すべてのアプリケーションと指定されたパッケージ名を持つアプリケーションを表示するために使用されます。

        生成されたアプリケーション リストの親オブジェクトとしての 2 つのスクロール ビュー。

        ObtainApplicationListManager スクリプトの ExitApp メソッドを呼び出す終了ボタン。

        ObtainApplicationListManager スクリプトの OpenSystemSettings メソッドを呼び出して Android システム設定を開くシステム設定ボタン。

        UI レイアウトを次の図に示します。

(2) トグル設定

        以下に示すように、空のオブジェクトを作成し、Toggle Group コンポーネントをマウントします。

        クリック時のハイライト表示など、Toggleコンポーネントの画像表示を設定できます。以下の図に示すように、上記の Toggle Group に値を割り当て、Toggle コンポーネントで実行メソッドを呼び出します。 

        Background の Image コンポーネントは非ハイライト画像に設定されており、他の Toggle をクリックすると、Toggle コンポーネントの画像切り替えの設定に従って非ハイライト画像になります。 

        背景のサブオブジェクトのチェックマークは、自身のToggleを選択すると表示されるので、そのImageコンポーネントの画像をハイライト画像として設定します。 

(3) スクロールビュー設定

        Scroll Rectの水平のチェックを外し、その下のスクロールバーの水平を削除します。ScrollbarInitialize スクリプト コンポーネントをマウントし、その下の Content を RectTransform 変数に割り当てます。 

        Grid Layout Group コンポーネントと Content Size Fitter コンポーネントをその下の Content オブジェクトにマウントし、実際のニーズに応じて Grid Layout Group コンポーネントのセル サイズ、間隔、子の配置を設定し、Content Size Fitter コンポーネントの垂直フィットを設定します。 

4. Android プラットフォームに切り替え、APK をパックし、インストールしてテストします

4. AAR パッケージリソースのダウンロード入口

        上記のチュートリアルを読んでもパッケージ化の方法がわからない友人がいる場合は、次の AAR パッケージを直接ダウンロードして直接使用できます。その中にはプラグインのパッケージ名が含まれているので、「パッケージ名」を置き換えるだけです。 ObtainApplicationListManager スクリプト。

        Android側のアプリ一覧のAARプラグインパッケージを取得

おすすめ

転載: blog.csdn.net/qq_40364278/article/details/132171465