Android OnDeviceAppPrediction 优化

最近在Android R的一个项目上,Google 在基于Launcher3的基础上,集成了一个OnDeviceAppPrediction服务。功能呢,就是在All App View界面上端显示常用的应用和近期使用的应用。在所接触的项目中,经测试同事的辛苦挖掘,找出了不少问题:

1.Google对出现在近期列表中的应用执行卸载操作后做一个填充列表的动作,导致列表出现空缺。

2.使用其他应用后,没有实时改刷新界面显示。

一下便是我针对这两个问题的做的优化:

1.卸载应用后,做填充

/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.apppredictionservice;

import android.app.prediction.AppPredictionContext;
import android.app.prediction.AppPredictionSessionId;
import android.app.prediction.AppTarget;
import android.app.prediction.AppTargetEvent;
import android.app.prediction.AppTargetId;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.CancellationSignal;
import android.service.appprediction.AppPredictionService;
import android.util.Log;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;

import static android.os.Process.myUserHandle;
import static android.text.TextUtils.isEmpty;
import static java.util.Collections.emptyList;
/*
 * New plugin that replaces prediction driven Aiai APK in P
 * PredictionService simply populates the top row of the app
 * drawer with the 5 most recently used apps. Each time a new
 * app is launched, it is added to the left of the top row.
 * Duplicates are not added.
 */
public class PredictionService extends AppPredictionService {

    public static final String MY_PREF = "mypref";

    private static final String TAG = PredictionService.class.getSimpleName();

    private static final int MAX_NUM = 5;
    private static final String[] DEFAULT_PACKAGES = new String[]{
            "com.google.android.apps.photos",   // Photos
            "com.google.android.apps.maps",     // Maps
            "com.google.android.gm",            // Gmail
            "com.android.settings",          // Settings
            "com.google.android.calculator" //Calculator
    };
    private final Set<AppPredictionSessionId> activeLauncherSessions = new HashSet<>();
    private final List<AppTarget> predictionList = new ArrayList<>(5);

    private final List<String> appNames = new ArrayList<>(5);
    private final String[] appNameKeys = new String[] {
            "first", "second", "third", "fourth", "fifth" };
    SharedPreferences sharedPreferences;
    SharedPreferences.Editor editor;

    private PackageRemovedBroadcastReceiver mPackageRemovedBroadcastReceiver;
    private String mMostRecentComponent;
    private AppTarget mAppTarget;

    private String mPackageName;
    private Context mContext;
    private boolean mRemovedLast = true;
    private boolean mDebug = false;
    private boolean mAppSuggestionsEnabled = true;

    @Override
    public void onCreate() {
        super.onCreate();

        Log.d(TAG, "AppPredictionService onCreate");
        this.sharedPreferences = getSharedPreferences(MY_PREF, Context.MODE_PRIVATE);
        this.editor = sharedPreferences.edit();
        if (sharedPreferences.getString(appNameKeys[0], "").isEmpty()) {
            // fill the list with defaults if first one is null when devices powers up for the first time
            for (int i = 0; i < appNameKeys.length; i++) {
                editor.putString(appNameKeys[i],
                        getLauncherComponent(DEFAULT_PACKAGES[i]).flattenToShortString());
            }
            this.editor.apply();
        }

        for (int i = 0; i < appNameKeys.length; i++) {
            if (appNameKeys[i] != null && appNameKeys[i].length() > 3) {
                String appName = sharedPreferences.getString(appNameKeys[i], "");
                ComponentName cn = ComponentName.unflattenFromString(appName);
                if (cn != null) {
                    AppTarget target = new AppTarget.Builder(
                            new AppTargetId(Integer.toString(i + 1)), cn.getPackageName(), myUserHandle())
                            .setClassName(cn.getClassName())
                            .build();
                    appNames.add(appName);
                    predictionList.add(target);
                }
            }
        }
        postPredictionUpdateToAllClients();

        mContext = this;
        if (mPackageRemovedBroadcastReceiver == null) {
            mPackageRemovedBroadcastReceiver = new PackageRemovedBroadcastReceiver();
            IntentFilter packageRemovedfilter = new IntentFilter();
            packageRemovedfilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
            packageRemovedfilter.addDataScheme("package");
            registerReceiver(mPackageRemovedBroadcastReceiver, packageRemovedfilter);
        }
    }

