Unity calls the Android interface to obtain the application list

I. Introduction

        Encapsulate and obtain the application list interface in Android Studio, including application icon, application name and application package name, etc., and export the AAR package for Unity to call; Unity calls the interface encapsulated inside AAR through C# script, and displays the application list through UGUI. manage.

2. Android interactive function package

        Use Android Studio to encapsulate the call interface related to obtaining the application list into an AAR package for subsequent calls by Unity.

1. Create a new Android project

        I won’t go into details here, see: Use of Android WebView

2. Create a module

         I won’t go into details here, see: Use of Android WebView

3. Java class creation

(1) Create a method call class

        This class is used to create a method for Unity to call, the specific code is as follows:

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) Create an application icon conversion class

        After obtaining the application icon, you will get the data in Drawable format, which needs to be converted into a Bitmap, and then converted into a Byte array again for subsequent calls by Unity. Notes for icon acquisition: Android obtains application information by package name and opens the application

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) Create an application information storage class

        It is used to save information such as application name, package name and icon.

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 file configuration

        Add a permission to avoid incomplete application access, as follows:

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

5. Modify the Build variable to release

        Click Build-->Select Build Variant..., and select the Build Variant of the module as release in the opened interface.

6. AAR package obfuscation processing

(1) build.gradle modification

        In the module, find and open the build.gradle file, and modify the minifyEnabled of the release block in buildTypes to true. 

(2) Confused file modification

        In the module, find and open the proguard-rules.pro file and write the following:

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

        The first line indicates that the public methods in the ObtainApplicationListUtils class are not obfuscated, and the second line indicates that the members (variables) in the AppInfo class are not obfuscated. These are all called on the Unity side.

7. Build the AAR package

        Select the module, click Build-->Make Module, or directly Build-->Rebuild Project.

        After the compilation is completed, the compiled AAR package can be found in the module -->build-->outputs-->aar.

3. Unity call

1. AAR package placement

        Drag and drop the AAR package directly to Unity's Assets-->Plugins-->Android path.

2. Create a C# script and call the AAR package

(1) Call class

        Write the following content to call the function interface in the AAR package.

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 information class

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) Slider initialization class

        In this case, there will be two parts of View to display all applications and the application with the specified package name. In order to prevent the slider inside one of the Views from being displayed again after the other View is displayed, the slider is not at the top position at the beginning due to the previous sliding operation. problem, initialize its position here, as follows:

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 design

(1) Layout overview

        The UI layout contains two Toggles, which are used to call the SetAllApplicationList and SetSpecificApplicationList methods in the ObtainApplicationListManager script respectively to display all applications and applications with specified package names;

        Two Scroll Views, as the parent object of the generated application list;

        An exit button that calls the ExitApp method in the ObtainApplicationListManager script;

        A system settings button that calls the OpenSystemSettings method in the ObtainApplicationListManager script to open the Android system settings.

        The UI layout is shown in the figure below:

(2) Toggle settings

        Create an empty object and mount the Toggle Group component, as shown below:

        You can set the image display of the Toggle component, such as highlighting when clicked. Assign a value to the Toggle Group above, and call the execution method in the Toggle component, as shown in the figure below. 

        The Image component of the Background is set to a non-highlighted image, so that when other Toggles are clicked, it will become non-highlighted according to the settings in the image switching of the Toggle component. 

        The sub-object Checkmark of the Background will be displayed when its own Toggle is selected, so set the image of its Image component as a highlighted image. 

(3) Scroll View Settings

        Uncheck the Horizontal of Scroll Rect and delete the Scrollbar Horizontal under it. Mount the ScrollbarInitialize script component, and assign the Content under it to the RectTransform variable. 

        Mount the Grid Layout Group component and Content Size Fitter component to the Content object under it, set the Cell Size, Spacing and Child Alignment of the Grid Layout Group component according to actual needs, and set the Vertical Fit of the Content Size Fitter component. 

4. Switch to Android platform, pack apk, install and test

4. AAR package resource download entrance

        If some friends don't know how to package after reading the above tutorial, you can directly download the following AAR package and use it directly. There is the package name of the plug-in in it, just replace the "package name" in the ObtainApplicationListManager script.

        Get the AAR plug-in package of the application list on the Android side

Guess you like

Origin blog.csdn.net/qq_40364278/article/details/132171465