Merge "Update sysui flags" into ub-launcher3-qt-dev
diff --git a/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java b/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java
index 6bc859a..af07aa3 100644
--- a/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java
+++ b/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java
@@ -23,8 +23,10 @@
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
-import android.graphics.Point;
+import android.graphics.Path;
+import android.graphics.PathMeasure;
 import android.graphics.Rect;
+import android.graphics.RectF;
 import android.util.Log;
 import android.view.ViewDebug;
 
@@ -36,33 +38,51 @@
     private static final String TAG = "DotRenderer";
 
     // The dot size is defined as a percentage of the app icon size.
-    private static final float SIZE_PERCENTAGE = 0.38f;
+    private static final float SIZE_PERCENTAGE = 0.228f;
 
-    // Extra scale down of the dot
-    private static final float DOT_SCALE = 0.6f;
-
-    // Offset the dot slightly away from the icon if there's space.
-    private static final float OFFSET_PERCENTAGE = 0.02f;
-
-    private final float mDotCenterOffset;
-    private final int mOffset;
     private final float mCircleRadius;
     private final Paint mCirclePaint = new Paint(ANTI_ALIAS_FLAG | FILTER_BITMAP_FLAG);
 
     private final Bitmap mBackgroundWithShadow;
     private final float mBitmapOffset;
 
