Merge "Removing OAV HIDDEN_GESTURE_RUNNING flag." into ub-launcher3-master
diff --git a/quickstep/res/layout/gesture_tutorial_fragment.xml b/quickstep/res/layout/gesture_tutorial_fragment.xml
index 43bf0ea..52475df 100644
--- a/quickstep/res/layout/gesture_tutorial_fragment.xml
+++ b/quickstep/res/layout/gesture_tutorial_fragment.xml
@@ -13,7 +13,8 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.quickstep.interaction.RootSandboxLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:attr/colorBackground">
@@ -93,11 +94,11 @@
style="@style/TextAppearance.GestureTutorial.Feedback"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_above="@id/gesture_tutorial_fragment_action_button"
+ android:layout_below="@id/gesture_tutorial_fragment_titles_container"
android:layout_centerHorizontal="true"
android:layout_marginStart="@dimen/gesture_tutorial_feedback_margin_start_end"
android:layout_marginEnd="@dimen/gesture_tutorial_feedback_margin_start_end"
- android:layout_marginBottom="10dp"/>
+ android:layout_marginTop="40dp"/>
<!-- android:stateListAnimator="@null" removes shadow and normal on click behavior (increase
of elevation and shadow) which is replaced by ripple effect in android:foreground -->
@@ -126,4 +127,4 @@
android:background="@null"
android:foreground="?android:attr/selectableItemBackgroundBorderless"
android:stateListAnimator="@null"/>
-</RelativeLayout>
\ No newline at end of file
+</com.android.quickstep.interaction.RootSandboxLayout>
\ No newline at end of file
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 26ad377..22e6755 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -434,9 +434,13 @@
mOnDeferredActivityLaunch);
mGestureState.runOnceAtState(STATE_END_TARGET_SET,
- () -> mDeviceState.getRotationTouchHelper().
- onEndTargetCalculated(mGestureState.getEndTarget(),
- mActivityInterface));
+ () -> {
+ mDeviceState.getRotationTouchHelper()
+ .onEndTargetCalculated(mGestureState.getEndTarget(),
+ mActivityInterface);
+
+ mRecentsView.onGestureEndTargetCalculated(mGestureState.getEndTarget());
+ });
notifyGestureStartedAsync();
}
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index e4b05ae..2666869 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -114,17 +114,6 @@
}
@Override
- public void onSplitScreenInvoked() {
- if (mSystemUiProxy != null) {
- try {
- mSystemUiProxy.onSplitScreenInvoked();
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call onSplitScreenInvoked", e);
- }
- }
- }
-
- @Override
public void onOverviewShown(boolean fromHome) {
onOverviewShown(fromHome, TAG);
}
diff --git a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
index 3b245b3..2b55b80 100644
--- a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
@@ -228,7 +228,6 @@
@Override
protected boolean onActivityStarted(BaseDraggingActivity activity) {
- SystemUiProxy.INSTANCE.get(activity).onSplitScreenInvoked();
activity.getUserEventDispatcher().logActionOnControl(TAP,
LauncherLogProto.ControlType.SPLIT_SCREEN_TARGET);
return true;
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index ebdc867..4b5adcb 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -20,6 +20,7 @@
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_UP;
+import static com.android.launcher3.config.FeatureFlags.ASSISTANT_GIVES_LAUNCHER_FOCUS;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.quickstep.GestureState.DEFAULT_STATE;
@@ -704,8 +705,10 @@
if (activity.getRootView().hasWindowFocus()
|| previousGestureState.isRunningAnimationToLauncher()
- || (FeatureFlags.ASSISTANT_GIVES_LAUNCHER_FOCUS.get()
- && forceOverviewInputConsumer)) {
+ || (ASSISTANT_GIVES_LAUNCHER_FOCUS.get()
+ && forceOverviewInputConsumer)
+ || (ENABLE_QUICKSTEP_LIVE_TILE.get())
+ && gestureState.getActivityInterface().isInLiveTileMode()) {
return new OverviewInputConsumer(gestureState, activity, mInputMonitorCompat,
false /* startingInActivityBounds */);
} else {
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index f5f5259..8f2356c 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -15,6 +15,7 @@
*/
package com.android.quickstep.fallback;
+import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS;
import static com.android.quickstep.fallback.RecentsState.DEFAULT;
import static com.android.quickstep.fallback.RecentsState.MODAL_TASK;
@@ -27,6 +28,7 @@
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.statemanager.StateManager.StateListener;
import com.android.quickstep.FallbackActivityInterface;
+import com.android.quickstep.GestureState;
import com.android.quickstep.RecentsActivity;
import com.android.quickstep.views.OverviewActionsView;
import com.android.quickstep.views.RecentsView;
@@ -74,14 +76,14 @@
}
/**
- * When the gesture ends and recents view become interactive, we also remove the temporary
+ * When the gesture ends and we're going to recents view, we also remove the temporary
* invisible tile added for the home task. This also pushes the remaining tiles back
* to the center.
*/
@Override
- public void onGestureAnimationEnd() {
- super.onGestureAnimationEnd();
- if (mHomeTaskInfo != null) {
+ public void onGestureEndTargetCalculated(GestureState.GestureEndTarget endTarget) {
+ super.onGestureEndTargetCalculated(endTarget);
+ if (mHomeTaskInfo != null && endTarget == RECENTS) {
TaskView tv = getTaskView(mHomeTaskInfo.taskId);
if (tv != null) {
PendingAnimation pa = createTaskDismissAnimation(tv, true, false, 150);
diff --git a/quickstep/src/com/android/quickstep/interaction/EdgeBackGestureHandler.java b/quickstep/src/com/android/quickstep/interaction/EdgeBackGestureHandler.java
index e4b348e..9489bac 100644
--- a/quickstep/src/com/android/quickstep/interaction/EdgeBackGestureHandler.java
+++ b/quickstep/src/com/android/quickstep/interaction/EdgeBackGestureHandler.java
@@ -143,6 +143,10 @@
return false;
}
+ boolean onInterceptTouch(MotionEvent motionEvent) {
+ return isWithinTouchRegion((int) motionEvent.getX(), (int) motionEvent.getY());
+ }
+
private boolean isWithinTouchRegion(int x, int y) {
// Disallow if too far from the edge
if (x > mEdgeWidth + mLeftInset && x < (mDisplaySize.x - mEdgeWidth - mRightInset)) {
diff --git a/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java b/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
index f897ecc..d1b0a70 100644
--- a/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
+++ b/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
@@ -250,6 +250,12 @@
return intercepted;
}
+ boolean onInterceptTouch(MotionEvent event) {
+ return mAssistantLeftRegion.contains(event.getX(), event.getY())
+ || mAssistantRightRegion.contains(event.getX(), event.getY())
+ || event.getY() >= mDisplaySize.y - mBottomGestureHeight;
+ }
+
protected void onMotionPauseDetected() {
VibratorWrapper.INSTANCE.get(mContext).vibrate(OVERVIEW_HAPTIC);
}
diff --git a/quickstep/src/com/android/quickstep/interaction/RootSandboxLayout.java b/quickstep/src/com/android/quickstep/interaction/RootSandboxLayout.java
new file mode 100644
index 0000000..db1afc2
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/interaction/RootSandboxLayout.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2020 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.interaction;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.widget.RelativeLayout;
+
+import androidx.fragment.app.FragmentManager;
+
+/** Root layout that TutorialFragment uses to intercept motion events. */
+public class RootSandboxLayout extends RelativeLayout {
+ public RootSandboxLayout(Context context) {
+ super(context);
+ }
+
+ public RootSandboxLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public RootSandboxLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent motionEvent) {
+ return ((TutorialFragment) FragmentManager.findFragment(this))
+ .onInterceptTouch(motionEvent);
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/interaction/SandboxLauncherRenderer.java b/quickstep/src/com/android/quickstep/interaction/SandboxLauncherRenderer.java
new file mode 100644
index 0000000..80ffe66
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/interaction/SandboxLauncherRenderer.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2020 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.interaction;
+
+import android.content.Context;
+import android.view.View;
+
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.graphics.LauncherPreviewRenderer;
+
+/** Renders a fake Launcher for use in the Sandbox. */
+class SandboxLauncherRenderer extends LauncherPreviewRenderer {
+ SandboxLauncherRenderer(Context context, InvariantDeviceProfile idp, boolean migrated) {
+ super(context, idp, migrated);
+ }
+
+ @Override
+ public boolean shouldShowRealLauncherPreview() {
+ return false;
+ }
+
+ @Override
+ public boolean shouldShowQsb() {
+ return false;
+ }
+
+ @Override
+ public View.OnLongClickListener getWorkspaceChildOnLongClickListener() {
+ return null;
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialController.java b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
index 73f1f8c..db80342 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
@@ -47,6 +47,7 @@
final TextView mTitleTextView;
final TextView mSubtitleTextView;
final TextView mFeedbackView;
+ final View mLauncherView;
final ClipIconView mFakeIconView;
final View mFakeTaskView;
final View mRippleView;
@@ -68,6 +69,7 @@
mTitleTextView = rootView.findViewById(R.id.gesture_tutorial_fragment_title_view);
mSubtitleTextView = rootView.findViewById(R.id.gesture_tutorial_fragment_subtitle_view);
mFeedbackView = rootView.findViewById(R.id.gesture_tutorial_fragment_feedback_view);
+ mLauncherView = tutorialFragment.getLauncherView();
mFakeIconView = rootView.findViewById(R.id.gesture_tutorial_fake_icon_view);
mFakeTaskView = rootView.findViewById(R.id.gesture_tutorial_fake_task_view);
mRippleView = rootView.findViewById(R.id.gesture_tutorial_ripple_view);
@@ -162,8 +164,10 @@
if (isComplete()) {
hideHandCoachingAnimation();
+ mLauncherView.setVisibility(View.INVISIBLE);
} else {
showHandCoachingAnimation();
+ mLauncherView.setVisibility(View.VISIBLE);
}
}
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
index 9a8264d..c90ad94 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
@@ -31,6 +31,7 @@
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
+import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.R;
import com.android.quickstep.interaction.TutorialController.TutorialType;
@@ -45,6 +46,7 @@
TutorialHandAnimation mHandCoachingAnimation;
EdgeBackGestureHandler mEdgeBackGestureHandler;
NavBarGestureHandler mNavBarGestureHandler;
+ private View mLauncherView;
public static TutorialFragment newInstance(TutorialType tutorialType) {
TutorialFragment fragment = getFragmentForTutorialType(tutorialType);
@@ -114,8 +116,11 @@
return insets;
});
mRootView.setOnTouchListener(this);
- mHandCoachingAnimation = new TutorialHandAnimation(getContext(), mRootView,
- getHandAnimationResId());
+ mHandCoachingAnimation =
+ new TutorialHandAnimation(getContext(), mRootView, getHandAnimationResId());
+ InvariantDeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(getContext());
+ mLauncherView = new SandboxLauncherRenderer(getContext(), dp, true).getRenderedView();
+ ((ViewGroup) mRootView).addView(mLauncherView, 0);
return mRootView;
}
@@ -133,11 +138,17 @@
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
- // Note: Using logical or to ensure both functions get called.
+ // Note: Using logical-or to ensure both functions get called.
return mEdgeBackGestureHandler.onTouch(view, motionEvent)
| mNavBarGestureHandler.onTouch(view, motionEvent);
}
+ boolean onInterceptTouch(MotionEvent motionEvent) {
+ // Note: Using logical-or to ensure both functions get called.
+ return mEdgeBackGestureHandler.onInterceptTouch(motionEvent)
+ | mNavBarGestureHandler.onInterceptTouch(motionEvent);
+ }
+
void onAttachedToWindow() {
mEdgeBackGestureHandler.setViewGroupParent((ViewGroup) getRootView());
}
@@ -168,6 +179,10 @@
return mRootView;
}
+ View getLauncherView() {
+ return mLauncherView;
+ }
+
TutorialHandAnimation getHandAnimation() {
return mHandCoachingAnimation;
}
diff --git a/quickstep/src/com/android/quickstep/util/OverviewToHomeAnim.java b/quickstep/src/com/android/quickstep/util/OverviewToHomeAnim.java
index 4a298d3..1031c5b 100644
--- a/quickstep/src/com/android/quickstep/util/OverviewToHomeAnim.java
+++ b/quickstep/src/com/android/quickstep/util/OverviewToHomeAnim.java
@@ -18,8 +18,10 @@
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.anim.Interpolators.DEACCEL;
+import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
import static com.android.launcher3.anim.Interpolators.INSTANT;
+import static com.android.launcher3.anim.Interpolators.clampToProgress;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_COMPONENTS;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_ACTIONS_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE;
@@ -110,7 +112,7 @@
boolean isLayoutNaturalToLauncher = recentsView.getPagedOrientationHandler()
.isLayoutNaturalToLauncher();
config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, isLayoutNaturalToLauncher
- ? DEACCEL : FINAL_FRAME);
+ ? clampToProgress(FAST_OUT_SLOW_IN, 0, 0.75f) : FINAL_FRAME);
config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, FINAL_FRAME);
config.setInterpolator(ANIM_OVERVIEW_SCALE, FINAL_FRAME);
config.setInterpolator(ANIM_OVERVIEW_ACTIONS_FADE, INSTANT);
diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
index 903e87a..3a54bd6 100644
--- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -278,7 +278,6 @@
int start = mOrientationState.getOrientationHandler()
.getPrimaryValue(mTaskRect.left, mTaskRect.top);
mScrollState.screenCenter = start + mScrollState.scroll + mScrollState.halfPageSize;
- mScrollState.pageParentScale = recentsViewScale.value;
mScrollState.updateInterpolation(start);
mCurveScale = TaskView.getCurveScaleForInterpolation(mScrollState.linearInterpolation);
}
@@ -297,9 +296,9 @@
mMatrix.postTranslate(insets.left, insets.top);
mMatrix.postScale(scale, scale);
- // Apply TaskView matrix: translate, scale, scroll
- mMatrix.postTranslate(mTaskRect.left, mTaskRect.top + mOffsetY);
+ // Apply TaskView matrix: scale, translate, scroll
mMatrix.postScale(mCurveScale, mCurveScale, taskWidth / 2, taskHeight / 2);
+ mMatrix.postTranslate(mTaskRect.left, mTaskRect.top + mOffsetY);
mOrientationState.getOrientationHandler().set(
mMatrix, MATRIX_POST_TRANSLATE, mScrollState.scroll);
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 1ae011b..0d1dd22 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -117,6 +117,7 @@
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.ViewPool;
import com.android.quickstep.BaseActivityInterface;
+import com.android.quickstep.GestureState;
import com.android.quickstep.RecentsAnimationController;
import com.android.quickstep.RecentsAnimationTargets;
import com.android.quickstep.RecentsModel;
@@ -1181,7 +1182,14 @@
}
/**
- * Called when a gesture from an app has finished.
+ * Called when a gesture from an app has finished, and an end target has been determined.
+ */
+ public void onGestureEndTargetCalculated(GestureState.GestureEndTarget endTarget) {
+
+ }
+
+ /**
+ * Called when a gesture from an app has finished, and the animation to the target has ended.
*/
public void onGestureAnimationEnd() {
if (mOrientationState.setGestureActive(false)) {
@@ -1404,13 +1412,12 @@
* Updates linearInterpolation for the provided child position
*/
public void updateInterpolation(float childStart) {
- float scaledHalfPageSize = halfPageSize / pageParentScale;
- float pageCenter = childStart + scaledHalfPageSize;
+ float pageCenter = childStart + halfPageSize;
float distanceFromScreenCenter = screenCenter - pageCenter;
// How far the page has to move from the center to be offscreen, taking into account
// the EDGE_SCALE_DOWN_FACTOR that will be applied at that position.
float distanceToReachEdge = halfScreenSize
- + scaledHalfPageSize * (1 - TaskView.EDGE_SCALE_DOWN_FACTOR);
+ + halfPageSize * (1 - TaskView.EDGE_SCALE_DOWN_FACTOR);
linearInterpolation = Math.min(1,
Math.abs(distanceFromScreenCenter) / distanceToReachEdge);
}
diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuView.java b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
index d47eba6..4aff7e3 100644
--- a/quickstep/src/com/android/quickstep/views/TaskMenuView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
@@ -16,6 +16,7 @@
package com.android.quickstep.views;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.quickstep.views.TaskThumbnailView.DIM_ALPHA;
import android.animation.Animator;
@@ -229,7 +230,16 @@
menuOptionView.findViewById(R.id.icon), menuOptionView.findViewById(R.id.text));
LayoutParams lp = (LayoutParams) menuOptionView.getLayoutParams();
mTaskView.getPagedOrientationHandler().setLayoutParamsForTaskMenuOptionItem(lp);
- menuOptionView.setOnClickListener(menuOption);
+ menuOptionView.setOnClickListener(view -> {
+ if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ RecentsView recentsView = mTaskView.getRecentsView();
+ recentsView.switchToScreenshot(null,
+ () -> recentsView.finishRecentsAnimation(true /* toRecents */,
+ () -> menuOption.onClick(view)));
+ } else {
+ menuOption.onClick(view);
+ }
+ });
mOptionLayout.addView(menuOptionView);
}
diff --git a/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java b/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java
index ccfa3fc..2a7da7e 100644
--- a/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java
+++ b/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java
@@ -3,6 +3,8 @@
import static androidx.test.InstrumentationRegistry.getInstrumentation;
import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT;
+import static com.android.launcher3.util.rule.TestStabilityRule.UNBUNDLED_POSTSUBMIT;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -11,12 +13,12 @@
import android.app.PendingIntent;
import android.app.usage.UsageStatsManager;
import android.content.Intent;
-import android.os.Build;
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.launcher3.Launcher;
+import com.android.launcher3.util.rule.TestStabilityRule;
import com.android.quickstep.views.DigitalWellBeingToast;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
@@ -34,9 +36,6 @@
@Test
public void testToast() throws Exception {
- // b/150303529
- if (Build.MODEL.contains("Cuttlefish")) return;
-
startAppFast(CALCULATOR_PACKAGE);
final UsageStatsManager usageStatsManager =
diff --git a/res/layout/search_result_icon_row.xml b/res/layout/search_result_icon_row.xml
new file mode 100644
index 0000000..5ecc0c2
--- /dev/null
+++ b/res/layout/search_result_icon_row.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2020 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.views.SearchResultIconRow xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:launcher="http://schemas.android.com/apk/res-auto"
+ style="@style/BaseIcon"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:drawablePadding="@dimen/dynamic_grid_icon_drawable_padding"
+ android:gravity="start|center_vertical"
+ android:textAlignment="viewStart"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="16sp"
+ android:padding="@dimen/dynamic_grid_edge_margin"
+ launcher:iconDisplay="hero_app"
+ launcher:layoutHorizontal="true"
+ >
+
+</com.android.launcher3.views.SearchResultIconRow>
\ No newline at end of file
diff --git a/res/layout/search_result_shortcut.xml b/res/layout/search_result_shortcut.xml
deleted file mode 100644
index c350c97..0000000
--- a/res/layout/search_result_shortcut.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2020 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.views.SearchResultShortcut xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:launcher="http://schemas.android.com/apk/res-auto"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:gravity="center_vertical"
- android:padding="@dimen/dynamic_grid_edge_margin">
-
- <com.android.launcher3.BubbleTextView
- android:id="@+id/bubble_text"
- style="@style/BaseIcon"
- android:drawablePadding="@dimen/dynamic_grid_icon_drawable_padding"
- android:gravity="start|center_vertical"
- android:textAlignment="viewStart"
- android:textColor="?android:attr/textColorPrimary"
- android:textSize="16sp"
- android:layout_height="wrap_content"
- launcher:iconDisplay="hero_app"
- launcher:layoutHorizontal="true" />
-
- <View
- android:id="@+id/icon"
- android:layout_width="@dimen/deep_shortcut_icon_size"
- android:layout_height="@dimen/deep_shortcut_icon_size"
- android:layout_gravity="start|center_vertical"
- android:background="@drawable/ic_deepshortcut_placeholder" />
-
-</com.android.launcher3.views.SearchResultShortcut>
\ No newline at end of file
diff --git a/res/layout/search_result_thumbnail.xml b/res/layout/search_result_thumbnail.xml
new file mode 100644
index 0000000..0cc5a29
--- /dev/null
+++ b/res/layout/search_result_thumbnail.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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.views.ThumbnailSearchResultView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="125dp"
+ android:layout_height="125dp"/>
\ No newline at end of file
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 06bb263..1e5a9e4 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -23,6 +23,7 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
@@ -30,6 +31,8 @@
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PointF;
+import android.graphics.PorterDuff.Mode;
+import android.graphics.PorterDuffColorFilter;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
@@ -43,6 +46,8 @@
import android.view.ViewDebug;
import android.widget.TextView;
+import androidx.core.graphics.ColorUtils;
+
import com.android.launcher3.Launcher.OnResumeCallback;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.dot.DotInfo;
@@ -50,6 +55,7 @@
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.graphics.IconPalette;
import com.android.launcher3.graphics.IconShape;
+import com.android.launcher3.graphics.PlaceHolderIconDrawable;
import com.android.launcher3.graphics.PreloadIconDrawable;
import com.android.launcher3.icons.DotRenderer;
import com.android.launcher3.icons.IconCache.IconLoadRequest;
@@ -59,6 +65,7 @@
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.model.data.PackageItemInfo;
import com.android.launcher3.model.data.PromiseAppInfo;
+import com.android.launcher3.model.data.RemoteActionItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.util.SafeCloseable;
import com.android.launcher3.views.ActivityContext;
@@ -84,6 +91,8 @@
private final PointF mTranslationForReorderBounce = new PointF(0, 0);
private final PointF mTranslationForReorderPreview = new PointF(0, 0);
+ private static final int ICON_UPDATE_ANIMATION_DURATION = 375;
+
private float mScaleForReorderBounce = 1f;
private static final Property<BubbleTextView, Float> DOT_SCALE_PROPERTY
@@ -292,6 +301,14 @@
verifyHighRes();
}
+ /**
+ * Apply label and tag using a {@link RemoteActionItemInfo}
+ */
+ public void applyFromRemoteActionInfo(RemoteActionItemInfo remoteActionItemInfo) {
+ applyIconAndLabel(remoteActionItemInfo);
+ setTag(remoteActionItemInfo);
+ }
+
private void applyIconAndLabel(ItemInfoWithIcon info) {
FastBitmapDrawable iconDrawable = newIcon(getContext(), info);
mDotParams.color = IconPalette.getMutedColor(info.bitmap.color, 0.54f);
@@ -636,11 +653,14 @@
mDisableRelayout = mIcon != null;
icon.setBounds(0, 0, mIconSize, mIconSize);
- if (mLayoutHorizontal) {
- setCompoundDrawablesRelative(icon, null, null, null);
- } else {
- setCompoundDrawables(null, icon, null, null);
+
+ updateIcon(icon);
+
+ // If the current icon is a placeholder color, animate its update.
+ if (mIcon != null && mIcon instanceof PlaceHolderIconDrawable) {
+ animateIconUpdate((PlaceHolderIconDrawable) mIcon, icon);
}
+
mDisableRelayout = false;
}
@@ -670,6 +690,8 @@
mActivity.invalidateParent(info);
} else if (info instanceof PackageItemInfo) {
applyFromItemInfoWithIcon((PackageItemInfo) info);
+ } else if (info instanceof RemoteActionItemInfo) {
+ applyFromRemoteActionInfo((RemoteActionItemInfo) info);
}
mDisableRelayout = false;
@@ -776,4 +798,33 @@
((FastBitmapDrawable) mIcon).setScale(1f);
}
}
+
+ private void updateIcon(Drawable newIcon) {
+ if (mLayoutHorizontal) {
+ setCompoundDrawablesRelative(newIcon, null, null, null);
+ } else {
+ setCompoundDrawables(null, newIcon, null, null);
+ }
+ }
+
+ private static void animateIconUpdate(PlaceHolderIconDrawable oldIcon, Drawable newIcon) {
+ int placeholderColor = oldIcon.mPaint.getColor();
+ int originalAlpha = Color.alpha(placeholderColor);
+
+ ValueAnimator iconUpdateAnimation = ValueAnimator.ofInt(originalAlpha, 0);
+ iconUpdateAnimation.setDuration(ICON_UPDATE_ANIMATION_DURATION);
+ iconUpdateAnimation.addUpdateListener(valueAnimator -> {
+ int newAlpha = (int) valueAnimator.getAnimatedValue();
+ int newColor = ColorUtils.setAlphaComponent(placeholderColor, newAlpha);
+
+ newIcon.setColorFilter(new PorterDuffColorFilter(newColor, Mode.SRC_ATOP));
+ });
+ iconUpdateAnimation.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ newIcon.setColorFilter(null);
+ }
+ });
+ iconUpdateAnimation.start();
+ }
}
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 1cd201f..2809bd5 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -323,11 +323,8 @@
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
- if (mTouchHelper != null
- || (mInterceptTouchListener != null && mInterceptTouchListener.onTouch(this, ev))) {
- return true;
- }
- return false;
+ return mTouchHelper != null
+ || (mInterceptTouchListener != null && mInterceptTouchListener.onTouch(this, ev));
}
public void enableHardwareLayer(boolean hasLayer) {
diff --git a/src/com/android/launcher3/FastBitmapDrawable.java b/src/com/android/launcher3/FastBitmapDrawable.java
index d3b86de..139d4a8 100644
--- a/src/com/android/launcher3/FastBitmapDrawable.java
+++ b/src/com/android/launcher3/FastBitmapDrawable.java
@@ -33,6 +33,8 @@
import android.graphics.drawable.Drawable;
import android.util.Property;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.graphics.PlaceHolderIconDrawable;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
@@ -54,6 +56,8 @@
protected Bitmap mBitmap;
protected final int mIconColor;
+ @Nullable private ColorFilter mColorFilter;
+
private boolean mIsPressed;
private boolean mIsDisabled;
private float mDisabledAlpha = 1f;
@@ -115,7 +119,8 @@
@Override
public void setColorFilter(ColorFilter cf) {
- // No op
+ mColorFilter = cf;
+ updateFilter();
}
@Override
@@ -265,7 +270,7 @@
* Updates the paint to reflect the current brightness and saturation.
*/
protected void updateFilter() {
- mPaint.setColorFilter(mIsDisabled ? getDisabledColorFilter() : null);
+ mPaint.setColorFilter(mIsDisabled ? getDisabledColorFilter() : mColorFilter);
invalidateSelf();
}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 90566f3..1cf2e89 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -18,6 +18,7 @@
import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
+import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO;
import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
@@ -70,6 +71,7 @@
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.database.sqlite.SQLiteDatabase;
+import android.graphics.Bitmap;
import android.os.Build;
import android.os.Bundle;
import android.os.CancellationSignal;
@@ -88,9 +90,10 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
-import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams;
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.OvershootInterpolator;
+import android.widget.ImageView;
import android.widget.Toast;
import androidx.annotation.CallSuper;
@@ -117,6 +120,7 @@
import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.folder.FolderGridOrganizer;
import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.icons.BitmapRenderer;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.keyboard.CustomActionsPopup;
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
@@ -269,6 +273,8 @@
private static final int APPS_VIEW_ALPHA_CHANNEL_INDEX = 1;
private static final int SCRIM_VIEW_ALPHA_CHANNEL_INDEX = 0;
+ private static final int THEME_CROSS_FADE_ANIMATION_DURATION = 375;
+
private LauncherAppTransitionManager mAppTransitionManager;
private Configuration mOldConfig;
@@ -400,6 +406,7 @@
inflateRootView(R.layout.launcher);
setupViews();
+ crossFadeWithPreviousAppearance();
mPopupDataProvider = new PopupDataProvider(this::updateNotificationDots);
mAppTransitionManager = LauncherAppTransitionManager.newInstance(this);
@@ -476,7 +483,7 @@
() -> getStateManager().goToState(NORMAL));
if (Utilities.ATLEAST_R) {
- getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
+ getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
}
mLifecycleRegistry = new LifecycleRegistry(this);
@@ -1364,6 +1371,18 @@
closeContextMenu();
}
+ @Override
+ public Object onRetainNonConfigurationInstance() {
+ int width = mDragLayer.getWidth();
+ int height = mDragLayer.getHeight();
+
+ if (width <= 0 || height <= 0) {
+ return null;
+ }
+
+ return BitmapRenderer.createHardwareBitmap(width, height, mDragLayer::draw);
+ }
+
public AllAppsTransitionController getAllAppsController() {
return mAllAppsController;
}
@@ -2758,4 +2777,40 @@
void onLauncherResume();
}
+
+ /**
+ * Cross-fades the launcher's updated appearance with its previous appearance.
+ *
+ * This method is used to cross-fade UI updates on activity creation, specifically dark mode
+ * updates.
+ */
+ private void crossFadeWithPreviousAppearance() {
+ Bitmap previousAppearanceBitmap = (Bitmap) getLastNonConfigurationInstance();
+
+ if (previousAppearanceBitmap == null) {
+ return;
+ }
+
+ ImageView crossFadeHelper = new ImageView(this);
+
+ crossFadeHelper.setImageBitmap(previousAppearanceBitmap);
+ crossFadeHelper.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
+
+ InsettableFrameLayout.LayoutParams layoutParams = new InsettableFrameLayout.LayoutParams(
+ InsettableFrameLayout.LayoutParams.MATCH_PARENT,
+ InsettableFrameLayout.LayoutParams.MATCH_PARENT);
+
+ layoutParams.ignoreInsets = true;
+
+ crossFadeHelper.setLayoutParams(layoutParams);
+
+ getRootView().addView(crossFadeHelper);
+
+ crossFadeHelper
+ .animate()
+ .setDuration(THEME_CROSS_FADE_ANIMATION_DURATION)
+ .alpha(0f)
+ .withEndAction(() -> getRootView().removeView(crossFadeHelper))
+ .start();
+ }
}
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index bfe327e..a4181c5 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -105,7 +105,7 @@
new Handler().post( () -> mInvariantDeviceProfile.verifyConfigChangedInBackground(context));
mInstallSessionTracker = InstallSessionHelper.INSTANCE.get(context)
- .registerInstallTracker(mModel, MODEL_EXECUTOR);
+ .registerInstallTracker(mModel);
// Register an observer to rebind the notification listener when dots are re-enabled.
mNotificationDotsObserver =
diff --git a/src/com/android/launcher3/SessionCommitReceiver.java b/src/com/android/launcher3/SessionCommitReceiver.java
index 007e5f5..1bbbb2b 100644
--- a/src/com/android/launcher3/SessionCommitReceiver.java
+++ b/src/com/android/launcher3/SessionCommitReceiver.java
@@ -25,8 +25,11 @@
import android.os.UserHandle;
import android.text.TextUtils;
+import androidx.annotation.WorkerThread;
+
import com.android.launcher3.model.ItemInstallQueue;
import com.android.launcher3.pm.InstallSessionHelper;
+import com.android.launcher3.util.Executors;
/**
* BroadcastReceiver to handle session commit intent.
@@ -38,6 +41,11 @@
@Override
public void onReceive(Context context, Intent intent) {
+ Executors.MODEL_EXECUTOR.execute(() -> processIntent(context, intent));
+ }
+
+ @WorkerThread
+ private static void processIntent(Context context, Intent intent) {
if (!isEnabled(context)) {
// User has decided to not add icons on homescreen.
return;
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index dea2a8d..43ccb79 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -133,6 +133,10 @@
public static final String EXTRA_WALLPAPER_OFFSET = "com.android.launcher3.WALLPAPER_OFFSET";
public static final String EXTRA_WALLPAPER_FLAVOR = "com.android.launcher3.WALLPAPER_FLAVOR";
+ // An intent extra to indicate the launch source by launcher.
+ public static final String EXTRA_WALLPAPER_LAUNCH_SOURCE =
+ "com.android.wallpaper.LAUNCH_SOURCE";
+
public static boolean IS_RUNNING_IN_TEST_HARNESS =
ActivityManager.isRunningInTestHarness();
diff --git a/src/com/android/launcher3/WorkspaceLayoutManager.java b/src/com/android/launcher3/WorkspaceLayoutManager.java
index ea887cc..d6302ce 100644
--- a/src/com/android/launcher3/WorkspaceLayoutManager.java
+++ b/src/com/android/launcher3/WorkspaceLayoutManager.java
@@ -130,12 +130,16 @@
}
child.setHapticFeedbackEnabled(false);
- child.setOnLongClickListener(ItemLongClickListener.INSTANCE_WORKSPACE);
+ child.setOnLongClickListener(getWorkspaceChildOnLongClickListener());
if (child instanceof DropTarget) {
onAddDropTarget((DropTarget) child);
}
}
+ default View.OnLongClickListener getWorkspaceChildOnLongClickListener() {
+ return ItemLongClickListener.INSTANCE_WORKSPACE;
+ }
+
Hotseat getHotseat();
CellLayout getScreenWithId(int screenId);
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index d1340fa..81d94ba 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -92,10 +92,12 @@
public static final int VIEW_TYPE_SEARCH_SLICE = 1 << 9;
- public static final int VIEW_TYPE_SEARCH_SHORTCUT = 1 << 10;
+ public static final int VIEW_TYPE_SEARCH_ICON_ROW = 1 << 10;
public static final int VIEW_TYPE_SEARCH_PEOPLE = 1 << 11;
+ public static final int VIEW_TYPE_SEARCH_THUMBNAIL = 1 << 12;
+
// Common view type masks
public static final int VIEW_TYPE_MASK_DIVIDER = VIEW_TYPE_ALL_APPS_DIVIDER;
public static final int VIEW_TYPE_MASK_ICON = VIEW_TYPE_ICON;
@@ -186,7 +188,8 @@
|| viewType == VIEW_TYPE_SEARCH_SLICE
|| viewType == VIEW_TYPE_SEARCH_ROW
|| viewType == VIEW_TYPE_SEARCH_PEOPLE
- || viewType == VIEW_TYPE_SEARCH_SHORTCUT;
+ || viewType == VIEW_TYPE_SEARCH_THUMBNAIL
+ || viewType == VIEW_TYPE_SEARCH_ICON_ROW;
}
}
@@ -197,6 +200,7 @@
*/
public static class AdapterItemWithPayload<T> extends AdapterItem {
private T mPayload;
+ private String mSearchSessionId;
private AllAppsSearchPlugin mPlugin;
private IntConsumer mSelectionHandler;
@@ -218,6 +222,14 @@
mSelectionHandler = runnable;
}
+ public void setSearchSessionId(String searchSessionId) {
+ mSearchSessionId = searchSessionId;
+ }
+
+ public String getSearchSessionId() {
+ return mSearchSessionId;
+ }
+
public IntConsumer getSelectionHandler() {
return mSelectionHandler;
}
@@ -225,6 +237,8 @@
public T getPayload() {
return mPayload;
}
+
+
}
/**
@@ -307,15 +321,21 @@
@Override
public int getSpanSize(int position) {
- if (isIconViewType(mApps.getAdapterItems().get(position).viewType)) {
- return 1;
+ int viewType = mApps.getAdapterItems().get(position).viewType;
+ if (isIconViewType(viewType)) {
+ return 1 * SPAN_MULTIPLIER;
+ } else if (viewType == VIEW_TYPE_SEARCH_THUMBNAIL) {
+ return mAppsPerRow;
} else {
// Section breaks span the full width
- return mAppsPerRow;
+ return mAppsPerRow * SPAN_MULTIPLIER;
}
}
}
+ // multiplier to support adapter item column count that is not mAppsPerRow.
+ public static final int SPAN_MULTIPLIER = 3;
+
private final BaseDraggingActivity mLauncher;
private final LayoutInflater mLayoutInflater;
private final AlphabeticalAppsList mApps;
@@ -352,7 +372,7 @@
public void setAppsPerRow(int appsPerRow) {
mAppsPerRow = appsPerRow;
- mGridLayoutMgr.setSpanCount(mAppsPerRow);
+ mGridLayoutMgr.setSpanCount(mAppsPerRow * SPAN_MULTIPLIER);
}
/**
@@ -438,12 +458,15 @@
case VIEW_TYPE_SEARCH_SLICE:
return new ViewHolder(mLayoutInflater.inflate(
R.layout.search_result_slice, parent, false));
- case VIEW_TYPE_SEARCH_SHORTCUT:
+ case VIEW_TYPE_SEARCH_ICON_ROW:
return new ViewHolder(mLayoutInflater.inflate(
- R.layout.search_result_shortcut, parent, false));
+ R.layout.search_result_icon_row, parent, false));
case VIEW_TYPE_SEARCH_PEOPLE:
return new ViewHolder(mLayoutInflater.inflate(
R.layout.search_result_people_item, parent, false));
+ case VIEW_TYPE_SEARCH_THUMBNAIL:
+ return new ViewHolder(mLayoutInflater.inflate(
+ R.layout.search_result_thumbnail, parent, false));
default:
throw new RuntimeException("Unexpected view type");
}
@@ -464,24 +487,27 @@
//TODO: replace with custom TopHitBubbleTextView with support for both shortcut
// and apps
if (adapterItem instanceof AdapterItemWithPayload) {
- AdapterItemWithPayload withPayload = (AdapterItemWithPayload) adapterItem;
- IntConsumer selectionHandler = type -> {
+ AdapterItemWithPayload item = (AdapterItemWithPayload) adapterItem;
+ item.setSelectionHandler(type -> {
SearchTargetEvent e = new SearchTargetEvent(SearchTarget.ItemType.APP,
- type);
+ type, item.position, item.getSearchSessionId());
e.bundle = HeroSearchResultView.getAppBundle(info);
- if (withPayload.getPlugin() != null) {
- withPayload.getPlugin().notifySearchTargetEvent(e);
+ if (item.getPlugin() != null) {
+ item.getPlugin().notifySearchTargetEvent(e);
}
- };
+ });
icon.setOnClickListener(view -> {
- selectionHandler.accept(SearchTargetEvent.SELECT);
+ item.getSelectionHandler().accept(SearchTargetEvent.SELECT);
mOnIconClickListener.onClick(view);
});
icon.setOnLongClickListener(view -> {
- selectionHandler.accept(SearchTargetEvent.LONG_PRESS);
+ item.getSelectionHandler().accept(SearchTargetEvent.SELECT);
return mOnIconLongClickListener.onLongClick(view);
});
- withPayload.setSelectionHandler(selectionHandler);
+ }
+ else {
+ icon.setOnClickListener(mOnIconClickListener);
+ icon.setOnLongClickListener(mOnIconLongClickListener);
}
break;
case VIEW_TYPE_EMPTY_SEARCH:
@@ -500,20 +526,22 @@
break;
case VIEW_TYPE_SEARCH_SLICE:
SliceView sliceView = (SliceView) holder.itemView;
- AdapterItemWithPayload<Uri> item =
+ AdapterItemWithPayload<Uri> slicePayload =
(AdapterItemWithPayload<Uri>) mApps.getAdapterItems().get(position);
sliceView.setOnSliceActionListener((info1, s) -> {
- if (item.getPlugin() != null) {
+ if (slicePayload.getPlugin() != null) {
SearchTargetEvent searchTargetEvent = new SearchTargetEvent(
SearchTarget.ItemType.SETTINGS_SLICE,
- SearchTargetEvent.CHILD_SELECT);
+ SearchTargetEvent.CHILD_SELECT, slicePayload.position,
+ slicePayload.getSearchSessionId());
searchTargetEvent.bundle = new Bundle();
- searchTargetEvent.bundle.putParcelable("uri", item.getPayload());
- item.getPlugin().notifySearchTargetEvent(searchTargetEvent);
+ searchTargetEvent.bundle.putParcelable("uri", slicePayload.getPayload());
+ slicePayload.getPlugin().notifySearchTargetEvent(searchTargetEvent);
}
});
try {
- LiveData<Slice> liveData = SliceLiveData.fromUri(mLauncher, item.getPayload());
+ LiveData<Slice> liveData = SliceLiveData.fromUri(mLauncher,
+ slicePayload.getPayload());
liveData.observe((Launcher) mLauncher, sliceView);
sliceView.setTag(liveData);
} catch (Exception ignored) {
@@ -523,11 +551,13 @@
case VIEW_TYPE_SEARCH_ROW_WITH_BUTTON:
case VIEW_TYPE_SEARCH_HERO_APP:
case VIEW_TYPE_SEARCH_ROW:
- case VIEW_TYPE_SEARCH_SHORTCUT:
+ case VIEW_TYPE_SEARCH_ICON_ROW:
case VIEW_TYPE_SEARCH_PEOPLE:
+ case VIEW_TYPE_SEARCH_THUMBNAIL:
+ AdapterItemWithPayload item =
+ (AdapterItemWithPayload) mApps.getAdapterItems().get(position);
PayloadResultHandler payloadResultView = (PayloadResultHandler) holder.itemView;
- payloadResultView.applyAdapterInfo(
- (AdapterItemWithPayload) mApps.getAdapterItems().get(position));
+ payloadResultView.setup(item);
break;
case VIEW_TYPE_ALL_APPS_DIVIDER:
// nothing to do
@@ -541,8 +571,8 @@
if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get()) return;
if (holder.itemView instanceof BubbleTextView) {
BubbleTextView icon = (BubbleTextView) holder.itemView;
- icon.setOnClickListener(mOnIconClickListener);
- icon.setOnLongClickListener(mOnIconLongClickListener);
+ icon.setOnClickListener(null);
+ icon.setOnLongClickListener(null);
} else if (holder.itemView instanceof SliceView) {
SliceView sliceView = (SliceView) holder.itemView;
sliceView.setOnSliceActionListener(null);
@@ -553,7 +583,6 @@
}
}
-
@Override
public boolean onFailedToRecycleView(ViewHolder holder) {
// Always recycle and we will reset the view when it is bound
diff --git a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
index 3320189..d7fa5bc 100644
--- a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
+++ b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
@@ -35,6 +35,8 @@
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.systemui.plugins.AllAppsSearchPlugin;
+import com.android.systemui.plugins.shared.SearchTarget;
+import com.android.systemui.plugins.shared.SearchTargetEvent;
import java.util.ArrayList;
import java.util.List;
@@ -213,6 +215,44 @@
/**
* Updates View using Adapter's payload
*/
+
+ default void setup(AdapterItemWithPayload<T> adapterItemWithPayload) {
+ Object[] targetInfo = getTargetInfo();
+ if (targetInfo != null) {
+ targetInfo[0] = adapterItemWithPayload.getSearchSessionId();
+ targetInfo[1] = adapterItemWithPayload.position;
+ }
+ applyAdapterInfo(adapterItemWithPayload);
+ }
+
void applyAdapterInfo(AdapterItemWithPayload<T> adapterItemWithPayload);
+
+ /**
+ * Gets object created by {@link PayloadResultHandler#createTargetInfo()}
+ */
+ Object[] getTargetInfo();
+
+ /**
+ * Creates a wrapper object to hold searchSessionId and item position
+ */
+ default Object[] createTargetInfo() {
+ return new Object[2];
+ }
+
+ /**
+ * Generates a SearchTargetEvent object for a PayloadHandlerView
+ */
+ default SearchTargetEvent getSearchTargetEvent(SearchTarget.ItemType itemType,
+ int eventType) {
+ Object[] targetInfo = getTargetInfo();
+ if (targetInfo == null) return null;
+
+ String searchSessionId = (String) targetInfo[0];
+ int position = (int) targetInfo[1];
+ return new SearchTargetEvent(itemType, eventType,
+ position, searchSessionId);
+ }
}
+
+
}
\ No newline at end of file
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index cd84c96..effb3a4 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -513,8 +513,7 @@
}
// Setup search view
- SearchUiManager searchUiManager =
- mRootView.findViewById(R.id.search_container_all_apps);
+ SearchUiManager searchUiManager = mRootView.findViewById(R.id.search_container_all_apps);
mRootView.findViewById(R.id.apps_view).setTranslationY(
mDp.heightPx - searchUiManager.getScrollRangeDelta(mInsets));
ViewGroup searchView = (ViewGroup) searchUiManager;
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 2b4520b..c0d5882 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -49,6 +49,7 @@
import android.util.TimingLogger;
import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.Utilities;
@@ -306,6 +307,7 @@
final PackageManagerHelper pmHelper = new PackageManagerHelper(context);
final boolean isSafeMode = pmHelper.isSafeMode();
final boolean isSdCardReady = Utilities.isBootCompleted();
+ final WidgetManagerHelper widgetHelper = new WidgetManagerHelper(context);
boolean clearDb = false;
try {
@@ -391,6 +393,7 @@
WorkspaceItemInfo info;
LauncherAppWidgetInfo appWidgetInfo;
+ LauncherAppWidgetProviderInfo widgetProviderInfo;
Intent intent;
String targetPkg;
@@ -720,6 +723,19 @@
+ appWidgetInfo.spanX + "x" + appWidgetInfo.spanY);
continue;
}
+ widgetProviderInfo =
+ widgetHelper.getLauncherAppWidgetInfo(appWidgetId);
+ if (widgetProviderInfo != null
+ && (appWidgetInfo.spanX < widgetProviderInfo.minSpanX
+ || appWidgetInfo.spanY < widgetProviderInfo.minSpanY)) {
+ // This can happen when display size changes.
+ c.markDeleted("Widget removed, min sizes not met: "
+ + "span=" + appWidgetInfo.spanX + "x"
+ + appWidgetInfo.spanY + " minSpan="
+ + widgetProviderInfo.minSpanX + "x"
+ + widgetProviderInfo.minSpanY);
+ continue;
+ }
if (!c.isOnWorkspaceOrHotseat()) {
c.markDeleted("Widget found where container != " +
"CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!");
diff --git a/src/com/android/launcher3/model/data/RemoteActionItemInfo.java b/src/com/android/launcher3/model/data/RemoteActionItemInfo.java
new file mode 100644
index 0000000..81f7f3a
--- /dev/null
+++ b/src/com/android/launcher3/model/data/RemoteActionItemInfo.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2020 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.model.data;
+
+import android.app.RemoteAction;
+import android.os.Process;
+
+/**
+ * Represents a launchable {@link RemoteAction}
+ */
+public class RemoteActionItemInfo extends ItemInfoWithIcon {
+
+ private final RemoteAction mRemoteAction;
+ private final String mToken;
+ private final boolean mShouldStart;
+
+ public RemoteActionItemInfo(RemoteAction remoteAction, String token, boolean shouldStart) {
+ mShouldStart = shouldStart;
+ mToken = token;
+ mRemoteAction = remoteAction;
+ title = remoteAction.getTitle();
+ user = Process.myUserHandle();
+ }
+
+ public RemoteActionItemInfo(RemoteActionItemInfo info) {
+ super(info);
+ this.mShouldStart = info.mShouldStart;
+ this.mRemoteAction = info.mRemoteAction;
+ this.mToken = info.mToken;
+ }
+
+ @Override
+ public ItemInfoWithIcon clone() {
+ return new RemoteActionItemInfo(this);
+ }
+
+ public RemoteAction getRemoteAction() {
+ return mRemoteAction;
+ }
+
+ public String getToken() {
+ return mToken;
+ }
+
+ /**
+ * Getter method for mShouldStart
+ */
+ public boolean shouldStartInLauncher() {
+ return mShouldStart;
+ }
+}
diff --git a/src/com/android/launcher3/pm/InstallSessionHelper.java b/src/com/android/launcher3/pm/InstallSessionHelper.java
index 753a6dd..fa25114 100644
--- a/src/com/android/launcher3/pm/InstallSessionHelper.java
+++ b/src/com/android/launcher3/pm/InstallSessionHelper.java
@@ -17,6 +17,7 @@
package com.android.launcher3.pm;
import static com.android.launcher3.Utilities.getPrefs;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -31,6 +32,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
+import androidx.annotation.WorkerThread;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.SessionCommitReceiver;
@@ -39,10 +41,10 @@
import com.android.launcher3.model.ItemInstallQueue;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.IntSet;
-import com.android.launcher3.util.LooperExecutor;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.util.Preconditions;
import java.util.ArrayList;
import java.util.HashMap;
@@ -65,27 +67,27 @@
private final LauncherApps mLauncherApps;
private final Context mAppContext;
- private final IntSet mPromiseIconIds;
private final PackageInstaller mInstaller;
private final HashMap<String, Boolean> mSessionVerifiedMap = new HashMap<>();
+ private IntSet mPromiseIconIds;
+
public InstallSessionHelper(Context context) {
mInstaller = context.getPackageManager().getPackageInstaller();
mAppContext = context.getApplicationContext();
mLauncherApps = context.getSystemService(LauncherApps.class);
+ }
+ @WorkerThread
+ private IntSet getPromiseIconIds() {
+ Preconditions.assertWorkerThread();
+ if (mPromiseIconIds != null) {
+ return mPromiseIconIds;
+ }
mPromiseIconIds = IntSet.wrap(IntArray.fromConcatString(
- getPrefs(context).getString(PROMISE_ICON_IDS, "")));
+ getPrefs(mAppContext).getString(PROMISE_ICON_IDS, "")));
- cleanUpPromiseIconIds();
- }
-
- public static UserHandle getUserHandle(SessionInfo info) {
- return Utilities.ATLEAST_Q ? info.getUser() : Process.myUserHandle();
- }
-
- protected void cleanUpPromiseIconIds() {
IntArray existingIds = new IntArray();
for (SessionInfo info : getActiveSessions().values()) {
existingIds.add(info.getSessionId());
@@ -100,6 +102,7 @@
for (int i = idsToRemove.size() - 1; i >= 0; --i) {
mPromiseIconIds.getArray().removeValue(idsToRemove.get(i));
}
+ return mPromiseIconIds;
}
public HashMap<PackageUserKey, SessionInfo> getActiveSessions() {
@@ -126,7 +129,7 @@
private void updatePromiseIconPrefs() {
getPrefs(mAppContext).edit()
- .putString(PROMISE_ICON_IDS, mPromiseIconIds.getArray().toConcatString())
+ .putString(PROMISE_ICON_IDS, getPromiseIconIds().getArray().toConcatString())
.apply();
}
@@ -184,13 +187,15 @@
return info.getInstallReason() == PackageManager.INSTALL_REASON_DEVICE_RESTORE;
}
+ @WorkerThread
public boolean promiseIconAddedForId(int sessionId) {
- return mPromiseIconIds.contains(sessionId);
+ return getPromiseIconIds().contains(sessionId);
}
+ @WorkerThread
public void removePromiseIconId(int sessionId) {
- if (mPromiseIconIds.contains(sessionId)) {
- mPromiseIconIds.getArray().removeValue(sessionId);
+ if (promiseIconAddedForId(sessionId)) {
+ getPromiseIconIds().getArray().removeValue(sessionId);
updatePromiseIconPrefs();
}
}
@@ -203,6 +208,7 @@
* - The app is not already installed
* - A promise icon for the session has not already been created
*/
+ @WorkerThread
void tryQueuePromiseAppIcon(PackageInstaller.SessionInfo sessionInfo) {
if (FeatureFlags.PROMISE_APPS_NEW_INSTALLS.get()
&& SessionCommitReceiver.isEnabled(mAppContext)
@@ -210,25 +216,24 @@
&& sessionInfo.getInstallReason() == PackageManager.INSTALL_REASON_USER
&& sessionInfo.getAppIcon() != null
&& !TextUtils.isEmpty(sessionInfo.getAppLabel())
- && !mPromiseIconIds.contains(sessionInfo.getSessionId())
+ && !promiseIconAddedForId(sessionInfo.getSessionId())
&& new PackageManagerHelper(mAppContext).getApplicationInfo(
sessionInfo.getAppPackageName(), getUserHandle(sessionInfo), 0) == null) {
ItemInstallQueue.INSTANCE.get(mAppContext)
.queueItem(sessionInfo.getAppPackageName(), getUserHandle(sessionInfo));
- mPromiseIconIds.add(sessionInfo.getSessionId());
+ getPromiseIconIds().add(sessionInfo.getSessionId());
updatePromiseIconPrefs();
}
}
- public InstallSessionTracker registerInstallTracker(
- InstallSessionTracker.Callback callback, LooperExecutor executor) {
+ public InstallSessionTracker registerInstallTracker(InstallSessionTracker.Callback callback) {
InstallSessionTracker tracker = new InstallSessionTracker(this, callback);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
- mInstaller.registerSessionCallback(tracker, executor.getHandler());
+ mInstaller.registerSessionCallback(tracker, MODEL_EXECUTOR.getHandler());
} else {
- mLauncherApps.registerPackageInstallerSessionCallback(executor, tracker);
+ mLauncherApps.registerPackageInstallerSessionCallback(MODEL_EXECUTOR, tracker);
}
return tracker;
}
@@ -240,4 +245,8 @@
mLauncherApps.unregisterPackageInstallerSessionCallback(tracker);
}
}
+
+ public static UserHandle getUserHandle(SessionInfo info) {
+ return Utilities.ATLEAST_Q ? info.getUser() : Process.myUserHandle();
+ }
}
diff --git a/src/com/android/launcher3/pm/InstallSessionTracker.java b/src/com/android/launcher3/pm/InstallSessionTracker.java
index eb3ca73..b0b907a 100644
--- a/src/com/android/launcher3/pm/InstallSessionTracker.java
+++ b/src/com/android/launcher3/pm/InstallSessionTracker.java
@@ -24,8 +24,11 @@
import android.os.UserHandle;
import android.util.SparseArray;
+import androidx.annotation.WorkerThread;
+
import com.android.launcher3.util.PackageUserKey;
+@WorkerThread
public class InstallSessionTracker extends PackageInstaller.SessionCallback {
// Lazily initialized
diff --git a/src/com/android/launcher3/pm/UserCache.java b/src/com/android/launcher3/pm/UserCache.java
index 5aab41a..5ade22b 100644
--- a/src/com/android/launcher3/pm/UserCache.java
+++ b/src/com/android/launcher3/pm/UserCache.java
@@ -21,8 +21,10 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.util.ArrayMap;
+import android.util.Log;
import android.util.LongSparseArray;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.SafeCloseable;
import com.android.launcher3.util.SimpleBroadcastReceiver;
@@ -58,6 +60,9 @@
private void onUsersChanged(Intent intent) {
enableAndResetCache();
mUserChangeListeners.forEach(Runnable::run);
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.WORK_PROFILE_REMOVED, "profile changed", new Exception());
+ }
}
/**
diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java
index 7f76355..b2d0081 100644
--- a/src/com/android/launcher3/testing/TestProtocol.java
+++ b/src/com/android/launcher3/testing/TestProtocol.java
@@ -106,4 +106,5 @@
public static final String PAUSE_NOT_DETECTED = "b/139891609";
public static final String OVERIEW_NOT_ALLAPPS = "b/156095088";
public static final String NO_SWIPE_TO_HOME = "b/158017601";
+ public static final String WORK_PROFILE_REMOVED = "b/159671700";
}
diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java
index 61d6f7d..d56391d 100644
--- a/src/com/android/launcher3/touch/ItemClickHandler.java
+++ b/src/com/android/launcher3/touch/ItemClickHandler.java
@@ -25,7 +25,9 @@
import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED;
import android.app.AlertDialog;
+import android.app.PendingIntent;
import android.content.Intent;
+import android.content.IntentSender;
import android.content.pm.LauncherApps;
import android.content.pm.PackageInstaller.SessionInfo;
import android.os.Process;
@@ -49,6 +51,7 @@
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.model.data.PromiseAppInfo;
+import com.android.launcher3.model.data.RemoteActionItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.pm.InstallSessionHelper;
import com.android.launcher3.testing.TestLogging;
@@ -236,6 +239,27 @@
startAppShortcutOrInfoActivity(v, shortcut, launcher);
}
+ /**
+ * Event handler for a {@link android.app.RemoteAction} click
+ *
+ */
+ public static void onClickRemoteAction(Launcher launcher,
+ RemoteActionItemInfo remoteActionInfo) {
+ try {
+ PendingIntent pendingIntent = remoteActionInfo.getRemoteAction().getActionIntent();
+ if (remoteActionInfo.shouldStartInLauncher()) {
+ launcher.startIntentSenderForResult(pendingIntent.getIntentSender(), 0, null, 0, 0,
+ 0);
+ } else {
+ pendingIntent.send();
+ }
+ } catch (PendingIntent.CanceledException | IntentSender.SendIntentException e) {
+ Toast.makeText(launcher,
+ launcher.getResources().getText(R.string.shortcut_not_available),
+ Toast.LENGTH_SHORT).show();
+ }
+ }
+
private static void startAppShortcutOrInfoActivity(View v, ItemInfo item, Launcher launcher) {
TestLogging.recordEvent(
TestProtocol.SEQUENCE_MAIN, "start: startAppShortcutOrInfoActivity");
diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
index fb02f79..17f02be 100644
--- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
@@ -72,7 +72,6 @@
out.halfPageSize = view.getNormalChildHeight() / 2;
out.halfScreenSize = view.getMeasuredHeight() / 2;
out.screenCenter = insets.top + view.getPaddingTop() + out.scroll + out.halfPageSize;
- out.pageParentScale = view.getScaleY();
}
@Override
diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java
index 354d78d..114b75a 100644
--- a/src/com/android/launcher3/touch/PagedOrientationHandler.java
+++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java
@@ -112,7 +112,6 @@
public int halfPageSize;
public int screenCenter;
public int halfScreenSize;
- public float pageParentScale;
}
class ChildBounds {
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index 06479e6..5f5b2d1 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -70,7 +70,6 @@
out.halfPageSize = view.getNormalChildWidth() / 2;
out.halfScreenSize = view.getMeasuredWidth() / 2;
out.screenCenter = insets.left + view.getPaddingLeft() + out.scroll + out.halfPageSize;
- out.pageParentScale = view.getScaleX();
}
@Override
diff --git a/src/com/android/launcher3/views/HeroSearchResultView.java b/src/com/android/launcher3/views/HeroSearchResultView.java
index a8e1c6b..dd322d9 100644
--- a/src/com/android/launcher3/views/HeroSearchResultView.java
+++ b/src/com/android/launcher3/views/HeroSearchResultView.java
@@ -16,12 +16,16 @@
package com.android.launcher3.views;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_ALL_APPS;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ShortcutInfo;
import android.graphics.Point;
import android.os.Bundle;
import android.util.AttributeSet;
+import android.util.Pair;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
@@ -31,9 +35,10 @@
import com.android.launcher3.DragSource;
import com.android.launcher3.DropTarget;
import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload;
-import com.android.launcher3.allapps.search.AllAppsSearchBarController;
+import com.android.launcher3.allapps.search.AllAppsSearchBarController.PayloadResultHandler;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.dragndrop.DraggableView;
import com.android.launcher3.graphics.DragPreviewProvider;
@@ -53,9 +58,10 @@
* A view representing a high confidence app search result that includes shortcuts
*/
public class HeroSearchResultView extends LinearLayout implements DragSource,
- AllAppsSearchBarController.PayloadResultHandler<List<ItemInfoWithIcon>> {
+ PayloadResultHandler<List<Pair<ShortcutInfo, ItemInfoWithIcon>>> {
public static final int MAX_SHORTCUTS_COUNT = 2;
+ private final Object[] mTargetInfo = createTargetInfo();
BubbleTextView mBubbleTextView;
View mIconView;
BubbleTextView[] mDeepShortcutTextViews = new BubbleTextView[2];
@@ -102,7 +108,7 @@
grid.allAppsIconSizePx));
bubbleTextView.setOnClickListener(view -> {
WorkspaceItemInfo itemInfo = (WorkspaceItemInfo) bubbleTextView.getTag();
- SearchTargetEvent event = new SearchTargetEvent(
+ SearchTargetEvent event = getSearchTargetEvent(
SearchTarget.ItemType.APP_HERO,
SearchTargetEvent.CHILD_SELECT);
event.bundle = getAppBundle(itemInfo);
@@ -119,15 +125,25 @@
* Apply {@link ItemInfo} for appIcon and shortcut Icons
*/
@Override
- public void applyAdapterInfo(AdapterItemWithPayload<List<ItemInfoWithIcon>> adapterItem) {
+ public void applyAdapterInfo(
+ AdapterItemWithPayload<List<Pair<ShortcutInfo, ItemInfoWithIcon>>> adapterItem) {
mBubbleTextView.applyFromApplicationInfo(adapterItem.appInfo);
mIconView.setBackground(mBubbleTextView.getIcon());
mIconView.setTag(adapterItem.appInfo);
- List<ItemInfoWithIcon> shorcutInfos = adapterItem.getPayload();
+ List<Pair<ShortcutInfo, ItemInfoWithIcon>> shortcutDetails = adapterItem.getPayload();
+ LauncherAppState appState = LauncherAppState.getInstance(getContext());
for (int i = 0; i < mDeepShortcutTextViews.length; i++) {
- mDeepShortcutTextViews[i].setVisibility(shorcutInfos.size() > i ? VISIBLE : GONE);
- if (i < shorcutInfos.size()) {
- mDeepShortcutTextViews[i].applyFromItemInfoWithIcon(shorcutInfos.get(i));
+ BubbleTextView shortcutView = mDeepShortcutTextViews[i];
+ mDeepShortcutTextViews[i].setVisibility(shortcutDetails.size() > i ? VISIBLE : GONE);
+ if (i < shortcutDetails.size()) {
+ Pair<ShortcutInfo, ItemInfoWithIcon> p = shortcutDetails.get(i);
+ //apply ItemInfo and prepare view
+ shortcutView.applyFromWorkspaceItem((WorkspaceItemInfo) p.second);
+ MODEL_EXECUTOR.execute(() -> {
+ // load unbadged shortcut in background and update view when icon ready
+ appState.getIconCache().getUnbadgedShortcutIcon(p.second, p.first);
+ MAIN_EXECUTOR.post(() -> shortcutView.reapplyItemInfo(p.second));
+ });
}
}
mPlugin = adapterItem.getPlugin();
@@ -135,6 +151,11 @@
}
@Override
+ public Object[] getTargetInfo() {
+ return mTargetInfo;
+ }
+
+ @Override
public void onDropCompleted(View target, DropTarget.DragObject d, boolean success) {
mBubbleTextView.setVisibility(VISIBLE);
mBubbleTextView.setIconVisible(true);
@@ -169,7 +190,7 @@
mLauncher.getWorkspace().beginDragShared(mContainer.mBubbleTextView,
draggableView, mContainer, itemInfo, previewProvider, new DragOptions());
- SearchTargetEvent event = new SearchTargetEvent(
+ SearchTargetEvent event = mContainer.getSearchTargetEvent(
SearchTarget.ItemType.APP_HERO, SearchTargetEvent.LONG_PRESS);
event.bundle = getAppBundle(itemInfo);
if (mContainer.mPlugin != null) {
@@ -186,7 +207,7 @@
Launcher launcher = Launcher.getLauncher(getContext());
launcher.startActivitySafely(this, itemInfo.getIntent(), itemInfo);
- SearchTargetEvent event = new SearchTargetEvent(
+ SearchTargetEvent event = getSearchTargetEvent(
SearchTarget.ItemType.APP_HERO, eventType);
event.bundle = getAppBundle(itemInfo);
if (mPlugin != null) {
diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java
index 3ec20d5..a8caa9e 100644
--- a/src/com/android/launcher3/views/OptionsPopupView.java
+++ b/src/com/android/launcher3/views/OptionsPopupView.java
@@ -16,6 +16,7 @@
package com.android.launcher3.views;
import static com.android.launcher3.Utilities.EXTRA_WALLPAPER_FLAVOR;
+import static com.android.launcher3.Utilities.EXTRA_WALLPAPER_LAUNCH_SOURCE;
import static com.android.launcher3.Utilities.EXTRA_WALLPAPER_OFFSET;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.IGNORE;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SETTINGS_BUTTON_TAP_OR_LONGPRESS;
@@ -211,7 +212,8 @@
Intent intent = new Intent(Intent.ACTION_SET_WALLPAPER)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
.putExtra(EXTRA_WALLPAPER_OFFSET,
- launcher.getWorkspace().getWallpaperOffsetForCenterPage());
+ launcher.getWorkspace().getWallpaperOffsetForCenterPage())
+ .putExtra(EXTRA_WALLPAPER_LAUNCH_SOURCE, "app_launched_launcher");
if (!Utilities.existsStyleWallpapers(launcher)) {
intent.putExtra(EXTRA_WALLPAPER_FLAVOR, "wallpaper_only");
} else {
diff --git a/src/com/android/launcher3/views/SearchResultIconRow.java b/src/com/android/launcher3/views/SearchResultIconRow.java
new file mode 100644
index 0000000..57c85cf
--- /dev/null
+++ b/src/com/android/launcher3/views/SearchResultIconRow.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2020 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.views;
+
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+
+import android.app.RemoteAction;
+import android.content.Context;
+import android.content.pm.ShortcutInfo;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.os.Bundle;
+import android.util.AttributeSet;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload;
+import com.android.launcher3.allapps.search.AllAppsSearchBarController;
+import com.android.launcher3.icons.LauncherIcons;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.ItemInfoWithIcon;
+import com.android.launcher3.model.data.RemoteActionItemInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.touch.ItemClickHandler;
+import com.android.systemui.plugins.AllAppsSearchPlugin;
+import com.android.systemui.plugins.shared.SearchTarget;
+import com.android.systemui.plugins.shared.SearchTargetEvent;
+
+/**
+ * A view representing a stand alone shortcut search result
+ */
+public class SearchResultIconRow extends DoubleShadowBubbleTextView implements
+ AllAppsSearchBarController.PayloadResultHandler<SearchTarget> {
+
+ private final Object[] mTargetInfo = createTargetInfo();
+ private ShortcutInfo mShortcutInfo;
+ private AllAppsSearchPlugin mPlugin;
+ private AdapterItemWithPayload<SearchTarget> mAdapterItem;
+
+
+ public SearchResultIconRow(@NonNull Context context) {
+ super(context);
+ }
+
+ public SearchResultIconRow(@NonNull Context context,
+ @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public SearchResultIconRow(@NonNull Context context, @Nullable AttributeSet attrs,
+ int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ @Override
+ public void applyAdapterInfo(AdapterItemWithPayload<SearchTarget> adapterItemWithPayload) {
+ if (mAdapterItem != null) {
+ mAdapterItem.setSelectionHandler(null);
+ }
+ mAdapterItem = adapterItemWithPayload;
+ SearchTarget payload = adapterItemWithPayload.getPayload();
+ mPlugin = adapterItemWithPayload.getPlugin();
+
+ if (payload.mRemoteAction != null) {
+ prepareUsingRemoteAction(payload.mRemoteAction,
+ payload.bundle.getString(SearchTarget.REMOTE_ACTION_TOKEN),
+ payload.bundle.getBoolean(SearchTarget.REMOTE_ACTION_SHOULD_START));
+ } else {
+ prepareUsingShortcutInfo(payload.shortcuts.get(0));
+ }
+ setOnClickListener(v -> handleSelection(SearchTargetEvent.SELECT));
+ adapterItemWithPayload.setSelectionHandler(this::handleSelection);
+ }
+
+ private void prepareUsingShortcutInfo(ShortcutInfo shortcutInfo) {
+ mShortcutInfo = shortcutInfo;
+ WorkspaceItemInfo workspaceItemInfo = new WorkspaceItemInfo(mShortcutInfo, getContext());
+ applyFromWorkspaceItem(workspaceItemInfo);
+ LauncherAppState launcherAppState = LauncherAppState.getInstance(getContext());
+ MODEL_EXECUTOR.execute(() -> {
+ launcherAppState.getIconCache().getShortcutIcon(workspaceItemInfo, mShortcutInfo);
+ reapplyItemInfoAsync(workspaceItemInfo);
+ });
+ }
+
+ private void prepareUsingRemoteAction(RemoteAction remoteAction, String token, boolean start) {
+ RemoteActionItemInfo itemInfo = new RemoteActionItemInfo(remoteAction, token, start);
+
+ applyFromRemoteActionInfo(itemInfo);
+ UI_HELPER_EXECUTOR.post(() -> {
+ // If the Drawable from the remote action is not AdaptiveBitmap, styling will not work.
+ try (LauncherIcons li = LauncherIcons.obtain(getContext())) {
+ Drawable d = itemInfo.getRemoteAction().getIcon().loadDrawable(getContext());
+ itemInfo.bitmap = li.createBadgedIconBitmap(d, itemInfo.user,
+ Build.VERSION.SDK_INT);
+ reapplyItemInfoAsync(itemInfo);
+ }
+ });
+
+ }
+
+ void reapplyItemInfoAsync(ItemInfoWithIcon itemInfoWithIcon) {
+ MAIN_EXECUTOR.post(() -> reapplyItemInfo(itemInfoWithIcon));
+ }
+
+ @Override
+ public Object[] getTargetInfo() {
+ return mTargetInfo;
+ }
+
+ private void handleSelection(int eventType) {
+ ItemInfo itemInfo = (ItemInfo) getTag();
+ Launcher launcher = Launcher.getLauncher(getContext());
+ final SearchTargetEvent searchTargetEvent;
+ if (itemInfo instanceof WorkspaceItemInfo) {
+ ItemClickHandler.onClickAppShortcut(this, (WorkspaceItemInfo) itemInfo, launcher);
+ searchTargetEvent = getSearchTargetEvent(SearchTarget.ItemType.SHORTCUT,
+ eventType);
+ searchTargetEvent.shortcut = mShortcutInfo;
+ } else {
+ RemoteActionItemInfo remoteItemInfo = (RemoteActionItemInfo) itemInfo;
+ ItemClickHandler.onClickRemoteAction(launcher, remoteItemInfo);
+ searchTargetEvent = getSearchTargetEvent(SearchTarget.ItemType.REMOTE_ACTION,
+ eventType);
+ searchTargetEvent.bundle = new Bundle();
+ searchTargetEvent.remoteAction = remoteItemInfo.getRemoteAction();
+ searchTargetEvent.bundle.putBoolean(SearchTarget.REMOTE_ACTION_SHOULD_START,
+ remoteItemInfo.shouldStartInLauncher());
+ searchTargetEvent.bundle.putString(SearchTarget.REMOTE_ACTION_TOKEN,
+ remoteItemInfo.getToken());
+ }
+ if (mPlugin != null) {
+ mPlugin.notifySearchTargetEvent(searchTargetEvent);
+ }
+ }
+}
diff --git a/src/com/android/launcher3/views/SearchResultPeopleView.java b/src/com/android/launcher3/views/SearchResultPeopleView.java
index 6e45e88..eacc095 100644
--- a/src/com/android/launcher3/views/SearchResultPeopleView.java
+++ b/src/com/android/launcher3/views/SearchResultPeopleView.java
@@ -67,7 +67,7 @@
private ImageButton[] mProviderButtons = new ImageButton[3];
private AllAppsSearchPlugin mPlugin;
private Uri mContactUri;
-
+ private final Object[] mTargetInfo = createTargetInfo();
public SearchResultPeopleView(Context context) {
this(context, null, 0);
@@ -129,14 +129,14 @@
Bundle provider = providers.get(i);
Intent intent = Intent.parseUri(provider.getString("intent_uri_str"),
URI_ANDROID_APP_SCHEME | URI_ALLOW_UNSAFE);
- setupProviderButton(button, provider, intent);
+ setupProviderButton(button, provider, intent, adapterItemWithPayload);
String pkg = provider.getString("package_name");
UI_HELPER_EXECUTOR.post(() -> {
try {
ApplicationInfo applicationInfo = mPackageManager.getApplicationInfo(
pkg, 0);
Drawable appIcon = applicationInfo.loadIcon(mPackageManager);
- MAIN_EXECUTOR.post(()-> button.setImageDrawable(appIcon));
+ MAIN_EXECUTOR.post(() -> button.setImageDrawable(appIcon));
} catch (PackageManager.NameNotFoundException ignored) {
}
@@ -151,11 +151,17 @@
adapterItemWithPayload.setSelectionHandler(this::handleSelection);
}
- private void setupProviderButton(ImageButton button, Bundle provider, Intent intent) {
+ @Override
+ public Object[] getTargetInfo() {
+ return mTargetInfo;
+ }
+
+ private void setupProviderButton(ImageButton button, Bundle provider, Intent intent,
+ AllAppsGridAdapter.AdapterItem adapterItem) {
Launcher launcher = Launcher.getLauncher(getContext());
button.setOnClickListener(b -> {
launcher.startActivitySafely(b, intent, null);
- SearchTargetEvent searchTargetEvent = new SearchTargetEvent(
+ SearchTargetEvent searchTargetEvent = getSearchTargetEvent(
SearchTarget.ItemType.PEOPLE,
SearchTargetEvent.CHILD_SELECT);
searchTargetEvent.bundle = new Bundle();
@@ -173,8 +179,8 @@
Launcher launcher = Launcher.getLauncher(getContext());
launcher.startActivitySafely(this, new Intent(Intent.ACTION_VIEW, mContactUri).setFlags(
Intent.FLAG_ACTIVITY_NEW_TASK), null);
- SearchTargetEvent searchTargetEvent = new SearchTargetEvent(
- SearchTarget.ItemType.PEOPLE, eventType);
+ SearchTargetEvent searchTargetEvent = getSearchTargetEvent(SearchTarget.ItemType.PEOPLE,
+ eventType);
searchTargetEvent.bundle = new Bundle();
searchTargetEvent.bundle.putParcelable("contact_uri", mContactUri);
if (mPlugin != null) {
diff --git a/src/com/android/launcher3/views/SearchResultPlayItem.java b/src/com/android/launcher3/views/SearchResultPlayItem.java
index 8624609..ff3ecc8 100644
--- a/src/com/android/launcher3/views/SearchResultPlayItem.java
+++ b/src/com/android/launcher3/views/SearchResultPlayItem.java
@@ -58,6 +58,8 @@
private String mPackageName;
private boolean mIsInstantGame;
private AllAppsSearchPlugin mPlugin;
+ private final Object[] mTargetInfo = createTargetInfo();
+
public SearchResultPlayItem(Context context) {
this(context, null, 0);
@@ -125,6 +127,11 @@
});
}
+ @Override
+ public Object[] getTargetInfo() {
+ return mTargetInfo;
+ }
+
private void showIfNecessary(TextView textView, @Nullable String string) {
if (string == null || string.isEmpty()) {
textView.setVisibility(GONE);
@@ -160,7 +167,7 @@
}
private void logSearchEvent(int eventType) {
- SearchTargetEvent searchTargetEvent = new SearchTargetEvent(
+ SearchTargetEvent searchTargetEvent = getSearchTargetEvent(
SearchTarget.ItemType.PLAY_RESULTS, eventType);
searchTargetEvent.bundle = new Bundle();
searchTargetEvent.bundle.putString("package_name", mPackageName);
diff --git a/src/com/android/launcher3/views/SearchResultShortcut.java b/src/com/android/launcher3/views/SearchResultShortcut.java
deleted file mode 100644
index 307cf34..0000000
--- a/src/com/android/launcher3/views/SearchResultShortcut.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2020 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.views;
-
-import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
-
-import android.content.Context;
-import android.content.pm.ShortcutInfo;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.R;
-import com.android.launcher3.allapps.AllAppsGridAdapter;
-import com.android.launcher3.allapps.search.AllAppsSearchBarController;
-import com.android.launcher3.model.data.WorkspaceItemInfo;
-import com.android.launcher3.touch.ItemClickHandler;
-import com.android.systemui.plugins.AllAppsSearchPlugin;
-import com.android.systemui.plugins.shared.SearchTarget;
-import com.android.systemui.plugins.shared.SearchTargetEvent;
-
-/**
- * A view representing a stand alone shortcut search result
- */
-public class SearchResultShortcut extends FrameLayout implements
- AllAppsSearchBarController.PayloadResultHandler<SearchTarget> {
-
- private BubbleTextView mBubbleTextView;
- private View mIconView;
- private ShortcutInfo mShortcutInfo;
- private AllAppsSearchPlugin mPlugin;
-
- public SearchResultShortcut(@NonNull Context context) {
- super(context);
- }
-
- public SearchResultShortcut(@NonNull Context context,
- @Nullable AttributeSet attrs) {
- super(context, attrs);
- }
-
- public SearchResultShortcut(@NonNull Context context, @Nullable AttributeSet attrs,
- int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- Launcher launcher = Launcher.getLauncher(getContext());
- DeviceProfile grid = launcher.getDeviceProfile();
- mIconView = findViewById(R.id.icon);
- ViewGroup.LayoutParams iconParams = mIconView.getLayoutParams();
- iconParams.height = grid.allAppsIconSizePx;
- iconParams.width = grid.allAppsIconSizePx;
- mBubbleTextView = findViewById(R.id.bubble_text);
- setOnClickListener(v -> handleSelection(SearchTargetEvent.SELECT));
- }
-
- @Override
- public void applyAdapterInfo(
- AllAppsGridAdapter.AdapterItemWithPayload<SearchTarget> adapterItemWithPayload) {
- SearchTarget payload = adapterItemWithPayload.getPayload();
- mPlugin = adapterItemWithPayload.getPlugin();
- mShortcutInfo = payload.shortcuts.get(0);
- WorkspaceItemInfo workspaceItemInfo = new WorkspaceItemInfo(mShortcutInfo, getContext());
- mBubbleTextView.applyFromWorkspaceItem(workspaceItemInfo);
- mIconView.setBackground(mBubbleTextView.getIcon());
- LauncherAppState launcherAppState = LauncherAppState.getInstance(getContext());
- MODEL_EXECUTOR.execute(() -> {
- launcherAppState.getIconCache().getShortcutIcon(workspaceItemInfo, mShortcutInfo);
- mBubbleTextView.applyFromWorkspaceItem(workspaceItemInfo);
- mIconView.setBackground(mBubbleTextView.getIcon());
- });
- adapterItemWithPayload.setSelectionHandler(this::handleSelection);
- }
-
- private void handleSelection(int eventType) {
- WorkspaceItemInfo itemInfo = (WorkspaceItemInfo) mBubbleTextView.getTag();
- ItemClickHandler.onClickAppShortcut(this, itemInfo, Launcher.getLauncher(getContext()));
-
- SearchTargetEvent searchTargetEvent = new SearchTargetEvent(
- SearchTarget.ItemType.SHORTCUT, eventType);
- searchTargetEvent.shortcut = mShortcutInfo;
- if (mPlugin != null) {
- mPlugin.notifySearchTargetEvent(searchTargetEvent);
- }
- }
-}
diff --git a/src/com/android/launcher3/views/SearchSectionHeaderView.java b/src/com/android/launcher3/views/SearchSectionHeaderView.java
index d439ee3..0fe0a43 100644
--- a/src/com/android/launcher3/views/SearchSectionHeaderView.java
+++ b/src/com/android/launcher3/views/SearchSectionHeaderView.java
@@ -52,4 +52,9 @@
setVisibility(INVISIBLE);
}
}
+
+ @Override
+ public Object[] getTargetInfo() {
+ return null;
+ }
}
diff --git a/src/com/android/launcher3/views/SearchSettingsRowView.java b/src/com/android/launcher3/views/SearchSettingsRowView.java
index 93bcee2..a1a0172 100644
--- a/src/com/android/launcher3/views/SearchSettingsRowView.java
+++ b/src/com/android/launcher3/views/SearchSettingsRowView.java
@@ -48,6 +48,8 @@
private TextView mBreadcrumbsView;
private Intent mIntent;
private AllAppsSearchPlugin mPlugin;
+ private final Object[] mTargetInfo = createTargetInfo();
+
public SearchSettingsRowView(@NonNull Context context) {
super(context);
@@ -87,6 +89,11 @@
adapterItemWithPayload.setSelectionHandler(this::handleSelection);
}
+ @Override
+ public Object[] getTargetInfo() {
+ return mTargetInfo;
+ }
+
private void showIfAvailable(TextView view, @Nullable String string) {
if (TextUtils.isEmpty(string)) {
view.setVisibility(GONE);
@@ -108,7 +115,7 @@
Launcher launcher = Launcher.getLauncher(getContext());
launcher.startActivityForResult(mIntent, 0);
- SearchTargetEvent searchTargetEvent = new SearchTargetEvent(
+ SearchTargetEvent searchTargetEvent = getSearchTargetEvent(
SearchTarget.ItemType.SETTINGS_ROW, eventType);
searchTargetEvent.bundle = new Bundle();
searchTargetEvent.bundle.putParcelable("intent", mIntent);
diff --git a/src/com/android/launcher3/views/ThumbnailSearchResultView.java b/src/com/android/launcher3/views/ThumbnailSearchResultView.java
new file mode 100644
index 0000000..1d58a06
--- /dev/null
+++ b/src/com/android/launcher3/views/ThumbnailSearchResultView.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2020 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.views;
+
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.AttributeSet;
+
+import androidx.core.graphics.drawable.RoundedBitmapDrawable;
+import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload;
+import com.android.launcher3.allapps.search.AllAppsSearchBarController;
+import com.android.launcher3.util.Themes;
+import com.android.systemui.plugins.AllAppsSearchPlugin;
+import com.android.systemui.plugins.shared.SearchTarget;
+import com.android.systemui.plugins.shared.SearchTargetEvent;
+
+/**
+ * A view representing a high confidence app search result that includes shortcuts
+ */
+public class ThumbnailSearchResultView extends androidx.appcompat.widget.AppCompatImageView
+ implements AllAppsSearchBarController.PayloadResultHandler<Bundle> {
+
+ private final Object[] mTargetInfo = createTargetInfo();
+ Intent mIntent;
+ AllAppsSearchPlugin mPlugin;
+ int mPosition;
+
+ public ThumbnailSearchResultView(Context context) {
+ super(context);
+ }
+
+ public ThumbnailSearchResultView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public ThumbnailSearchResultView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ private void handleSelection(int eventType) {
+ Launcher launcher = Launcher.getLauncher(getContext());
+ launcher.startActivitySafely(this, mIntent, null);
+
+ SearchTargetEvent event = getSearchTargetEvent(
+ SearchTarget.ItemType.SCREENSHOT, eventType);
+ if (mPlugin != null) {
+ mPlugin.notifySearchTargetEvent(event);
+ }
+ }
+
+ @Override
+ public void applyAdapterInfo(AdapterItemWithPayload<Bundle> adapterItem) {
+ mPosition = adapterItem.position;
+ RoundedBitmapDrawable drawable = RoundedBitmapDrawableFactory.create(null,
+ (Bitmap) adapterItem.getPayload().getParcelable("bitmap"));
+ drawable.setCornerRadius(Themes.getDialogCornerRadius(getContext()));
+ setImageDrawable(drawable);
+ mIntent = new Intent(Intent.ACTION_VIEW)
+ .setData(Uri.parse(adapterItem.getPayload().getString("uri")))
+ .setType("image/*")
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mPlugin = adapterItem.getPlugin();
+ adapterItem.setSelectionHandler(this::handleSelection);
+ }
+
+ @Override
+ public Object[] getTargetInfo() {
+ return mTargetInfo;
+ }
+}
diff --git a/src_plugins/com/android/systemui/plugins/AllAppsSearchPlugin.java b/src_plugins/com/android/systemui/plugins/AllAppsSearchPlugin.java
index 4fa670f..aa3ab8f 100644
--- a/src_plugins/com/android/systemui/plugins/AllAppsSearchPlugin.java
+++ b/src_plugins/com/android/systemui/plugins/AllAppsSearchPlugin.java
@@ -32,7 +32,7 @@
@ProvidesInterface(action = AllAppsSearchPlugin.ACTION, version = AllAppsSearchPlugin.VERSION)
public interface AllAppsSearchPlugin extends Plugin {
String ACTION = "com.android.systemui.action.PLUGIN_ALL_APPS_SEARCH_ACTIONS";
- int VERSION = 6;
+ int VERSION = 7;
void setup(Activity activity, View view);
@@ -43,6 +43,12 @@
void onStateTransitionComplete(int state);
/**
+ * Send launcher window focus and visibility changed signals.
+ */
+ void onWindowFocusChanged(boolean hasFocus);
+ void onWindowVisibilityChanged(int visibility);
+
+ /**
* Send signal when user starts typing, perform search, when search ends
*/
void startedSearchSession();
diff --git a/src_plugins/com/android/systemui/plugins/shared/SearchTarget.java b/src_plugins/com/android/systemui/plugins/shared/SearchTarget.java
index c6b8300..9d9ccba 100644
--- a/src_plugins/com/android/systemui/plugins/shared/SearchTarget.java
+++ b/src_plugins/com/android/systemui/plugins/shared/SearchTarget.java
@@ -15,6 +15,7 @@
*/
package com.android.systemui.plugins.shared;
+import android.app.RemoteAction;
import android.content.pm.ShortcutInfo;
import android.os.Bundle;
@@ -25,17 +26,64 @@
*/
public class SearchTarget implements Comparable<SearchTarget> {
+
+ /**
+ * A bundle key for boolean value of whether remote action should be started in launcher or not
+ */
+ public static final String REMOTE_ACTION_SHOULD_START = "should_start_for_result";
+ public static final String REMOTE_ACTION_TOKEN = "action_token";
+
+
public enum ViewType {
+
+ /**
+ * Consists of N number of icons. (N: launcher column count)
+ */
TOP_HIT(0),
+
+ /**
+ * Consists of 1 icon and two subsidiary icons.
+ */
HERO(1),
+
+ /**
+ * Main/sub/breadcrumb texts are rendered.
+ */
DETAIL(2),
+
+ /**
+ * Consists of an icon, three detail strings.
+ */
ROW(3),
+
+ /**
+ * Consists of an icon, three detail strings and a button.
+ */
ROW_WITH_BUTTON(4),
+
+ /**
+ * Consists of a single slice view
+ */
SLICE(5),
+
+ /**
+ * Similar to hero section.
+ */
SHORTCUT(6),
- PEOPLE(7);
+
+ /**
+ * Person icon and handling app icons are rendered.
+ */
+ PEOPLE(7),
+
+ /**
+ * N number of 1x1 ratio thumbnail is rendered.
+ * (current N = 3)
+ */
+ THUMBNAIL(8);
private final int mId;
+
ViewType(int id) {
mId = id;
}
@@ -52,9 +100,13 @@
APP(3, "", ViewType.TOP_HIT),
APP_HERO(4, "", ViewType.HERO),
SHORTCUT(5, "Shortcuts", ViewType.SHORTCUT),
- PEOPLE(6, "People", ViewType.PEOPLE);
+ PEOPLE(6, "People", ViewType.PEOPLE),
+ SCREENSHOT(7, "Screenshots", ViewType.THUMBNAIL),
+ REMOTE_ACTION(8, "Remote Actions", ViewType.SHORTCUT);
private final int mId;
+
+ /** Used to render section title. */
private final String mTitle;
private final ViewType mViewType;
@@ -81,19 +133,21 @@
public List<ShortcutInfo> shortcuts;
public Bundle bundle;
public float score;
+ public String mSessionId;
+ public RemoteAction mRemoteAction;
/**
* Constructor to create the search target. Bundle is currently temporary to hold
* search target primitives that cannot be expressed as java primitive objects
* or AOSP native objects.
- *
*/
public SearchTarget(ItemType itemType, List<ShortcutInfo> shortcuts,
- Bundle bundle, float score) {
+ Bundle bundle, float score, String sessionId) {
this.type = itemType;
this.shortcuts = shortcuts;
this.bundle = bundle;
this.score = score;
+ this.mSessionId = sessionId;
}
@Override
diff --git a/src_plugins/com/android/systemui/plugins/shared/SearchTargetEvent.java b/src_plugins/com/android/systemui/plugins/shared/SearchTargetEvent.java
index ac4bc33..5016abc 100644
--- a/src_plugins/com/android/systemui/plugins/shared/SearchTargetEvent.java
+++ b/src_plugins/com/android/systemui/plugins/shared/SearchTargetEvent.java
@@ -15,6 +15,7 @@
*/
package com.android.systemui.plugins.shared;
+import android.app.RemoteAction;
import android.content.pm.ShortcutInfo;
import android.os.Bundle;
@@ -29,12 +30,17 @@
public SearchTarget.ItemType type;
public ShortcutInfo shortcut;
+ public RemoteAction remoteAction;
public int eventType;
public Bundle bundle;
- public float score;
+ public int index;
+ public String sessionIdentifier;
- public SearchTargetEvent(SearchTarget.ItemType itemType, int eventType) {
+ public SearchTargetEvent(SearchTarget.ItemType itemType, int eventType, int index,
+ String sessionId) {
this.type = itemType;
this.eventType = eventType;
+ this.index = index;
+ this.sessionIdentifier = sessionId;
}
}
diff --git a/tests/src/com/android/launcher3/ui/WorkTabTest.java b/tests/src/com/android/launcher3/ui/WorkTabTest.java
index e51c1ca..488e763 100644
--- a/tests/src/com/android/launcher3/ui/WorkTabTest.java
+++ b/tests/src/com/android/launcher3/ui/WorkTabTest.java
@@ -24,6 +24,7 @@
import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
+import android.util.Log;
import android.widget.TextView;
import androidx.test.filters.LargeTest;
@@ -34,6 +35,7 @@
import com.android.launcher3.allapps.AllAppsPagedView;
import com.android.launcher3.allapps.WorkModeSwitch;
import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.views.WorkEduView;
import org.junit.After;
@@ -133,6 +135,10 @@
workEduView.findViewById(R.id.proceed).callOnClick();
});
+ executeOnLauncher(launcher -> Log.d(TestProtocol.WORK_PROFILE_REMOVED,
+ "work profile status (" + mProfileUserId + ") :"
+ + launcher.getAppsView().isWorkTabVisible()));
+
// verify work edu is seen next
waitForLauncherCondition("Launcher did not show the next edu screen", l ->
((AllAppsPagedView) l.getAppsView().getContentView()).getCurrentPage() == WORK_PAGE