    @Override
    public void onDestroy() {

        if (mPackageRemovedBroadcastReceiver != null) {

            unregisterReceiver(mPackageRemovedBroadcastReceiver);
            mPackageRemovedBroadcastReceiver = null;
        }
    }

    private ComponentName getLauncherComponent(String packageName) {
        List<LauncherActivityInfo> infos = getSystemService(LauncherApps.class)
                .getActivityList(packageName, myUserHandle());
        if (infos.isEmpty()) {
            return new ComponentName(packageName, "#");
        } else {
            return infos.get(0).getComponentName();
        }
    }

    private boolean isComponentNameLauncherable(ComponentName cn) {
        List<LauncherActivityInfo> infos = getSystemService(LauncherApps.class)
                .getActivityList(cn.getPackageName(), myUserHandle());
        if (infos.isEmpty()) {
            return false;
        } else {
            for(int i =0; i <  infos.size(); i++){
                Log.d(TAG, "isComponentNameLauncherable  infos.get(i).getComponentName()="+infos.get(i).getComponentName());
                if(infos.get(i).getComponentName().equals(cn)){
                    return true;
                }
            }
        }
        return false;
    }

     private boolean isLauncherable(String packageName) {
        List<LauncherActivityInfo> infos = getSystemService(LauncherApps.class)
                .getActivityList(packageName, myUserHandle());
        if (infos.isEmpty()) {
            return false;
        } else {
            return true;
        }
    }

    private void postPredictionUpdate(AppPredictionSessionId sessionId) {
        updatePredictions(sessionId, mAppSuggestionsEnabled ? predictionList : emptyList());
    }

    private void postPredictionUpdateToAllClients() {
        for (AppPredictionSessionId session : activeLauncherSessions) {
            postPredictionUpdate(session);
        }
    }

    @Override
    public void onCreatePredictionSession(
            AppPredictionContext context, AppPredictionSessionId sessionId) {
        Log.d(TAG, "onCreatePredictionSession");

        if (context.getUiSurface().equals("home") || context.getUiSurface().equals("overview")) {
            activeLauncherSessions.add(sessionId);
            postPredictionUpdate(sessionId);
        }
    }

