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.launcher3.uioverrides;
  
  import static com.android.launcher3.LauncherState.NORMAL;
  import static com.android.launcher3.LauncherState.OVERVIEW;
  import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
  
  import android.content.Context;
  import android.graphics.Rect;
  import android.view.Gravity;
  
  import com.android.launcher3.DeviceProfile;
  import com.android.launcher3.Launcher;
  import com.android.launcher3.LauncherState;
  import com.android.launcher3.LauncherStateManager.StateHandler;
  import com.android.launcher3.anim.AnimatorPlaybackController;
  import com.android.launcher3.config.FeatureFlags;
  import com.android.launcher3.graphics.RotationMode;
  import com.android.launcher3.uioverrides.touchcontrollers.FlingAndHoldTouchController;
  import com.android.launcher3.uioverrides.touchcontrollers.LandscapeEdgeSwipeController;
  import com.android.launcher3.uioverrides.touchcontrollers.NavBarToHomeTouchController;
  import com.android.launcher3.uioverrides.touchcontrollers.NoButtonQuickSwitchTouchController;
  import com.android.launcher3.uioverrides.touchcontrollers.OverviewToAllAppsTouchController;
  import com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController;
  import com.android.launcher3.uioverrides.touchcontrollers.QuickSwitchTouchController;
  import com.android.launcher3.uioverrides.touchcontrollers.StatusBarTouchController;
  import com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController;
  import com.android.launcher3.uioverrides.touchcontrollers.TransposedQuickSwitchTouchController;
  import com.android.launcher3.util.TouchController;
  import com.android.launcher3.util.UiThreadHelper;
  import com.android.launcher3.util.UiThreadHelper.AsyncCommand;
  import com.android.quickstep.SysUINavigationMode;
  import com.android.quickstep.SysUINavigationMode.Mode;
  import com.android.quickstep.TouchInteractionService;
  import com.android.quickstep.views.RecentsView;
  import com.android.systemui.shared.system.WindowManagerWrapper;
  
  import java.util.ArrayList;
  
  /**
   * Provides recents-related {@link UiFactory} logic and classes.
   */
  public abstract class RecentsUiFactory {
  
      public static final boolean GO_LOW_RAM_RECENTS_ENABLED = false;
      private static final AsyncCommand SET_SHELF_HEIGHT_CMD = (visible, height) ->
              WindowManagerWrapper.getInstance().setShelfHeight(visible != 0, height);
  
      public static RotationMode ROTATION_LANDSCAPE = new RotationMode(-90) {
          @Override
          public void mapRect(int left, int top, int right, int bottom, Rect out) {
              out.left = top;
              out.top = right;
              out.right = bottom;
              out.bottom = left;
          }
  
          @Override
          public void mapInsets(Context context, Rect insets, Rect out) {
              // If there is a display cutout, the top insets in portrait would also include the
              // cutout, which we will get as the left inset in landscape. Using the max of left and
              // top allows us to cover both cases (with or without cutout).
              if (SysUINavigationMode.getMode(context) == NO_BUTTON) {
                  out.top = Math.max(insets.top, insets.left);
                  out.bottom = Math.max(insets.right, insets.bottom);
                  out.left = out.right = 0;
              } else {
                  out.top = Math.max(insets.top, insets.left);
                  out.bottom = insets.right;
                  out.left = insets.bottom;
                  out.right = 0;
              }
          }
      };
  
      public static RotationMode ROTATION_SEASCAPE = new RotationMode(90) {
          @Override
          public void mapRect(int left, int top, int right, int bottom, Rect out) {
              out.left = bottom;
              out.top = left;
              out.right = top;
              out.bottom = right;
          }
  
          @Override
          public void mapInsets(Context context, Rect insets, Rect out) {
              if (SysUINavigationMode.getMode(context) == NO_BUTTON) {
                  out.top = Math.max(insets.top, insets.right);
                  out.bottom = Math.max(insets.left, insets.bottom);
                  out.left = out.right = 0;
              } else {
                  out.top = Math.max(insets.top, insets.right);
                  out.bottom = insets.left;
                  out.right = insets.bottom;
                  out.left = 0;
              }
          }
  
          @Override
          public int toNaturalGravity(int absoluteGravity) {
              int horizontalGravity = absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK;
              int verticalGravity = absoluteGravity & Gravity.VERTICAL_GRAVITY_MASK;
  
              if (horizontalGravity == Gravity.RIGHT) {
                  horizontalGravity = Gravity.LEFT;
              } else if (horizontalGravity == Gravity.LEFT) {
                  horizontalGravity = Gravity.RIGHT;
              }
  
              if (verticalGravity == Gravity.TOP) {
                  verticalGravity = Gravity.BOTTOM;
              } else if (verticalGravity == Gravity.BOTTOM) {
                  verticalGravity = Gravity.TOP;
              }
  
              return ((absoluteGravity & ~Gravity.HORIZONTAL_GRAVITY_MASK)
                      & ~Gravity.VERTICAL_GRAVITY_MASK)
                      | horizontalGravity | verticalGravity;
          }
      };
  
      public static RotationMode getRotationMode(DeviceProfile dp) {
          return !dp.isVerticalBarLayout() ? RotationMode.NORMAL
                  : (dp.isSeascape() ? ROTATION_SEASCAPE : ROTATION_LANDSCAPE);
      }
  
      public static TouchController[] createTouchControllers(Launcher launcher) {
          Mode mode = SysUINavigationMode.getMode(launcher);
  
          ArrayList<TouchController> list = new ArrayList<>();
          list.add(launcher.getDragController());
          if (mode == NO_BUTTON) {
              list.add(new NoButtonQuickSwitchTouchController(launcher));
              list.add(new NavBarToHomeTouchController(launcher));
              list.add(new FlingAndHoldTouchController(launcher));
          } else {
              if (launcher.getDeviceProfile().isVerticalBarLayout()) {
                  list.add(new OverviewToAllAppsTouchController(launcher));
                  list.add(new LandscapeEdgeSwipeController(launcher));
                  if (mode.hasGestures) {
                      list.add(new TransposedQuickSwitchTouchController(launcher));
                  }
              } else {
                  list.add(new PortraitStatesTouchController(launcher,
                          mode.hasGestures /* allowDragToOverview */));
                  if (mode.hasGestures) {
                      list.add(new QuickSwitchTouchController(launcher));
                  }
              }
          }
  
          if (FeatureFlags.PULL_DOWN_STATUS_BAR
                  && !launcher.getDeviceProfile().isMultiWindowMode) {
              list.add(new StatusBarTouchController(launcher));
          }
  
          list.add(new LauncherTaskViewController(launcher));
          return list.toArray(new TouchController[list.size()]);
      }
  
      /**
       * Creates and returns the controller responsible for recents view state transitions.
       *
       * @param launcher the launcher activity
       * @return state handler for recents
       */
      public static StateHandler createRecentsViewStateController(Launcher launcher) {
          return new RecentsViewStateController(launcher);
      }
  
      /**
       * Clears the swipe shared state for the current swipe gesture.
       */
      public static void clearSwipeSharedState(boolean finishAnimation) {
          TouchInteractionService.getSwipeSharedState().clearAllState(finishAnimation);
      }
  
      /**
       * Recents logic that triggers when launcher state changes or launcher activity stops/resumes.
       *
       * @param launcher the launcher activity
       */
      public static void onLauncherStateOrResumeChanged(Launcher launcher) {
          LauncherState state = launcher.getStateManager().getState();
          DeviceProfile profile = launcher.getDeviceProfile();
          boolean visible = (state == NORMAL || state == OVERVIEW) && launcher.isUserActive()
                  && !profile.isVerticalBarLayout();
          UiThreadHelper.runAsyncCommand(launcher, SET_SHELF_HEIGHT_CMD,
                  visible ? 1 : 0, profile.hotseatBarSizePx);
  
          if (state == NORMAL) {
              launcher.<RecentsView>getOverviewPanel().setSwipeDownShouldLaunchApp(false);
          }
      }
  
      private static final class LauncherTaskViewController extends
              TaskViewTouchController<Launcher> {
  
          LauncherTaskViewController(Launcher activity) {
              super(activity);
          }
  
          @Override
          protected boolean isRecentsInteractive() {
              return mActivity.isInState(OVERVIEW);
          }
  
          @Override
          protected void onUserControlledAnimationCreated(AnimatorPlaybackController animController) {
              mActivity.getStateManager().setCurrentUserControlledAnimation(animController);
          }
      }
  }
  