Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 27283f7e authored by Sunny Goyal's avatar Sunny Goyal
Browse files

Setting predition enabled/disabled state based on callbacks from the service

> If no callback is received within a timeout, it is set to disabled

Bug: 131188880
Change-Id: Ie6022b190a2504739f1569a500d6f5cc1566d373
parent d9c0fe13
Loading
Loading
Loading
Loading
+10 −37
Original line number Diff line number Diff line
@@ -15,7 +15,7 @@
 */
package com.android.launcher3.appprediction;

import static com.android.launcher3.appprediction.PredictionUiStateManager.KEY_APP_SUGGESTION;
import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_GRID;

import android.annotation.TargetApi;
import android.app.prediction.AppPredictionContext;
@@ -26,8 +26,6 @@ import android.app.prediction.AppTargetEvent;
import android.app.prediction.AppTargetId;
import android.content.ComponentName;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.os.Build;
import android.os.Handler;
import android.os.Message;
@@ -35,12 +33,10 @@ import android.os.UserHandle;
import android.util.Log;

import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.Utilities;
import com.android.launcher3.appprediction.PredictionUiStateManager.Client;
import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.util.UiThreadHelper;

import com.android.launcher3.appprediction.PredictionUiStateManager.Client;

import androidx.annotation.UiThread;
import androidx.annotation.WorkerThread;

@@ -48,8 +44,7 @@ import androidx.annotation.WorkerThread;
 * Subclass of app tracker which publishes the data to the prediction engine and gets back results.
 */