    @Override
    public void onAppTargetEvent(AppPredictionSessionId sessionId, AppTargetEvent event) {

        if (!activeLauncherSessions.contains(sessionId)) {
            return;
        }

        boolean found = false;
        Log.d(TAG, "onAppTargetEvent");

        AppTarget target = event.getTarget();
        if (target == null || isEmpty(target.getPackageName()) || isEmpty(target.getClassName())) {
            return;
        }
        boolean isPackageLauncherable = isLauncherable(target.getPackageName());
        if (mDebug) Log.i(TAG, "onAppTargetEvent  isPackageLauncherable = " + isPackageLauncherable + "   target.getPackageName()="+target.getPackageName());

        if (!isPackageLauncherable) {
            Log.d(TAG, "onAppTargetEvent  isPackageLauncherable = " + isPackageLauncherable);
            return;
        }

        ComponentName mostRecentCN = new ComponentName(
                target.getPackageName(), target.getClassName());

        if (mDebug)Log.d(TAG, "onAppTargetEvent  mostRecentCN = " + mostRecentCN);

        String mostRecentComponent = new ComponentName(
                target.getPackageName(), target.getClassName()).flattenToString();

       boolean isComponentNameLauncherable =  isComponentNameLauncherable(mostRecentCN);
       ComponentName launcherableComponent = getLauncherComponent(target.getPackageName());
       String launcherableComponentString   =    launcherableComponent.flattenToString();
       boolean isReplaceTarget = false;
       AppTarget launcherableTarget = event.getTarget();

       if(!isComponentNameLauncherable){
            if(!mostRecentComponent.equals(launcherableComponentString)){
                Log.d(TAG, "onAppTargetEvent  launcherableComponentString = " + launcherableComponentString + "  mostRecentComponent== " + mostRecentComponent);

                launcherableTarget = new AppTarget.Builder(
                                    new AppTargetId("app:" + launcherableComponent), launcherableComponent.getPackageName(), myUserHandle())
                                .setClassName(launcherableComponent.getClassName())
                                .build();
                mostRecentComponent = launcherableComponentString;
                isReplaceTarget = true;
            }
       }

        if (mDebug)
            Log.i(TAG, "onAppTargetEvent  mostRecentComponent = " + mostRecentComponent);
        // Check if packageName already exists in existing list of appNames

        for (int i = 0; i < appNames.size(); i++) {
            if (appNames.get(i).contains(target.getPackageName()) || appNames.get(i).equals(mostRecentComponent)) {
                    found = true;
                
                break;
            }
        }

        if (mDebug)
            Log.i(TAG, "onAppTargetEvent  found== " + found + "  target.getPackageName()== " + target.getPackageName() + "/" + target.getClassName());
        if (mDebug) Log.i(TAG, "onAppTargetEvent  appNames.size()== " + appNames.size());

        mRemovedLast = appNames.size() >= 4;
        if (mDebug) Log.i(TAG, "onAppTargetEvent  mRemovedLast== " + mRemovedLast);

        if (!found) {
            if (mRemovedLast) {
                appNames.remove(appNames.size() - 1);
            }

            appNames.add(0, mostRecentComponent);

            //Put the recent five app into sharedPreferences to store it
            for (int i = 0; i < MAX_NUM; i++) {
                if(i < appNames.size()){
                    editor.putString(appNameKeys[i], appNames.get(i));
                }
            }
            editor.apply();
            if (mRemovedLast) {
                predictionList.remove(predictionList.size() - 1);
            }
            if(isReplaceTarget){
                predictionList.add(0, launcherableTarget);
            }else{
                predictionList.add(0, event.getTarget());
            }

            Log.d(TAG, "onAppTargetEvent:: update predictions");
            postPredictionUpdateToAllClients();
        }
    }

    @Override
    public void onLaunchLocationShown(
            AppPredictionSessionId sessionId, String launchLocation, List<AppTargetId> targetIds) {
        Log.d(TAG, "onLaunchLocationShown");
    }

    @Override
    public void onSortAppTargets(
            AppPredictionSessionId sessionId,
            List<AppTarget> targets,
            CancellationSignal cancellationSignal,
            Consumer<List<AppTarget>> callback) {

        Log.d(TAG, "onSortAppTargets");
        if (!activeLauncherSessions.contains(sessionId)) {
            callback.accept(emptyList());
        } else {
            // No-op
            callback.accept(targets);
        }
    }

    @Override
    public void onRequestPredictionUpdate(AppPredictionSessionId sessionId) {
        Log.d(TAG, "onRequestPredictionUpdate");

        if (!activeLauncherSessions.contains(sessionId)) {
            updatePredictions(sessionId, emptyList());
        } else {
            postPredictionUpdate(sessionId);
            Log.d(TAG, "update predictions");
        }
    }

    @Override
    public void onDestroyPredictionSession(AppPredictionSessionId sessionId) {
        Log.d(TAG, "onDestroyPredictionSession");
        activeLauncherSessions.remove(sessionId);
    }

    @Override
    public void onStartPredictionUpdates() {
        Log.d(TAG, "onStartPredictionUpdates");
    }

    @Override
    public void onStopPredictionUpdates() {
        Log.d(TAG, "onStopPredictionUpdates");
    }

    public void setAppSuggestionsEnabled(boolean enabled) {
        mAppSuggestionsEnabled = enabled;
        postPredictionUpdateToAllClients();
    }

