/** * 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.quickstep; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import android.app.prediction.AppPredictor; import android.app.prediction.AppTarget; import android.app.prediction.AppTargetId; import android.content.ComponentName; import android.content.pm.LauncherActivityInfo; import android.os.Process; import android.view.View; import androidx.test.filters.LargeTest; import androidx.test.runner.AndroidJUnit4; import com.android.launcher3.BubbleTextView; import com.android.launcher3.Launcher; import com.android.launcher3.appprediction.PredictionRowView; import com.android.launcher3.appprediction.PredictionUiStateManager; import com.android.launcher3.appprediction.PredictionUiStateManager.Client; import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.model.AppLaunchTracker; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import java.util.ArrayList; import java.util.List; @LargeTest @RunWith(AndroidJUnit4.class) public class AppPredictionsUITests extends AbstractQuickStepTest { private LauncherActivityInfo mSampleApp1; private LauncherActivityInfo mSampleApp2; private LauncherActivityInfo mSampleApp3; private AppPredictor.Callback mCallback; @Before public void setUp() throws Exception { super.setUp(); List activities = LauncherAppsCompat.getInstance(mTargetContext) .getActivityList(null, Process.myUserHandle()); mSampleApp1 = activities.get(0); mSampleApp2 = activities.get(1); mSampleApp3 = activities.get(2); // Disable app tracker AppLaunchTracker.INSTANCE.initializeForTesting(new AppLaunchTracker()); PredictionUiStateManager.INSTANCE.initializeForTesting(null); mCallback = PredictionUiStateManager.INSTANCE.get(mTargetContext).appPredictorCallback( Client.HOME); mDevice.setOrientationNatural(); } @After public void tearDown() throws Throwable { AppLaunchTracker.INSTANCE.initializeForTesting(null); PredictionUiStateManager.INSTANCE.initializeForTesting(null); mDevice.unfreezeRotation(); } /** * Test that prediction UI is updated as soon as we get predictions from the system */ @Test public void testPredictionExistsInAllApps() { mActivityMonitor.startLauncher(); mLauncher.pressHome().switchToAllApps(); // 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 that prediction update is deferred if it is already visible */ @Test public void testPredictionsDeferredUntilHome() { mActivityMonitor.startLauncher(); sendPredictionUpdate(mSampleApp1, mSampleApp2); mLauncher.pressHome().switchToAllApps(); waitForLauncherCondition("Predictions were not updated in loading state", launcher -> getPredictedApp(launcher).size() == 2); // Update predictions while all-apps is visible sendPredictionUpdate(mSampleApp1, mSampleApp2, mSampleApp3); assertEquals(2, getFromLauncher(this::getPredictedApp).size()); // Go home and go back to all-apps mLauncher.pressHome().switchToAllApps(); 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 getPredictedApp(Launcher launcher) { PredictionRowView container = launcher.getAppsView().getFloatingHeaderView() .findFixedRowByType(PredictionRowView.class); ArrayList predictedAppViews = new ArrayList<>(); for (int i = 0; i < container.getChildCount(); i++) { View view = container.getChildAt(i); if (view instanceof BubbleTextView && view.getVisibility() == View.VISIBLE) { predictedAppViews.add((BubbleTextView) view); } } return predictedAppViews; } private void sendPredictionUpdate(LauncherActivityInfo... activities) { getOnUiThread(() -> { List targets = new ArrayList<>(activities.length); for (LauncherActivityInfo info : activities) { ComponentName cn = info.getComponentName(); AppTarget target = new AppTarget.Builder(new AppTargetId("app:" + cn), cn.getPackageName(), info.getUser()) .setClassName(cn.getClassName()) .build(); targets.add(target); } mCallback.onTargetsAvailable(targets); return null; }); } }