@TargetApi(Build.VERSION_CODES.Q)
public class PredictionAppTracker extends AppLaunchTracker
        implements OnSharedPreferenceChangeListener {
public class PredictionAppTracker extends AppLaunchTracker {

    private static final String TAG = "PredictionAppTracker";
    private static final boolean DBG = false;
@@ -62,8 +57,6 @@ public class PredictionAppTracker extends AppLaunchTracker
    private final Context mContext;
    private final Handler mMessageHandler;

    private boolean mEnabled;

    // Accessed only on worker thread
    private AppPredictor mHomeAppPredictor;
    private AppPredictor mRecentsOverviewPredictor;
@@ -71,24 +64,16 @@ public class PredictionAppTracker extends AppLaunchTracker
    public PredictionAppTracker(Context context) {
        mContext = context;
        mMessageHandler = new Handler(UiThreadHelper.getBackgroundLooper(), this::handleMessage);

        SharedPreferences prefs = Utilities.getPrefs(context);
        setEnabled(prefs.getBoolean(KEY_APP_SUGGESTION, true));
        prefs.registerOnSharedPreferenceChangeListener(this);
        InvariantDeviceProfile.INSTANCE.get(mContext).addOnChangeListener(this::onIdpChanged);

        mMessageHandler.sendEmptyMessage(MSG_INIT);
    }

    @UiThread
    private void onIdpChanged(int changeFlags, InvariantDeviceProfile profile) {
        if ((changeFlags & CHANGE_FLAG_GRID) != 0) {
            // Reinitialize everything
        setEnabled(mEnabled);
    }

    @Override
    @UiThread
    public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
        if (KEY_APP_SUGGESTION.equals(key)) {
            setEnabled(prefs.getBoolean(KEY_APP_SUGGESTION, true));
            mMessageHandler.sendEmptyMessage(MSG_INIT);
        }
    }

@@ -137,13 +122,13 @@ public class PredictionAppTracker extends AppLaunchTracker
                return true;
            }
            case MSG_LAUNCH: {
                if (mEnabled && mHomeAppPredictor != null) {
                if (mHomeAppPredictor != null) {
                    mHomeAppPredictor.notifyAppTargetEvent((AppTargetEvent) msg.obj);
                }
                return true;
            }
            case MSG_PREDICT: {
                if (mEnabled && mHomeAppPredictor != null) {
                if (mHomeAppPredictor != null) {
                    String client = (String) msg.obj;
                    if (Client.HOME.id.equals(client)) {
                        mHomeAppPredictor.requestPredictionUpdate();
@@ -168,18 +153,6 @@ public class PredictionAppTracker extends AppLaunchTracker
        }
    }

    @UiThread
    public void setEnabled(boolean isEnabled) {
        mEnabled = isEnabled;
        if (isEnabled) {
            mMessageHandler.removeMessages(MSG_DESTROY);
            mMessageHandler.sendEmptyMessage(MSG_INIT);
        } else {
            mMessageHandler.removeMessages(MSG_INIT);
            mMessageHandler.sendEmptyMessage(MSG_DESTROY);
        }
    }

    @Override
    @UiThread
    public void onStartShortcut(String packageName, String shortcutId, UserHandle user,
+30 −17
Original line number Diff line number Diff line
@@ -24,8 +24,7 @@ import android.app.prediction.AppPredictor;
import android.app.prediction.AppTarget;
import android.content.ComponentName;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.os.Handler;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;

import com.android.launcher3.AppInfo;
@@ -61,9 +60,10 @@ import java.util.List;
 * that client id.
 */
public class PredictionUiStateManager implements OnGlobalLayoutListener, ItemInfoUpdateReceiver,
        OnSharedPreferenceChangeListener, OnIDPChangeListener, OnUpdateListener {
        OnIDPChangeListener, OnUpdateListener {

    public static final String KEY_APP_SUGGESTION = "pref_show_predictions";
    public static final String LAST_PREDICTION_ENABLED_STATE = "last_prediction_enabled_state";
    private static final long INITIAL_CALLBACK_WAIT_TIMEOUT_MS = 5000;

    // TODO (b/129421797): Update the client constants
    public enum Client {
@@ -81,7 +81,6 @@ public class PredictionUiStateManager implements OnGlobalLayoutListener, ItemInf
            new MainThreadInitializedObject<>(PredictionUiStateManager::new);

    private final Context mContext;
    private final SharedPreferences mMainPrefs;

    private final DynamicItemCache mDynamicItemCache;
    private final List[] mPredictionServicePredictions;
@@ -94,9 +93,10 @@ public class PredictionUiStateManager implements OnGlobalLayoutListener, ItemInf
    private PredictionState mPendingState;
    private PredictionState mCurrentState;

    private boolean mGettingValidPredictionResults;

    private PredictionUiStateManager(Context context) {
        mContext = context;
        mMainPrefs = Utilities.getPrefs(context);

        mDynamicItemCache = new DynamicItemCache(context, this::onAppsUpdated);

@@ -110,8 +110,14 @@ public class PredictionUiStateManager implements OnGlobalLayoutListener, ItemInf
        for (int i = 0; i < mPredictionServicePredictions.length; i++) {
            mPredictionServicePredictions[i] = Collections.emptyList();
        }
        // Listens for enable/disable signal, and predictions if using AiAi is disabled.
        mMainPrefs.registerOnSharedPreferenceChangeListener(this);

        mGettingValidPredictionResults = Utilities.getDevicePrefs(context)
                .getBoolean(LAST_PREDICTION_ENABLED_STATE, true);
        if (mGettingValidPredictionResults) {
            new Handler().postDelayed(
                    this::updatePredictionStateAfterCallback, INITIAL_CALLBACK_WAIT_TIMEOUT_MS);
        }

        // Call this last
        mCurrentState = parseLastState();
    }
@@ -177,13 +183,6 @@ public class PredictionUiStateManager implements OnGlobalLayoutListener, ItemInf
        }
    }

    @Override
    public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
        if (KEY_APP_SUGGESTION.equals(key)) {
            dispatchOnChange(true);
        }
    }

    private void applyState(PredictionState state) {
        boolean wasEnabled = mCurrentState.isEnabled;
        mCurrentState = state;
@@ -198,10 +197,24 @@ public class PredictionUiStateManager implements OnGlobalLayoutListener, ItemInf
        }
    }

    private void updatePredictionStateAfterCallback() {
        boolean validResults = false;
        for (List l : mPredictionServicePredictions) {
            validResults |= l != null && !l.isEmpty();
        }
        if (validResults != mGettingValidPredictionResults) {
            mGettingValidPredictionResults = validResults;
            Utilities.getDevicePrefs(mContext).edit()
                    .putBoolean(LAST_PREDICTION_ENABLED_STATE, true)
                    .apply();
        }
        dispatchOnChange(true);
    }

    public AppPredictor.Callback appPredictorCallback(Client client) {
        return targets -> {
            mPredictionServicePredictions[client.ordinal()] = targets;
            dispatchOnChange(true);
            updatePredictionStateAfterCallback();
        };
    }

@@ -217,7 +230,7 @@ public class PredictionUiStateManager implements OnGlobalLayoutListener, ItemInf

    private PredictionState parseLastState() {
        PredictionState state = new PredictionState();
        state.isEnabled = mMainPrefs.getBoolean(KEY_APP_SUGGESTION, true);
        state.isEnabled = mGettingValidPredictionResults;
        if (!state.isEnabled) {
            state.apps = Collections.EMPTY_LIST;
            return state;
+17 −24
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.quickstep;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;

import android.app.prediction.AppPredictor;
import android.app.prediction.AppTarget;
@@ -25,7 +26,6 @@ import android.content.ComponentName;
import android.content.pm.LauncherActivityInfo;
import android.os.Process;
import android.view.View;
import android.widget.ProgressBar;

import com.android.launcher3.BubbleTextView;
import com.android.launcher3.Launcher;
@@ -50,7 +50,6 @@ import androidx.test.runner.AndroidJUnit4;
@LargeTest
@RunWith(AndroidJUnit4.class)
public class AppPredictionsUITests extends AbstractQuickStepTest {
    private static final String TAG = "AppPredictionsUITests";

    private LauncherActivityInfo mSampleApp1;
    private LauncherActivityInfo mSampleApp2;
@@ -91,20 +90,15 @@ public class AppPredictionsUITests extends AbstractQuickStepTest {
        mActivityMonitor.startLauncher();
        mLauncher.pressHome().switchToAllApps();

        // There has not been any update, verify that progress bar is showing
        waitForLauncherCondition("Prediction is not in loading state", launcher -> {
            ProgressBar p = findLoadingBar(launcher);
            return p != null && p.isShown();
        });

        // Dispatch an update
        sendPredictionUpdate(mSampleApp1, mSampleApp2);
        // The first update should apply immediately.
        waitForLauncherCondition("Predictions were not updated in loading state",
                launcher -> getPredictedApp(launcher).size() == 2);
    }

    /**
     * Test tat prediction update is deferred if it is already visible
     * Test that prediction update is deferred if it is already visible
     */
    @Test
    @Ignore // b/131188880
@@ -124,6 +118,19 @@ public class AppPredictionsUITests extends AbstractQuickStepTest {
        assertEquals(3, getFromLauncher(this::getPredictedApp).size());
    }

    @Test
    public void testPredictionsDisabled() {
        mActivityMonitor.startLauncher();
        sendPredictionUpdate();
        mLauncher.pressHome().switchToAllApps();

        waitForLauncherCondition("Predictions were not updated in loading state",
                launcher -> launcher.getAppsView().getFloatingHeaderView()
                        .findFixedRowByType(PredictionRowView.class).getVisibility() == View.GONE);
        assertFalse(PredictionUiStateManager.INSTANCE.get(mTargetContext)
                .getCurrentState().isEnabled);
    }

    public ArrayList<BubbleTextView> getPredictedApp(Launcher launcher) {
        PredictionRowView container = launcher.getAppsView().getFloatingHeaderView()
                .findFixedRowByType(PredictionRowView.class);
@@ -138,20 +145,6 @@ public class AppPredictionsUITests extends AbstractQuickStepTest {
        return predictedAppViews;
    }

    private ProgressBar findLoadingBar(Launcher launcher) {
        PredictionRowView container = launcher.getAppsView().getFloatingHeaderView()
                .findFixedRowByType(PredictionRowView.class);

        for (int i = 0; i < container.getChildCount(); i++) {
            View view = container.getChildAt(i);
            if (view instanceof ProgressBar) {
                return (ProgressBar) view;
            }
        }
        return null;
    }


    private void sendPredictionUpdate(LauncherActivityInfo... activities) {
        getOnUiThread(() -> {
            List<AppTarget> targets = new ArrayList<>(activities.length);