    private String getDefaultSystemHandlerActivityPackageName(Intent intent) {
        return getDefaultSystemHandlerActivityPackageName(intent, 0);
    }

    private String getDefaultSystemHandlerActivityPackageName(Intent intent, int flags) {
        ResolveInfo handler = getPackageManager().resolveActivity(intent, flags | PackageManager.MATCH_SYSTEM_ONLY);
        if (handler == null) {
            return null;
        }
        if ((handler.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
            return handler.activityInfo.packageName;
        }
        return null;
    }

    private void removePackage(String packageName) {

        if (null != packageName) {

            boolean isRemovePackage = false;
            for (int i = 0; i < appNames.size(); i++) {
                if (appNames.get(i).contains(packageName)) {
                    if (mDebug)Log.i(TAG, "removePackage 111   i== " + i + "    appNames.get(i) == " + appNames.get(i) + "   packageName == " + packageName);
                    appNames.remove(i);
                    predictionList.remove(i);
                    isRemovePackage = true;
                    break;
                }
            }
            List<ComponentName> AppNmaeExt = new ArrayList<>(5);
            if(isRemovePackage) {
                for(int i = 0;i < DEFAULT_PACKAGES.length;i++){
                    ComponentName mComponentName = getLauncherComponent(DEFAULT_PACKAGES[i]);
                    if(mComponentName != null){
                        boolean isHas = false;
                        if (mDebug) Log.i(TAG, "removePackage  222  i== " + i + "  packageName" + DEFAULT_PACKAGES[i] );
                        for(int j = 0;j < appNames.size();j++){
                            if (mDebug) Log.d(TAG,"removePackage  3333  j== " + j + "  appNames.get(j) == " + appNames.get(j));
                            if(appNames.get(j).contains(DEFAULT_PACKAGES[i])){
                                isHas = true;
                                continue;
                            }
                        }
                        if(!isHas){
                            AppNmaeExt.add(mComponentName);
                        }
                    }
                }
                if (mDebug) Log.d(TAG,"AppNmaeExt.size() ="+AppNmaeExt.size());
                for(int i = 0; i < AppNmaeExt.size();i++){
                    if (mDebug) Log.d(TAG,"AppNmaeExt.appName ="+AppNmaeExt.get(i).flattenToShortString());
                    if(appNames.size() < MAX_NUM){
                        appNames.add(AppNmaeExt.get(i).flattenToShortString());
                        AppTarget target = new AppTarget.Builder(
                                new AppTargetId(Integer.toString(i+1)), AppNmaeExt.get(i).getPackageName(), myUserHandle())
                                .setClassName(AppNmaeExt.get(i).getClassName())
                                .build();
                        predictionList.add(target);
                    }
                }
            }
            for (int i = 0; i < MAX_NUM; i++) {
                if(i < appNames.size()){
                    editor.putString(appNameKeys[i], appNames.get(i));
                }
            }
            editor.apply();
            Log.d(TAG,"removePackage end");
            postPredictionUpdateToAllClients();
        }
    }
    private class PackageRemovedBroadcastReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {

            if (intent.getAction().equals(Intent.ACTION_PACKAGE_REMOVED)) {
                String packageName = intent.getData().getSchemeSpecificPart();
                mPackageName = packageName;
                removePackage(mPackageName);
                if (mDebug) Log.i(TAG, "PackageRemovedBroadcastReceiver onReceive  ACTION_PACKAGE_REMOVED  == " + packageName);
            }
        }
    }

}

 2.改变列表做实时刷新

packages/apps/Launcher3/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java

diff --git
@@ -184,6 +184,11 @@ public class PredictionUiStateManager implements StateListener<LauncherState>,
             // Add a listener and wait until appsView is invisible again.
             Launcher.getLauncher(mAppsView.getContext()).getStateManager().addStateListener(this);
         }
+        //add .begin
+        if(mPendingState != null){
+            applyState(mPendingState);
+        }
+        //add .end
     }

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值