content: /**
   * 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<LauncherActivityInfo> 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() {
          mDevice.pressHome();
          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() {
          mDevice.pressHome();
          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() {
          mDevice.pressHome();
          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);
  
          ArrayList<BubbleTextView> 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<AppTarget> 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;
          });
      }
  }
  