-    public DotRenderer(int iconSizePx) {
-        mDotCenterOffset = SIZE_PERCENTAGE * iconSizePx;
-        mOffset = (int) (OFFSET_PERCENTAGE * iconSizePx);
+    // Stores the center x and y position as a percentage (0 to 1) of the icon size
+    private final float[] mRightDotPosition;
+    private final float[] mLeftDotPosition;
 
-        int size = (int) (DOT_SCALE * mDotCenterOffset);
+    public DotRenderer(int iconSizePx, Path iconShapePath, int pathSize) {
+        int size = Math.round(SIZE_PERCENTAGE * iconSizePx);
         ShadowGenerator.Builder builder = new ShadowGenerator.Builder(Color.TRANSPARENT);
         builder.ambientShadowAlpha = 88;
         mBackgroundWithShadow = builder.setupBlurForSize(size).createPill(size, size);
         mCircleRadius = builder.radius;
 
         mBitmapOffset = -mBackgroundWithShadow.getHeight() * 0.5f; // Same as width.
+
+        // Find the points on the path that are closest to the top left and right corners.
+        mLeftDotPosition = getPathPoint(iconShapePath, pathSize, -1);
+        mRightDotPosition = getPathPoint(iconShapePath, pathSize, 1);
+    }
+
+    private static float[] getPathPoint(Path path, float size, float direction) {
+        float halfSize = size / 2;
+        // Small delta so that we don't get a zero size triangle
+        float delta = 1;
+
+        float x = halfSize + direction * halfSize;
+        Path trianglePath = new Path();
+        trianglePath.moveTo(halfSize, halfSize);
+        trianglePath.lineTo(x + delta * direction, 0);
+        trianglePath.lineTo(x, -delta);
+        trianglePath.close();
+
+        trianglePath.op(path, Path.Op.INTERSECT);
+        float[] pos = new float[2];
+        new PathMeasure(trianglePath, false).getPosTan(0, pos, null);
+
+        pos[0] = pos[0] / size;
+        pos[1] = pos[1] / size;
+        return pos;
     }
 
     /**
@@ -74,15 +94,21 @@
             return;
         }
         canvas.save();
-        // We draw the dot relative to its center.
-        float dotCenterX = params.leftAlign
-                ? params.iconBounds.left + mDotCenterOffset / 2
-                : params.iconBounds.right - mDotCenterOffset / 2;
-        float dotCenterY = params.iconBounds.top + mDotCenterOffset / 2;
 
-        int offsetX = Math.min(mOffset, params.spaceForOffset.x);
-        int offsetY = Math.min(mOffset, params.spaceForOffset.y);
-        canvas.translate(dotCenterX + offsetX, dotCenterY - offsetY);
+        Rect iconBounds = params.iconBounds;
+        float[] dotPosition = params.leftAlign ? mLeftDotPosition : mRightDotPosition;
+        float dotCenterX = iconBounds.left + iconBounds.width() * dotPosition[0];
+        float dotCenterY = iconBounds.top + iconBounds.height() * dotPosition[1];
+
+        // Ensure dot fits entirely in canvas clip bounds.
+        Rect canvasBounds = canvas.getClipBounds();
+        float offsetX = params.leftAlign
+                ? Math.max(0, canvasBounds.left - (dotCenterX + mBitmapOffset))
+                : Math.min(0, canvasBounds.right - (dotCenterX - mBitmapOffset));
+        float offsetY = Math.max(0, canvasBounds.top - (dotCenterY + mBitmapOffset));
+
+        // We draw the dot relative to its center.
+        canvas.translate(dotCenterX + offsetX, dotCenterY + offsetY);
         canvas.scale(params.scale, params.scale);
 
         mCirclePaint.setColor(Color.BLACK);
@@ -102,9 +128,6 @@
         /** The progress of the animation, from 0 to 1. */
         @ViewDebug.ExportedProperty(category = "notification dot")
         public float scale;
-        /** Overrides internally calculated offset if specified value is smaller. */
-        @ViewDebug.ExportedProperty(category = "notification dot")
-        public Point spaceForOffset = new Point();
         /** Whether the dot should align to the top left of the icon rather than the top right. */
         @ViewDebug.ExportedProperty(category = "notification dot")
         public boolean leftAlign;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
index 61ae880..a343a36 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -429,8 +429,7 @@
         MotionEvent event = (MotionEvent) ev;
         TOUCH_INTERACTION_LOG.addLog("onMotionEvent", event.getActionMasked());
         if (event.getAction() == ACTION_DOWN) {
-            if (isInValidSystemUiState()
-                    && mSwipeTouchRegion.contains(event.getX(), event.getY())) {
+            if (mSwipeTouchRegion.contains(event.getX(), event.getY())) {
                 boolean useSharedState = mConsumer.useSharedSwipeState();
                 mConsumer.onConsumerAboutToBeSwitched();
                 mConsumer = newConsumer(useSharedState, event);
@@ -443,15 +442,45 @@
         mUncheckedConsumer.onMotionEvent(event);
     }
 
-    private boolean isInValidSystemUiState() {
-        return (mSystemUiStateFlags & SYSUI_STATE_NAV_BAR_HIDDEN) == 0
-                && (mSystemUiStateFlags & SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED) == 0
-                && !ActivityManagerWrapper.getInstance().isLockToAppActive();
-    }
 
     private InputConsumer newConsumer(boolean useSharedState, MotionEvent event) {
-        // TODO: this makes a binder call every touch down. we should move to a listener pattern.
-        if (!mIsUserUnlocked || mKM.isDeviceLocked()) {
+        boolean validSystemUIFlags = (mSystemUiStateFlags & SYSUI_STATE_NAV_BAR_HIDDEN) == 0
+                && (mSystemUiStateFlags & SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED) == 0;
+        boolean topTaskLocked = ActivityManagerWrapper.getInstance().isLockToAppActive();
+        boolean isInValidSystemUiState = validSystemUIFlags && !topTaskLocked;
+
+        if (!mIsUserUnlocked) {
+            if (isInValidSystemUiState) {
+                // This handles apps launched in direct boot mode (e.g. dialer) as well as apps
+                // launched while device is locked even after exiting direct boot mode (e.g. camera).
+                return new DeviceLockedInputConsumer(this);
+            } else {
+                return InputConsumer.NO_OP;
+            }
+        }
+
+        InputConsumer base = isInValidSystemUiState
+                ? newBaseConsumer(useSharedState, event) : InputConsumer.NO_OP;
+        if (mMode == Mode.NO_BUTTON) {
+            final ActivityControlHelper activityControl =
+                    mOverviewComponentObserver.getActivityControlHelper();
+            if (mAssistantAvailable && !topTaskLocked
+                    && AssistantTouchConsumer.withinTouchRegion(this, event)) {
+                base = new AssistantTouchConsumer(this, mISystemUiProxy, activityControl, base,
+                        mInputMonitorCompat);
+            }
+
+            if ((mSystemUiStateFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0) {
+                base = new AccessibilityInputConsumer(this, mISystemUiProxy,
+                        (mSystemUiStateFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0, base,
+                        mInputMonitorCompat);
+            }
+        }
+        return base;
+    }
+
+    private InputConsumer newBaseConsumer(boolean useSharedState, MotionEvent event) {
+        if (mKM.isDeviceLocked()) {
             // This handles apps launched in direct boot mode (e.g. dialer) as well as apps launched
             // while device is locked even after exiting direct boot mode (e.g. camera).
             return new DeviceLockedInputConsumer(this);
@@ -465,42 +494,26 @@
         final ActivityControlHelper activityControl =
                 mOverviewComponentObserver.getActivityControlHelper();
 
-        InputConsumer base;
         if (runningTaskInfo == null && !mSwipeSharedState.goingToLauncher
                 && !mSwipeSharedState.recentsAnimationFinishInterrupted) {
-            base = InputConsumer.NO_OP;
+            return InputConsumer.NO_OP;
         } else if (mSwipeSharedState.recentsAnimationFinishInterrupted) {
             // If the finish animation was interrupted, then continue using the other activity input
             // consumer but with the next task as the running task
             RunningTaskInfo info = new ActivityManager.RunningTaskInfo();
             info.id = mSwipeSharedState.nextRunningTaskId;
-            base = createOtherActivityInputConsumer(event, info);
+            return createOtherActivityInputConsumer(event, info);
         } else if (mSwipeSharedState.goingToLauncher || activityControl.isResumed()) {
-            base = OverviewInputConsumer.newInstance(activityControl, mInputMonitorCompat, false);
+            return OverviewInputConsumer.newInstance(activityControl, mInputMonitorCompat, false);
         } else if (ENABLE_QUICKSTEP_LIVE_TILE.get() &&
                 activityControl.isInLiveTileMode()) {
-            base = OverviewInputConsumer.newInstance(activityControl, mInputMonitorCompat, false);
+            return OverviewInputConsumer.newInstance(activityControl, mInputMonitorCompat, false);
         } else if (mGestureBlockingActivity != null && runningTaskInfo != null
                 && mGestureBlockingActivity.equals(runningTaskInfo.topActivity)) {
-            base = InputConsumer.NO_OP;
+            return InputConsumer.NO_OP;
         } else {
-            base = createOtherActivityInputConsumer(event, runningTaskInfo);
+            return createOtherActivityInputConsumer(event, runningTaskInfo);
         }
-
-        if (mMode == Mode.NO_BUTTON) {
-            if (mAssistantAvailable && AssistantTouchConsumer.withinTouchRegion(this, event)) {
-                base = new AssistantTouchConsumer(this, mISystemUiProxy, activityControl, base,
-                        mInputMonitorCompat);
-            }
-
-            if ((mSystemUiStateFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0) {
-                base = new AccessibilityInputConsumer(this, mISystemUiProxy,
-                        (mSystemUiStateFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0, base,
-                        mInputMonitorCompat);
-            }
-        }
-
-        return base;
     }
 
     private OtherActivityInputConsumer createOtherActivityInputConsumer(MotionEvent event,
diff --git a/res/layout/hotseat.xml b/res/layout/hotseat.xml
new file mode 100644
index 0000000..82b0b8d
--- /dev/null
+++ b/res/layout/hotseat.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<com.android.launcher3.Hotseat
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:launcher="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/hotseat"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:theme="@style/HomeScreenElementTheme"
+    android:importantForAccessibility="no"
+    launcher:containerType="hotseat" />
\ No newline at end of file
diff --git a/res/layout/launcher.xml b/res/layout/launcher.xml
index 6ecc1f5..cca899b 100644
--- a/res/layout/launcher.xml
+++ b/res/layout/launcher.xml
@@ -39,13 +39,9 @@
             launcher:pageIndicator="@+id/page_indicator" />
 
         <!-- DO NOT CHANGE THE ID -->
-        <com.android.launcher3.Hotseat
+        <include
             android:id="@+id/hotseat"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:theme="@style/HomeScreenElementTheme"
-            android:importantForAccessibility="no"
-            launcher:containerType="hotseat" />
+            layout="@layout/hotseat" />
 
         <include
             android:id="@+id/overview_panel"
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 8291acc..bff7f42 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -46,10 +46,12 @@
 import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.graphics.DrawableFactory;
 import com.android.launcher3.graphics.IconPalette;
+import com.android.launcher3.graphics.IconShape;
 import com.android.launcher3.graphics.PreloadIconDrawable;
 import com.android.launcher3.icons.DotRenderer;
 import com.android.launcher3.icons.IconCache.IconLoadRequest;
 import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
+import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.model.PackageItemInfo;
 import com.android.launcher3.views.ActivityContext;
 
@@ -388,7 +390,7 @@
     protected void drawDotIfNecessary(Canvas canvas) {
         if (!mForceHideDot && (hasDot() || mDotParams.scale > 0)) {
             getIconBounds(mDotParams.iconBounds);
-            mDotParams.spaceForOffset.set((getWidth() - mIconSize) / 2, getPaddingTop());
+            Utilities.scaleRectAboutCenter(mDotParams.iconBounds, IconShape.getNormalizationScale());
             final int scrollX = getScrollX();
             final int scrollY = getScrollY();
             canvas.translate(scrollX, scrollY);
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index c0affb9..3d2d7cf 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -24,15 +24,13 @@
 import android.graphics.Rect;
 import android.util.DisplayMetrics;
 import android.view.Surface;
-import android.view.View;
 import android.view.WindowManager;
 
 import com.android.launcher3.CellLayout.ContainerType;
+import com.android.launcher3.graphics.IconShape;
 import com.android.launcher3.icons.DotRenderer;
 import com.android.launcher3.icons.IconNormalizer;
 
-import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
-
 public class DeviceProfile {
 
     public final InvariantDeviceProfile inv;
@@ -240,7 +238,8 @@
         updateWorkspacePadding();
 
         // This is done last, after iconSizePx is calculated above.
-        mDotRenderer = new DotRenderer(iconSizePx);
+        mDotRenderer = new DotRenderer(iconSizePx, IconShape.getShapePath(),
+                IconShape.DEFAULT_PATH_SIZE);
     }
 
     public DeviceProfile copy(Context context) {
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 6fa9ba9..8d9c520 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -36,6 +36,8 @@
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
 
+import androidx.annotation.NonNull;
+
 import com.android.launcher3.Alarm;
 import com.android.launcher3.AppInfo;
 import com.android.launcher3.BubbleTextView;
@@ -50,11 +52,11 @@
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.OnAlarmListener;
 import com.android.launcher3.R;
-import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.SimpleOnStylusPressListener;
 import com.android.launcher3.StylusEventHelper;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.Workspace;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.dot.FolderDotInfo;
 import com.android.launcher3.dragndrop.BaseItemDragListener;
@@ -68,8 +70,6 @@
 import java.util.ArrayList;
 import java.util.List;
 
-import androidx.annotation.NonNull;
-
 /**
  * An icon that can appear on in the workspace representing an {@link Folder}.
  */
@@ -510,10 +510,11 @@
             Rect iconBounds = mDotParams.iconBounds;
             BubbleTextView.getIconBounds(this, iconBounds,
                     mLauncher.getWallpaperDeviceProfile().iconSizePx);
+            float iconScale = (float) mBackground.previewSize / iconBounds.width();
+            Utilities.scaleRectAboutCenter(iconBounds, iconScale);
 
             // If we are animating to the accepting state, animate the dot out.
             mDotParams.scale = Math.max(0, mDotScale - mBackground.getScaleProgress());
-            mDotParams.spaceForOffset.set(getWidth() - iconBounds.right, iconBounds.top);
             mDotParams.color = mBackground.getDotColor();
             mDotRenderer.draw(canvas, mDotParams);
         }
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index f5c5a8d..87ef044 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -374,16 +374,6 @@
                     event -> true,
                     "Pressing Home didn't produce any events");
             mDevice.waitForIdle();
-
-            // Temporarily press home twice as the first click sometimes gets ignored  (b/124239413)
-            executeAndWaitForEvent(
-                    () -> {
-                        log("LauncherInstrumentation.pressHome before clicking");
-                        waitForSystemUiObject("home").click();
-                    },
-                    event -> true,
-                    "Pressing Home didn't produce any events");
-            mDevice.waitForIdle();
         }
         try (LauncherInstrumentation.Closable c = addContextLayer(
                 "performed action to switch to Home - " + action)) {