Merge "Fade away half-screen assistant on swipe-to-recents gesture." into ub-launcher3-master
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 6ef7828..c3ddfd6 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -71,7 +71,7 @@
android:clearTaskOnLaunch="true"
android:stateNotNeeded="true"
android:windowSoftInputMode="adjustPan"
- android:screenOrientation="nosensor"
+ android:screenOrientation="unspecified"
android:configChanges="keyboard|keyboardHidden|navigation"
android:resizeableActivity="true"
android:resumeWhilePausing="true"
diff --git a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
index 91f74a6..b97669b 100644
--- a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
@@ -16,6 +16,8 @@
package com.android.launcher3;
+import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
+import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
import static com.android.systemui.shared.recents.utilities.Utilities.getNextFrameNumber;
import static com.android.systemui.shared.recents.utilities.Utilities.getSurface;
@@ -35,6 +37,7 @@
import android.content.res.Resources;
import android.graphics.Matrix;
import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Handler;
import android.util.Log;
@@ -46,6 +49,7 @@
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.InsettableFrameLayout.LayoutParams;
import com.android.launcher3.allapps.AllAppsTransitionController;
+import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.PropertyListBuilder;
import com.android.launcher3.dragndrop.DragLayer;
@@ -225,7 +229,7 @@
// Resolve the opening task id
int openingTaskId = -1;
for (RemoteAnimationTargetCompat target : targets) {
- if (target.mode == RemoteAnimationTargetCompat.MODE_OPENING) {
+ if (target.mode == MODE_OPENING) {
openingTaskId = target.taskId;
break;
}
@@ -245,8 +249,36 @@
}
// Found a visible recents task that matches the opening app, lets launch the app from there
- return new LauncherTransitionAnimator(getRecentsLauncherAnimator(recentsView, taskView),
- getRecentsWindowAnimator(taskView, targets));
+ Animator launcherAnim;
+ AnimatorListenerAdapter windowAnimEndListener;
+ boolean launcherClosing = launcherIsATargetWithMode(targets, MODE_CLOSING);
+ if (launcherClosing) {
+ launcherAnim = getRecentsLauncherAnimator(recentsView, taskView);
+ windowAnimEndListener = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ // Make sure recents gets fixed up by resetting task alphas and scales, etc.
+ mLauncher.getStateManager().reapplyState();
+ }
+ };
+ } else {
+ AnimatorPlaybackController controller =
+ mLauncher.getStateManager()
+ .createAnimationToNewWorkspace(NORMAL, RECENTS_LAUNCH_DURATION);
+ controller.dispatchOnStart();
+ launcherAnim = controller.getAnimationPlayer().setDuration(RECENTS_LAUNCH_DURATION);
+ windowAnimEndListener = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mLauncher.getStateManager().goToState(NORMAL, false);
+ }
+ };
+ }
+
+ MutableBoolean skipLauncherChanges = new MutableBoolean(!launcherClosing);
+ Animator windowAnim = getRecentsWindowAnimator(taskView, skipLauncherChanges, targets);
+ windowAnim.addListener(windowAnimEndListener);
+ return new LauncherTransitionAnimator(launcherAnim, windowAnim, skipLauncherChanges);
}
/**
@@ -316,7 +348,7 @@
launcherAnimator.play(allAppsSlideOut);
Workspace workspace = mLauncher.getWorkspace();
- float[] workspaceScaleAndTranslation = LauncherState.NORMAL
+ float[] workspaceScaleAndTranslation = NORMAL
.getWorkspaceScaleAndTranslation(mLauncher);
Animator recenterWorkspace = LauncherAnimUtils.ofPropertyValuesHolder(
workspace, new PropertyListBuilder()
@@ -336,7 +368,7 @@
* @return Animator that controls the window of the opening targets for the recents launch
* animation.
*/
- private ValueAnimator getRecentsWindowAnimator(TaskView v,
+ private ValueAnimator getRecentsWindowAnimator(TaskView v, MutableBoolean skipLauncherChanges,
RemoteAnimationTargetCompat[] targets) {
Rect taskViewBounds = new Rect();
mDragLayer.getDescendantRectRelativeToSelf(v, taskViewBounds);
@@ -372,13 +404,15 @@
final float percent = animation.getAnimatedFraction();
TaskWindowBounds tw = recentsInterpolator.interpolate(percent);
- v.setScaleX(tw.taskScale);
- v.setScaleY(tw.taskScale);
- v.setTranslationX(tw.taskX);
- v.setTranslationY(tw.taskY);
- // Defer fading out the view until after the app window gets faded in
- v.setAlpha(getValue(1f, 0f, 75, 75,
- appAnimator.getDuration() * percent, Interpolators.LINEAR));
+ if (!skipLauncherChanges.value) {
+ v.setScaleX(tw.taskScale);
+ v.setScaleY(tw.taskScale);
+ v.setTranslationX(tw.taskX);
+ v.setTranslationY(tw.taskY);
+ // Defer fading out the view until after the app window gets faded in
+ v.setAlpha(getValue(1f, 0f, 75, 75,
+ appAnimator.getDuration() * percent, Interpolators.LINEAR));
+ }
matrix.setScale(tw.winScale, tw.winScale);
matrix.postTranslate(tw.winX, tw.winY);
@@ -400,7 +434,10 @@
matrix.postTranslate(target.position.x, target.position.y);
t.setMatrix(target.leash, matrix);
t.setWindowCrop(target.leash, crop);
- t.deferTransactionUntil(target.leash, surface, getNextFrameNumber(surface));
+
+ if (!skipLauncherChanges.value) {
+ t.deferTransactionUntil(target.leash, surface, frameNumber);
+ }
}
if (isFirstFrame) {
t.show(target.leash);
@@ -412,13 +449,6 @@
isFirstFrame = false;
}
});
- appAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- // Make sure recents gets fixed up by resetting task alphas and scales, etc.
- mLauncher.getStateManager().reapplyState();
- }
- });
return appAnimator;
}
@@ -520,8 +550,14 @@
: rect.left;
final int viewLocationTop = rect.top;
+ float startScale = 1f;
if (isBubbleTextView && !isDeepShortcutTextView) {
- ((BubbleTextView) v).getIconBounds(rect);
+ BubbleTextView btv = (BubbleTextView) v;
+ btv.getIconBounds(rect);
+ Drawable dr = btv.getIcon();
+ if (dr instanceof FastBitmapDrawable) {
+ startScale = ((FastBitmapDrawable) dr).getAnimatedScale();
+ }
} else {
rect.set(0, 0, rect.width(), rect.height());
}
@@ -563,14 +599,10 @@
float maxScaleX = mDeviceProfile.widthPx / (float) rect.width();
float maxScaleY = mDeviceProfile.heightPx / (float) rect.height();
float scale = Math.max(maxScaleX, maxScaleY);
- ObjectAnimator sX = ObjectAnimator.ofFloat(mFloatingView, View.SCALE_X, 1f, scale);
- ObjectAnimator sY = ObjectAnimator.ofFloat(mFloatingView, View.SCALE_Y, 1f, scale);
- sX.setDuration(500);
- sY.setDuration(500);
- sX.setInterpolator(Interpolators.EXAGGERATED_EASE);
- sY.setInterpolator(Interpolators.EXAGGERATED_EASE);
- appIconAnimatorSet.play(sX);
- appIconAnimatorSet.play(sY);
+ ObjectAnimator scaleAnim = ObjectAnimator
+ .ofFloat(mFloatingView, SCALE_PROPERTY, startScale, scale);
+ scaleAnim.setDuration(500).setInterpolator(Interpolators.EXAGGERATED_EASE);
+ appIconAnimatorSet.play(scaleAnim);
// Fade out the app icon.
ObjectAnimator alpha = ObjectAnimator.ofFloat(mFloatingView, View.ALPHA, 1f, 0f);
diff --git a/quickstep/src/com/android/launcher3/LauncherTransitionAnimator.java b/quickstep/src/com/android/launcher3/LauncherTransitionAnimator.java
index aec2869..ab9234b 100644
--- a/quickstep/src/com/android/launcher3/LauncherTransitionAnimator.java
+++ b/quickstep/src/com/android/launcher3/LauncherTransitionAnimator.java
@@ -27,11 +27,20 @@
*/
public class LauncherTransitionAnimator {
+ private final MutableBoolean mLauncherAnimCancelState;
+
private AnimatorSet mAnimatorSet;
private Animator mLauncherAnimator;
private Animator mWindowAnimator;
LauncherTransitionAnimator(Animator launcherAnimator, Animator windowAnimator) {
+ this(launcherAnimator, windowAnimator, new MutableBoolean(false));
+ }
+
+
+ LauncherTransitionAnimator(Animator launcherAnimator, Animator windowAnimator,
+ MutableBoolean launcherAnimCancelState) {
+ mLauncherAnimCancelState = launcherAnimCancelState;
if (launcherAnimator != null) {
mLauncherAnimator = launcherAnimator;
}
@@ -50,6 +59,7 @@
public void cancel() {
mAnimatorSet.cancel();
+ mLauncherAnimCancelState.value = true;
}
public boolean isRunning() {
@@ -58,6 +68,7 @@
public void finishLauncherAnimation() {
if (mLauncherAnimator != null) {
+ mLauncherAnimCancelState.value = true;
mLauncherAnimator.end();
}
}
diff --git a/quickstep/src/com/android/launcher3/MutableBoolean.java b/quickstep/src/com/android/launcher3/MutableBoolean.java
new file mode 100644
index 0000000..7538217
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/MutableBoolean.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2018 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;
+
+public class MutableBoolean {
+ public boolean value;
+
+ public MutableBoolean(boolean value) {
+ this.value = value;
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/EdgeSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/EdgeSwipeController.java
index 541c6bb..a55edfe 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/EdgeSwipeController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/EdgeSwipeController.java
@@ -31,7 +31,6 @@
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.anim.SpringAnimationHandler;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
@@ -143,11 +142,6 @@
}
@Override
- protected void initSprings() {
- mSpringHandlers = new SpringAnimationHandler[0];
- }
-
- @Override
protected float getShiftRange() {
return getShiftRange(mLauncher);
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
index 3f305e9..854fb4f 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
@@ -17,6 +17,7 @@
import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
import static com.android.launcher3.anim.Interpolators.ACCEL_2;
+import static com.android.launcher3.states.RotationHelper.REQUEST_ROTATE;
import android.graphics.Rect;
import android.view.View;
@@ -74,6 +75,11 @@
}
@Override
+ public void onStateTransitionEnd(Launcher launcher) {
+ launcher.getRotationHelper().setCurrentStateRequest(REQUEST_ROTATE);
+ }
+
+ @Override
public View getFinalFocus(Launcher launcher) {
return launcher.getOverviewPanel();
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
index 4542431..92d071a 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -83,7 +83,8 @@
// Scroll to the workspace card before changing to the NORMAL state.
int currPage = mRecentsView.getCurrentPage();
- if (toState == NORMAL && currPage != 0 && !config.userControlled) {
+ LauncherState fromState = mLauncher.getStateManager().getState();
+ if (fromState.overviewUi && toState == NORMAL && currPage != 0 && !config.userControlled) {
int maxSnapDuration = PagedView.SLOW_PAGE_SNAP_ANIMATION_DURATION;
int durationPerPage = maxSnapDuration / 10;
int snapDuration = Math.min(maxSnapDuration, durationPerPage * currPage);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java
index 2695054..c8d75dc 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java
@@ -19,7 +19,6 @@
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
-import static com.android.launcher3.anim.SpringAnimationHandler.Y_DIRECTION;
import static com.android.quickstep.TouchInteractionService.EDGE_NAV_BAR;
import android.animation.Animator;
@@ -27,7 +26,6 @@
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.support.animation.SpringAnimation;
import android.util.Log;
import android.view.MotionEvent;
@@ -38,11 +36,9 @@
import com.android.launcher3.LauncherStateManager.AnimationConfig;
import com.android.launcher3.LauncherStateManager.StateHandler;
import com.android.launcher3.Utilities;
-import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.AnimatorSetBuilder;
-import com.android.launcher3.anim.SpringAnimationHandler;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.touch.SwipeDetector;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
@@ -52,8 +48,6 @@
import com.android.launcher3.util.TouchController;
import com.android.quickstep.TouchInteractionService;
-import java.util.ArrayList;
-
/**
* Handles vertical touch gesture on the DragLayer
*/
@@ -112,8 +106,6 @@
// Ratio of transition process [0, 1] to drag displacement (px)
private float mProgressMultiplier;
- private SpringAnimationHandler[] mSpringHandlers;
-
public TwoStepSwipeController(Launcher l) {
mLauncher = l;
mDetector = new SwipeDetector(l, this, SwipeDetector.VERTICAL);
@@ -156,29 +148,6 @@
}
}
- private void initSprings() {
- AllAppsContainerView appsView = mLauncher.getAppsView();
-
- SpringAnimationHandler handler = appsView.getSpringAnimationHandler();
- if (handler == null) {
- mSpringHandlers = new SpringAnimationHandler[0];
- return;
- }
-
- ArrayList<SpringAnimationHandler> handlers = new ArrayList<>();
- handlers.add(handler);
-
- SpringAnimation searchSpring = appsView.getSearchUiManager().getSpringForFling();
- if (searchSpring != null) {
- SpringAnimationHandler searchHandler =
- new SpringAnimationHandler(Y_DIRECTION, handler.getFactory());
- searchHandler.add(searchSpring, true /* setDefaultValues */);
- handlers.add(searchHandler);
- }
-
- mSpringHandlers = handlers.toArray(new SpringAnimationHandler[handlers.size()]);
- }
-
@Override
public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
@@ -214,10 +183,6 @@
mDetector.setDetectableScrollConditions(
directionsToDetectScroll, ignoreSlopWhenSettling);
-
- if (mSpringHandlers == null) {
- initSprings();
- }
}
if (mNoIntercept) {
@@ -230,9 +195,6 @@
@Override
public boolean onControllerTouchEvent(MotionEvent ev) {
- for (SpringAnimationHandler h : mSpringHandlers) {
- h.addMovement(ev);
- }
return mDetector.onTouchEvent(ev);
}
@@ -283,10 +245,6 @@
mDragPauseDetector.clearDisabledFlags(FLAG_OVERVIEW_DISABLED_FLING);
updatePauseDetectorRangeFlag();
}
-
- for (SpringAnimationHandler h : mSpringHandlers) {
- h.skipToEnd();
- }
}
private float getShiftRange() {
@@ -329,13 +287,6 @@
targetState = (progress > SUCCESS_TRANSITION_PROGRESS) ? mToState : mFromState;
}
- if (fling && targetState == ALL_APPS) {
- for (SpringAnimationHandler h : mSpringHandlers) {
- // The icons are moving upwards, so we go to 0 from 1. (y-axis 1 is below 0.)
- h.animateToFinalPosition(0 /* pos */, 1 /* startValue */);
- }
- }
-
float endProgress;
if (mDragPauseDetector.isTriggered() && targetState == NORMAL) {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
index 7f98935..a8bcb11 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
@@ -18,28 +18,25 @@
import static com.android.launcher3.LauncherState.NORMAL;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
import android.graphics.PointF;
+import android.view.View;
import android.view.View.AccessibilityDelegate;
+import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherStateManager.StateHandler;
import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.graphics.BitmapRenderer;
+import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.util.TouchController;
import com.android.quickstep.OverviewInteractionState;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.RecentsView;
-import com.android.systemui.shared.recents.view.RecentsTransition;
public class UiFactory {
private static final String CONTROL_REMOTE_APP_TRANSITION_PERMISSION =
"android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS";
- public static final boolean USE_HARDWARE_BITMAP = false; // FeatureFlags.IS_DOGFOOD_BUILD;
-
public static TouchController[] createTouchControllers(Launcher launcher) {
if (FeatureFlags.ENABLE_TWO_SWIPE_TARGETS) {
return new TouchController[] {
@@ -68,19 +65,21 @@
}
public static void onLauncherStateOrFocusChanged(Launcher launcher) {
- OverviewInteractionState.setBackButtonVisible(launcher, launcher == null
- || !launcher.isInState(NORMAL) || !launcher.hasWindowFocus());
- }
-
- public static Bitmap createFromRenderer(int width, int height, boolean forceSoftwareRenderer,
- BitmapRenderer renderer) {
- if (USE_HARDWARE_BITMAP && !forceSoftwareRenderer) {
- return RecentsTransition.createHardwareBitmap(width, height, renderer::render);
- } else {
- Bitmap result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- renderer.render(new Canvas(result));
- return result;
+ boolean shouldBackButtonBeVisible = launcher == null
+ || !launcher.isInState(NORMAL)
+ || !launcher.hasWindowFocus();
+ if (!shouldBackButtonBeVisible) {
+ // Show the back button if there is a floating view visible.
+ DragLayer dragLayer = launcher.getDragLayer();
+ for (int i = dragLayer.getChildCount() - 1; i >= 0; i--) {
+ View child = dragLayer.getChildAt(i);
+ if (child instanceof AbstractFloatingView) {
+ shouldBackButtonBeVisible = true;
+ break;
+ }
+ }
}
+ OverviewInteractionState.setBackButtonVisible(launcher, shouldBackButtonBeVisible);
}
public static void resetOverview(Launcher launcher) {
diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
index 67b36b3..ede0ad2 100644
--- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -19,6 +19,8 @@
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.states.RotationHelper.REQUEST_LOCK;
+import static com.android.launcher3.states.RotationHelper.REQUEST_NONE;
import static com.android.quickstep.QuickScrubController.QUICK_SWITCH_START_DURATION;
import static com.android.quickstep.TouchConsumer.INTERACTION_NORMAL;
import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SCRUB;
@@ -33,7 +35,6 @@
import android.annotation.TargetApi;
import android.app.ActivityManager.RunningTaskInfo;
import android.content.Context;
-import android.content.pm.ActivityInfo;
import android.content.res.Resources;
import android.graphics.Matrix;
import android.graphics.Matrix.ScaleToFit;
@@ -234,7 +235,9 @@
mContext = context;
mRunningTaskId = runningTaskInfo.id;
mTouchTimeMs = touchTimeMs;
- mInputConsumer.registerInputConsumer();
+ // Register the input consumer on the UI thread, to ensure that it runs after any pending
+ // unregister calls
+ mMainExecutor.execute(mInputConsumer::registerInputConsumer);
initStateCallbacks();
}
@@ -364,7 +367,8 @@
// For the duration of the gesture, lock the screen orientation to ensure that we do not
// rotate mid-quickscrub
- mLauncher.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);
+ mLauncher.getRotationHelper().setStateHandlerRequest(REQUEST_LOCK);
+
mRecentsView = mLauncher.getOverviewPanel();
mQuickScrubController = mRecentsView.getQuickScrubController();
mLauncherLayoutListener = new LauncherLayoutListener(mLauncher);
@@ -760,7 +764,8 @@
mLauncherLayoutListener.close(false);
// Restore the requested orientation to the user preference after the gesture has ended
- mLauncher.updateRequestedOrientation();
+ mLauncher.getRotationHelper().setStateHandlerRequest(REQUEST_NONE);
+
mRecentsView.setFirstTaskIconScaledDown(false /* isScaledDown */, false /* animate */);
}
diff --git a/res/drawable/all_apps_search_divider.xml b/res/drawable/bg_all_apps_searchbox.xml
similarity index 78%
rename from res/drawable/all_apps_search_divider.xml
rename to res/drawable/bg_all_apps_searchbox.xml
index 99905e4..c324927 100644
--- a/res/drawable/all_apps_search_divider.xml
+++ b/res/drawable/bg_all_apps_searchbox.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
+<!-- Copyright (C) 2018 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.
@@ -13,8 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle">
- <solid android:color="?android:attr/colorAccent" />
- <size android:height="1dp" />
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
+ <solid android:color="?attr/popupColorPrimary" />
+ <corners android:radius="2dp" />
</shape>
\ No newline at end of file
diff --git a/res/layout/all_apps.xml b/res/layout/all_apps.xml
index 2ce6b8c..450d107 100644
--- a/res/layout/all_apps.xml
+++ b/res/layout/all_apps.xml
@@ -29,20 +29,13 @@
<include layout="@layout/all_apps_rv_layout" />
- <include layout="@layout/all_apps_fast_scroller" />
-
<include layout="@layout/all_apps_floating_header" />
<!-- Note: we are reusing/repurposing a system attribute for search layout, because of a
platform bug, which prevents using custom attributes in <include> tag -->
<include
android:id="@id/search_container_all_apps"
- layout="?android:attr/keyboardLayout"/>
+ layout="@layout/search_container_all_apps"/>
- <View
- android:id="@+id/nav_bar_bg"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_alignParentBottom="true"
- android:background="?attr/allAppsNavBarScrimColor" />
+ <include layout="@layout/all_apps_fast_scroller" />
</com.android.launcher3.allapps.AllAppsContainerView>
\ No newline at end of file
diff --git a/res/layout/all_apps_rv_layout.xml b/res/layout/all_apps_rv_layout.xml
index 3c19f8c..c353b36 100644
--- a/res/layout/all_apps_rv_layout.xml
+++ b/res/layout/all_apps_rv_layout.xml
@@ -22,5 +22,4 @@
android:layout_below="@id/search_container_all_apps"
android:clipToPadding="false"
android:descendantFocusability="afterDescendants"
- android:focusable="true"
- android:overScrollMode="never" />
+ android:focusable="true" />
diff --git a/res/layout/deep_shortcut.xml b/res/layout/deep_shortcut.xml
index 4a3db1f..4a2ad42 100644
--- a/res/layout/deep_shortcut.xml
+++ b/res/layout/deep_shortcut.xml
@@ -35,7 +35,6 @@
android:textColor="?android:attr/textColorPrimary"
android:fontFamily="sans-serif"
launcher:layoutHorizontal="true"
- launcher:deferShadowGeneration="true"
launcher:iconDisplay="shortcut_popup"
launcher:iconSizeOverride="@dimen/deep_shortcut_icon_size" />
diff --git a/res/layout/search_container_all_apps.xml b/res/layout/search_container_all_apps.xml
index fc07002..14d7b53 100644
--- a/res/layout/search_container_all_apps.xml
+++ b/res/layout/search_container_all_apps.xml
@@ -17,53 +17,23 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@id/search_container_all_apps"
android:layout_width="match_parent"
- android:layout_height="@dimen/all_apps_search_bar_height"
- android:layout_gravity="center|top"
- android:layout_marginBottom="-8dp"
- android:gravity="center|bottom"
- android:paddingLeft="@dimen/dynamic_grid_edge_margin"
- android:paddingRight="@dimen/dynamic_grid_edge_margin"
- android:saveEnabled="false" >
-
- <!--
- Note: The following relation should always be true so that the shadows are aligned properly
- search_box_input.layout_marginBottom
- == search_divider.layout_marginBottom
- == - (search_container.layout_marginBottom)
- >= 5dp
- -->
- <com.android.launcher3.ExtendedEditText
- android:id="@+id/search_box_input"
- android:layout_width="match_parent"
- android:layout_height="@dimen/all_apps_search_bar_field_height"
- android:layout_gravity="bottom"
- android:layout_marginBottom="8dp"
- android:background="@android:color/transparent"
- android:focusableInTouchMode="true"
- android:gravity="center"
- android:hint="@string/all_apps_search_bar_hint"
- android:imeOptions="actionSearch|flagNoExtractUi"
- android:inputType="text|textNoSuggestions|textCapWords"
- android:maxLines="1"
- android:saveEnabled="false"
- android:scrollHorizontally="true"
- android:singleLine="true"
- android:textColor="?android:attr/textColorSecondary"
- android:textColorHint="@drawable/all_apps_search_hint"
- android:textSize="16sp" />
-
- <!-- This needs to be a container with a view, to simulate a scrolling effect for the header.
- We translate the header down, and its content up by the same amount, so that it gets
- clipped by the parent, making it look like the divider was scrolled out of the view. -->
- <FrameLayout
- android:id="@+id/search_divider"
- android:layout_width="match_parent"
- android:layout_height="1dp"
- android:layout_gravity="bottom"
- android:layout_marginBottom="8dp" >
- <View
- android:layout_width="match_parent"
- android:layout_height="1dp"
- android:background="@drawable/all_apps_search_divider"/>
- </FrameLayout>
-</com.android.launcher3.allapps.search.AppsSearchContainerLayout>
\ No newline at end of file
+ android:layout_height="@dimen/all_apps_search_bar_field_height"
+ android:layout_centerHorizontal="true"
+ android:layout_gravity="top|center_horizontal"
+ android:layout_marginTop="8dp"
+ android:background="@drawable/bg_all_apps_searchbox"
+ android:elevation="1dp"
+ android:focusableInTouchMode="true"
+ android:gravity="center"
+ android:hint="@string/all_apps_search_bar_hint"
+ android:imeOptions="actionSearch|flagNoExtractUi"
+ android:inputType="text|textNoSuggestions|textCapWords"
+ android:maxLines="1"
+ android:padding="8dp"
+ android:saveEnabled="false"
+ android:scrollHorizontally="true"
+ android:singleLine="true"
+ android:textColor="?android:attr/textColorSecondary"
+ android:textColorHint="@drawable/all_apps_search_hint"
+ android:textSize="16sp"
+ android:translationY="24dp" />
\ No newline at end of file
diff --git a/res/layout/system_shortcut.xml b/res/layout/system_shortcut.xml
index 1888e22..04f3d02 100644
--- a/res/layout/system_shortcut.xml
+++ b/res/layout/system_shortcut.xml
@@ -34,7 +34,6 @@
android:fontFamily="sans-serif"
launcher:iconDisplay="shortcut_popup"
launcher:layoutHorizontal="true"
- launcher:deferShadowGeneration="true"
android:focusable="false" />
<View
diff --git a/res/layout/widgets_full_sheet.xml b/res/layout/widgets_full_sheet.xml
index ca397bd..978b5a1 100644
--- a/res/layout/widgets_full_sheet.xml
+++ b/res/layout/widgets_full_sheet.xml
@@ -47,13 +47,5 @@
android:layout_height="match_parent"
android:layout_gravity="end"
android:layout_marginEnd="@dimen/fastscroll_end_margin" />
-
- <View
- android:id="@+id/nav_bar_bg"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_gravity="bottom"
- android:background="?attr/allAppsNavBarScrimColor"
- android:focusable="false" />
</com.android.launcher3.views.TopRoundedCornerView>
</com.android.launcher3.widget.WidgetsFullSheet>
\ No newline at end of file
diff --git a/res/layout/widgets_list_row_view.xml b/res/layout/widgets_list_row_view.xml
index 4cd03ce..91baf7a 100644
--- a/res/layout/widgets_list_row_view.xml
+++ b/res/layout/widgets_list_row_view.xml
@@ -42,7 +42,6 @@
android:textColor="?android:attr/textColorPrimary"
android:textSize="16sp"
android:textAlignment="viewStart"
- launcher:deferShadowGeneration="true"
launcher:iconDisplay="widget_section"
launcher:iconSizeOverride="@dimen/widget_section_icon_size"
launcher:layoutHorizontal="true" />
diff --git a/res/values-sw720dp/styles.xml b/res/values-sw720dp/styles.xml
index 72894dc..f322e9f 100644
--- a/res/values-sw720dp/styles.xml
+++ b/res/values-sw720dp/styles.xml
@@ -26,7 +26,6 @@
<item name="android:windowNoTitle">true</item>
<item name="android:windowActionModeOverlay">true</item>
<item name="android:colorEdgeEffect">?android:attr/textColorSecondary</item>
- <item name="android:keyboardLayout">@layout/search_container_all_apps</item>
</style>
<!-- Workspace -->
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 1351dfa..64ca05e 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -44,7 +44,6 @@
<enum name="widget_section" value="3" />
<enum name="shortcut_popup" value="4" />
</attr>
- <attr name="deferShadowGeneration" format="boolean" />
<attr name="centerVertically" format="boolean" />
</declare-styleable>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 1f46844..9f4233c 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -79,7 +79,7 @@
<!-- All Apps -->
<dimen name="all_apps_button_scale_down">0dp</dimen>
<dimen name="all_apps_search_bar_field_height">48dp</dimen>
- <dimen name="all_apps_search_bar_height">60dp</dimen>
+ <dimen name="all_apps_search_bar_bottom_padding">30dp</dimen>
<dimen name="all_apps_empty_search_message_top_offset">40dp</dimen>
<dimen name="all_apps_empty_search_bg_top_offset">144dp</dimen>
<dimen name="all_apps_background_canvas_width">700dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index d6abe22..d8b68e7 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -173,12 +173,6 @@
<string name="accessibility_action_overview">Overview</string>
<!-- Strings for settings -->
- <!-- Title for Allow Rotation setting. [CHAR LIMIT=50] -->
- <string name="allow_rotation_title">Allow Home screen rotation</string>
- <!-- Text explaining when the home screen will get rotated. [CHAR LIMIT=100] -->
- <string name="allow_rotation_desc">When phone is rotated</string>
- <!-- Text explaining that rotation is disabled in Display settings. 'Display' refers to the Display section in system settings [CHAR LIMIT=100] -->
- <string name="allow_rotation_blocked_desc">Current Display setting doesn\'t permit rotation</string>
<!-- Title for Notification dots setting. Tapping this will link to the system Notifications settings screen where the user can turn off notification dots globally. [CHAR LIMIT=50] -->
<string name="icon_badging_title">Notification dots</string>
<!-- Text to indicate that the system icon badging setting is on [CHAR LIMIT=100] -->
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 8cc4743..ac6a6b1 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -25,7 +25,6 @@
<item name="android:windowShowWallpaper">true</item>
<item name="android:windowNoTitle">true</item>
<item name="android:colorEdgeEffect">#FF757575</item>
- <item name="android:keyboardLayout">@layout/search_container_all_apps</item>
</style>
<style name="BaseLauncherThemeWithCustomAttrs" parent="@style/BaseLauncherTheme">
diff --git a/res/xml/launcher_preferences.xml b/res/xml/launcher_preferences.xml
index 28a35b8..7bb19f3 100644
--- a/res/xml/launcher_preferences.xml
+++ b/res/xml/launcher_preferences.xml
@@ -37,13 +37,6 @@
android:persistent="true"
/>
- <SwitchPreference
- android:key="pref_allowRotation"
- android:title="@string/allow_rotation_title"
- android:defaultValue="@bool/allow_rotation"
- android:persistent="true"
- />
-
<ListPreference
android:key="pref_override_icon_shape"
android:title="@string/icon_shape_override_label"
diff --git a/src/com/android/launcher3/BaseRecyclerView.java b/src/com/android/launcher3/BaseRecyclerView.java
index cc13263..74b9cfa 100644
--- a/src/com/android/launcher3/BaseRecyclerView.java
+++ b/src/com/android/launcher3/BaseRecyclerView.java
@@ -33,8 +33,7 @@
* <li> Enable fast scroller.
* </ul>
*/
-public abstract class BaseRecyclerView extends RecyclerView
- implements RecyclerView.OnItemTouchListener {
+public abstract class BaseRecyclerView extends RecyclerView {
protected RecyclerViewFastScroller mScrollbar;
@@ -51,12 +50,6 @@
}
@Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- addOnItemTouchListener(this);
- }
-
- @Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
bindFastScrollbar();
@@ -69,40 +62,8 @@
onUpdateScrollbar(0);
}
- /**
- * We intercept the touch handling only to support fast scrolling when initiated from the
- * scroll bar. Otherwise, we fall back to the default RecyclerView touch handling.
- */
- @Override
- public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent ev) {
- return handleTouchEvent(ev);
- }
-
- @Override
- public void onTouchEvent(RecyclerView rv, MotionEvent ev) {
- handleTouchEvent(ev);
- }
-
- /**
- * Handles the touch event and determines whether to show the fast scroller (or updates it if
- * it is already showing).
- */
- private boolean handleTouchEvent(MotionEvent ev) {
- // Move to mScrollbar's coordinate system.
- // We need to take parent into account (view pager's location)
- ViewGroup parent = (ViewGroup) getParent();
- int left = parent.getLeft() - mScrollbar.getLeft();
- int top = parent.getTop() + getTop() - mScrollbar.getTop() - getScrollBarTop();
- ev.offsetLocation(left, top);
- try {
- return mScrollbar.handleTouchEvent(ev);
- } finally {
- ev.offsetLocation(-left, -top);
- }
- }
-
- public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
- // DO NOT REMOVE, NEEDED IMPLEMENTATION FOR M BUILDS
+ public RecyclerViewFastScroller getScrollbar() {
+ return mScrollbar;
}
public int getScrollBarTop() {
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 4f50d1a..8b6d9f8 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -20,7 +20,6 @@
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
-import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
@@ -37,7 +36,6 @@
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewDebug;
-import android.view.ViewParent;
import android.widget.TextView;
import com.android.launcher3.IconCache.IconLoadRequest;
@@ -48,7 +46,6 @@
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.folder.FolderIconPreviewVerifier;
import com.android.launcher3.graphics.DrawableFactory;
-import com.android.launcher3.graphics.HolographicOutlineHelper;
import com.android.launcher3.graphics.IconPalette;
import com.android.launcher3.graphics.PreloadIconDrawable;
import com.android.launcher3.model.PackageItemInfo;
@@ -73,13 +70,9 @@
private final boolean mCenterVertically;
private final CheckLongPressHelper mLongPressHelper;
- private final HolographicOutlineHelper mOutlineHelper;
private final StylusEventHelper mStylusEventHelper;
private final float mSlop;
- private Bitmap mPressedBackground;
-
- private final boolean mDeferShadowGenerationOnTouch;
private final boolean mLayoutHorizontal;
private final int mIconSize;
@ViewDebug.ExportedProperty(category = "launcher")
@@ -147,8 +140,6 @@
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.BubbleTextView, defStyle, 0);
mLayoutHorizontal = a.getBoolean(R.styleable.BubbleTextView_layoutHorizontal, false);
- mDeferShadowGenerationOnTouch =
- a.getBoolean(R.styleable.BubbleTextView_deferShadowGeneration, false);
int display = a.getInteger(R.styleable.BubbleTextView_iconDisplay, DISPLAY_WORKSPACE);
int defaultIconSize = grid.iconSizePx;
@@ -173,7 +164,6 @@
mLongPressHelper = new CheckLongPressHelper(this);
mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this);
- mOutlineHelper = HolographicOutlineHelper.getInstance(getContext());
setAccessibilityDelegate(mLauncher.getAccessibilityDelegate());
}
@@ -290,13 +280,6 @@
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
- // So that the pressed outline is visible immediately on setStayPressed(),
- // we pre-create it on ACTION_DOWN (it takes a small but perceptible amount of time
- // to create it)
- if (!mDeferShadowGenerationOnTouch && mPressedBackground == null) {
- mPressedBackground = mOutlineHelper.createMediumDropShadow(this);
- }
-
// If we're in a stylus button press, don't check for long press.
if (!mStylusEventHelper.inStylusButtonPressed()) {
mLongPressHelper.postCheckForLongPress();
@@ -304,12 +287,6 @@
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
- // If we've touched down and up on an item, and it's still not "pressed", then
- // destroy the pressed outline
- if (!isPressed()) {
- mPressedBackground = null;
- }
-
mLongPressHelper.cancelLongPress();
break;
case MotionEvent.ACTION_MOVE:
@@ -323,22 +300,6 @@
void setStayPressed(boolean stayPressed) {
mStayPressed = stayPressed;
- if (!stayPressed) {
- HolographicOutlineHelper.getInstance(getContext()).recycleShadowBitmap(mPressedBackground);
- mPressedBackground = null;
- } else {
- if (mPressedBackground == null) {
- mPressedBackground = mOutlineHelper.createMediumDropShadow(this);
- }
- }
-
- // Only show the shadow effect when persistent pressed state is set.
- ViewParent parent = getParent();
- if (parent != null && parent.getParent() instanceof BubbleTextShadowHandler) {
- ((BubbleTextShadowHandler) parent.getParent()).setPressedIcon(
- this, mPressedBackground);
- }
-
refreshDrawableState();
}
@@ -355,26 +316,12 @@
}
@Override
- public boolean onKeyDown(int keyCode, KeyEvent event) {
- if (super.onKeyDown(keyCode, event)) {
- // Pre-create shadow so show immediately on click.
- if (mPressedBackground == null) {
- mPressedBackground = mOutlineHelper.createMediumDropShadow(this);
- }
- return true;
- }
- return false;
- }
-
- @Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
// Unlike touch events, keypress event propagate pressed state change immediately,
// without waiting for onClickHandler to execute. Disable pressed state changes here
// to avoid flickering.
mIgnorePressedStateChange = true;
boolean result = super.onKeyUp(keyCode, event);
-
- mPressedBackground = null;
mIgnorePressedStateChange = false;
refreshDrawableState();
return result;
@@ -662,11 +609,4 @@
public int getIconSize() {
return mIconSize;
}
-
- /**
- * Interface to be implemented by the grand parent to allow click shadow effect.
- */
- public interface BubbleTextShadowHandler {
- void setPressedIcon(BubbleTextView icon, Bitmap background);
- }
}
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 5e4f670..734aec3 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -21,6 +21,7 @@
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
@@ -45,7 +46,6 @@
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
-import com.android.launcher3.BubbleTextView.BubbleTextShadowHandler;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.accessibility.DragAndDropAccessibilityDelegate;
import com.android.launcher3.accessibility.FolderAccessibilityHelper;
@@ -70,7 +70,7 @@
import java.util.Comparator;
import java.util.Stack;
-public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
+public class CellLayout extends ViewGroup {
public static final int WORKSPACE_ACCESSIBILITY_DRAG = 2;
public static final int FOLDER_ACCESSIBILITY_DRAG = 1;
@@ -128,8 +128,6 @@
private int mDragOutlineCurrent = 0;
private final Paint mDragOutlinePaint = new Paint();
- private final ClickShadowView mTouchFeedbackView;
-
@Thunk final ArrayMap<LayoutParams, Animator> mReorderAnimators = new ArrayMap<>();
@Thunk final ArrayMap<View, ReorderPreviewAnimation> mShakeAnimators = new ArrayMap<>();
@@ -285,9 +283,6 @@
mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mCountX, mCountY);
mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this);
-
- mTouchFeedbackView = new ClickShadowView(context);
- addView(mTouchFeedbackView);
addView(mShortcutsAndWidgets);
}
@@ -384,11 +379,6 @@
return mDropPending;
}
- @Override
- public void setPressedIcon(BubbleTextView icon, Bitmap background) {
- mTouchFeedbackView.setPressedIcon(icon, background);
- }
-
void setIsDragOverlapping(boolean isDragOverlapping) {
if (mIsDragOverlapping != isDragOverlapping) {
mIsDragOverlapping = isDragOverlapping;
@@ -785,13 +775,6 @@
throw new RuntimeException("CellLayout cannot have UNSPECIFIED dimensions");
}
- // Make the feedback view large enough to hold the blur bitmap.
- mTouchFeedbackView.measure(
- MeasureSpec.makeMeasureSpec(mCellWidth + mTouchFeedbackView.getExtraSize(),
- MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(mCellHeight + mTouchFeedbackView.getExtraSize(),
- MeasureSpec.EXACTLY));
-
mShortcutsAndWidgets.measure(
MeasureSpec.makeMeasureSpec(newWidth, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(newHeight, MeasureSpec.EXACTLY));
@@ -815,11 +798,7 @@
int top = getPaddingTop();
int bottom = b - t - getPaddingBottom();
- mTouchFeedbackView.layout(left, top,
- left + mTouchFeedbackView.getMeasuredWidth(),
- top + mTouchFeedbackView.getMeasuredHeight());
mShortcutsAndWidgets.layout(left, top, right, bottom);
-
// Expand the background drawing bounds by the padding baked into the background drawable
mBackground.getPadding(mTempRect);
mBackground.setBounds(
@@ -1012,6 +991,7 @@
}
}
+ @SuppressLint("StringFormatMatches")
public String getItemMoveDescription(int cellX, int cellY) {
if (mContainerType == HOTSEAT) {
return getContext().getString(R.string.move_to_hotseat_position,
diff --git a/src/com/android/launcher3/ClickShadowView.java b/src/com/android/launcher3/ClickShadowView.java
deleted file mode 100644
index 5391b4d..0000000
--- a/src/com/android/launcher3/ClickShadowView.java
+++ /dev/null
@@ -1,209 +0,0 @@
-/*
- * Copyright (C) 2014 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;
-
-import static com.android.launcher3.FastBitmapDrawable.CLICK_FEEDBACK_DURATION;
-import static com.android.launcher3.FastBitmapDrawable.CLICK_FEEDBACK_INTERPOLATOR;
-import static com.android.launcher3.LauncherAnimUtils.ELEVATION;
-import static com.android.launcher3.graphics.HolographicOutlineHelper.ADAPTIVE_ICON_SHADOW_BITMAP;
-
-import android.animation.ObjectAnimator;
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Outline;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.graphics.drawable.AdaptiveIconDrawable;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.util.Property;
-import android.view.View;
-import android.view.ViewDebug;
-import android.view.ViewGroup;
-import android.view.ViewOutlineProvider;
-
-public class ClickShadowView extends View {
-
- private static final int SHADOW_SIZE_FACTOR = 3;
- private static final int SHADOW_LOW_ALPHA = 30;
- private static final int SHADOW_HIGH_ALPHA = 60;
-
- private static float sAdaptiveIconScaleFactor = 1f;
-
- private final Paint mPaint;
-
- @ViewDebug.ExportedProperty(category = "launcher")
- private final float mShadowOffset;
- @ViewDebug.ExportedProperty(category = "launcher")
- private final float mShadowPadding;
-
- private Bitmap mBitmap;
- private ObjectAnimator mAnim;
-
- private Drawable mAdaptiveIcon;
- private ViewOutlineProvider mOutlineProvider;
-
- public ClickShadowView(Context context) {
- super(context);
- mPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
- mPaint.setColor(Color.BLACK);
-
- mShadowPadding = getResources().getDimension(R.dimen.blur_size_click_shadow);
- mShadowOffset = getResources().getDimension(R.dimen.click_shadow_high_shift);
- }
-
- public static void setAdaptiveIconScaleFactor(float factor) {
- sAdaptiveIconScaleFactor = factor;
- }
-
- /**
- * @return extra space required by the view to show the shadow.
- */
- public int getExtraSize() {
- return (int) (SHADOW_SIZE_FACTOR * mShadowPadding);
- }
-
- public void setPressedIcon(BubbleTextView icon, Bitmap background) {
- if (icon == null) {
- setBitmap(null);
- cancelAnim();
- return;
- }
- if (background == null) {
- if (mBitmap == ADAPTIVE_ICON_SHADOW_BITMAP) {
- // clear animation shadow
- }
- setBitmap(null);
- cancelAnim();
- icon.setOutlineProvider(null);
- } else if (setBitmap(background)) {
- if (mBitmap == ADAPTIVE_ICON_SHADOW_BITMAP) {
- setupAdaptiveShadow(icon);
- cancelAnim();
- startAnim(icon, ELEVATION,
- getResources().getDimension(R.dimen.click_shadow_elevation));
- } else {
- alignWithIconView(icon);
- startAnim(this, ALPHA, 1);
- }
- }
- }
-
- @TargetApi(Build.VERSION_CODES.O)
- private void setupAdaptiveShadow(final BubbleTextView view) {
- if (mAdaptiveIcon == null) {
- mAdaptiveIcon = new AdaptiveIconDrawable(null, null);
- mOutlineProvider = new ViewOutlineProvider() {
- @Override
- public void getOutline(View view, Outline outline) {
- mAdaptiveIcon.getOutline(outline);
- }
- };
- }
-
- int iconWidth = view.getRight() - view.getLeft();
- int iconHSpace = iconWidth - view.getCompoundPaddingRight() - view.getCompoundPaddingLeft();
- int drawableWidth = view.getIcon().getBounds().width();
-
- Rect bounds = new Rect();
- bounds.left = view.getCompoundPaddingLeft() + (iconHSpace - drawableWidth) / 2;
- bounds.right = bounds.left + drawableWidth;
- bounds.top = view.getPaddingTop();
- bounds.bottom = bounds.top + view.getIcon().getBounds().height();
- Utilities.scaleRectAboutCenter(bounds, sAdaptiveIconScaleFactor);
-
- mAdaptiveIcon.setBounds(bounds);
- view.setOutlineProvider(mOutlineProvider);
- }
-
- /**
- * Applies the new bitmap.
- * @return true if the view was invalidated.
- */
- private boolean setBitmap(Bitmap b) {
- if (b != mBitmap){
- mBitmap = b;
- invalidate();
- return true;
- }
- return false;
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- if (mBitmap != null) {
- mPaint.setAlpha(SHADOW_LOW_ALPHA);
- canvas.drawBitmap(mBitmap, 0, 0, mPaint);
- mPaint.setAlpha(SHADOW_HIGH_ALPHA);
- canvas.drawBitmap(mBitmap, 0, mShadowOffset, mPaint);
- }
- }
-
- private void cancelAnim() {
- if (mAnim != null) {
- mAnim.cancel();
- mAnim.setCurrentPlayTime(0);
- mAnim = null;
- }
- }
-
- private void startAnim(View target, Property<View, Float> property, float endValue) {
- cancelAnim();
- property.set(target, 0f);
- mAnim = ObjectAnimator.ofFloat(target, property, endValue);
- mAnim.setDuration(CLICK_FEEDBACK_DURATION)
- .setInterpolator(CLICK_FEEDBACK_INTERPOLATOR);
- mAnim.start();
- }
-
- /**
- * Aligns the shadow with {@param view}
- * Note: {@param view} must be a descendant of my parent.
- */
- private void alignWithIconView(BubbleTextView view) {
- int[] coords = new int[] {0, 0};
- Utilities.getDescendantCoordRelativeToAncestor(
- (ViewGroup) view.getParent(), (View) getParent(), coords, false);
-
- float leftShift = view.getLeft() + coords[0] - getLeft();
- float topShift = view.getTop() + coords[1] - getTop();
- int iconWidth = view.getRight() - view.getLeft();
- int iconHeight = view.getBottom() - view.getTop();
- int iconHSpace = iconWidth - view.getCompoundPaddingRight() - view.getCompoundPaddingLeft();
- float drawableWidth = view.getIcon().getBounds().width();
-
- // Set the bounds to clip against
- int clipLeft = (int) Math.max(0, coords[0] - leftShift - mShadowPadding);
- int clipTop = (int) Math.max(0, coords[1] - topShift - mShadowPadding) ;
- setClipBounds(new Rect(clipLeft, clipTop, clipLeft + iconWidth, clipTop + iconHeight));
-
- setTranslationX(leftShift
- + view.getCompoundPaddingLeft() * view.getScaleX()
- + (iconHSpace - drawableWidth) * view.getScaleX() / 2 /* drawable gap */
- + iconWidth * (1 - view.getScaleX()) / 2 /* gap due to scale */
- - mShadowPadding /* extra shadow size */
- );
- setTranslationY(topShift
- + view.getPaddingTop() * view.getScaleY() /* drawable gap */
- + view.getHeight() * (1 - view.getScaleY()) / 2 /* gap due to scale */
- - mShadowPadding /* extra shadow size */
- );
- }
-}
diff --git a/src/com/android/launcher3/FastBitmapDrawable.java b/src/com/android/launcher3/FastBitmapDrawable.java
index c4ec8c9..5354edf 100644
--- a/src/com/android/launcher3/FastBitmapDrawable.java
+++ b/src/com/android/launcher3/FastBitmapDrawable.java
@@ -16,8 +16,9 @@
package com.android.launcher3;
+import static com.android.launcher3.anim.Interpolators.ACCEL;
+
import android.animation.ObjectAnimator;
-import android.animation.TimeInterpolator;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -28,9 +29,8 @@
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
+import android.graphics.Rect;
import android.graphics.drawable.Drawable;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
import android.util.Property;
import android.util.SparseArray;
@@ -38,14 +38,12 @@
public class FastBitmapDrawable extends Drawable {
- private static final float PRESSED_BRIGHTNESS = 100f / 255f;
+ private static final float PRESSED_SCALE = 1.1f;
+
private static final float DISABLED_DESATURATION = 1f;
private static final float DISABLED_BRIGHTNESS = 0.5f;
- public static final TimeInterpolator CLICK_FEEDBACK_INTERPOLATOR = (input) ->
- (input < 0.05f) ? (input / 0.05f) : ((input < 0.3f) ? 1 : (1 - input) / 0.7f);
-
- public static final int CLICK_FEEDBACK_DURATION = 2000;
+ public static final int CLICK_FEEDBACK_DURATION = 200;
// Since we don't need 256^2 values for combinations of both the brightness and saturation, we
// reduce the value space to a smaller value V, which reduces the number of cached
@@ -66,18 +64,23 @@
private boolean mIsPressed;
private boolean mIsDisabled;
- private static final Property<FastBitmapDrawable, Float> BRIGHTNESS
- = new Property<FastBitmapDrawable, Float>(Float.TYPE, "brightness") {
+ // Animator and properties for the fast bitmap drawable's scale
+ private static final Property<FastBitmapDrawable, Float> SCALE
+ = new Property<FastBitmapDrawable, Float>(Float.TYPE, "scale") {
@Override
public Float get(FastBitmapDrawable fastBitmapDrawable) {
- return fastBitmapDrawable.getBrightness();
+ return fastBitmapDrawable.mScale;
}
@Override
public void set(FastBitmapDrawable fastBitmapDrawable, Float value) {
- fastBitmapDrawable.setBrightness(value);
+ fastBitmapDrawable.mScale = value;
+ fastBitmapDrawable.invalidateSelf();
}
};
+ private ObjectAnimator mScaleAnimation;
+ private float mScale = 1;
+
// The saturation and brightness are values that are mapped to REDUCED_FILTER_VALUE_SPACE and
// as a result, can be used to compose the key for the cached ColorMatrixColorFilters
@@ -86,9 +89,6 @@
private int mAlpha = 255;
private int mPrevUpdateKey = Integer.MAX_VALUE;
- // Animators for the fast bitmap drawable's brightness
- private ObjectAnimator mBrightnessAnimator;
-
public FastBitmapDrawable(Bitmap b) {
this(b, Color.TRANSPARENT);
}
@@ -108,8 +108,20 @@
}
@Override
- public void draw(Canvas canvas) {
- canvas.drawBitmap(mBitmap, null, getBounds(), mPaint);
+ public final void draw(Canvas canvas) {
+ if (mScaleAnimation != null) {
+ int count = canvas.save();
+ Rect bounds = getBounds();
+ canvas.scale(mScale, mScale, bounds.exactCenterX(), bounds.exactCenterY());
+ drawInternal(canvas, bounds);
+ canvas.restoreToCount(count);
+ } else {
+ drawInternal(canvas, getBounds());
+ }
+ }
+
+ protected void drawInternal(Canvas canvas, Rect bounds) {
+ canvas.drawBitmap(mBitmap, null, bounds, mPaint);
}
@Override
@@ -138,6 +150,10 @@
return mAlpha;
}
+ public float getAnimatedScale() {
+ return mScaleAnimation == null ? 1 : mScale;
+ }
+
@Override
public int getIntrinsicWidth() {
return mBitmap.getWidth();
@@ -184,19 +200,20 @@
if (mIsPressed != isPressed) {
mIsPressed = isPressed;
- if (mBrightnessAnimator != null) {
- mBrightnessAnimator.cancel();
+ if (mScaleAnimation != null) {
+ mScaleAnimation.cancel();
+ mScaleAnimation = null;
}
if (mIsPressed) {
// Animate when going to pressed state
- mBrightnessAnimator = ObjectAnimator.ofFloat(
- this, BRIGHTNESS, getExpectedBrightness());
- mBrightnessAnimator.setDuration(CLICK_FEEDBACK_DURATION);
- mBrightnessAnimator.setInterpolator(CLICK_FEEDBACK_INTERPOLATOR);
- mBrightnessAnimator.start();
+ mScaleAnimation = ObjectAnimator.ofFloat(this, SCALE, PRESSED_SCALE);
+ mScaleAnimation.setDuration(CLICK_FEEDBACK_DURATION);
+ mScaleAnimation.setInterpolator(ACCEL);
+ mScaleAnimation.start();
} else {
- setBrightness(getExpectedBrightness());
+ mScale = 1f;
+ invalidateSelf();
}
return true;
}
@@ -205,12 +222,7 @@
private void invalidateDesaturationAndBrightness() {
setDesaturation(mIsDisabled ? DISABLED_DESATURATION : 0);
- setBrightness(getExpectedBrightness());
- }
-
- private float getExpectedBrightness() {
- return mIsDisabled ? DISABLED_BRIGHTNESS :
- (mIsPressed ? PRESSED_BRIGHTNESS : 0);
+ setBrightness(mIsDisabled ? DISABLED_BRIGHTNESS : 0);
}
public void setIsDisabled(boolean isDisabled) {
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java
index d0581a2..f4ae62a 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -45,12 +45,10 @@
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.UserManagerCompat;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.graphics.BitmapInfo;
-import com.android.launcher3.graphics.ColorExtractor;
+import com.android.launcher3.graphics.BitmapRenderer;
import com.android.launcher3.graphics.LauncherIcons;
import com.android.launcher3.model.PackageItemInfo;
-import com.android.launcher3.uioverrides.UiFactory;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.InstantAppResolver;
import com.android.launcher3.util.Preconditions;
@@ -126,7 +124,7 @@
// automatically be loaded as ALPHA_8888.
mLowResOptions.inPreferredConfig = Bitmap.Config.RGB_565;
- if (UiFactory.USE_HARDWARE_BITMAP) {
+ if (BitmapRenderer.USE_HARDWARE_BITMAP) {
mHighResOptions = new BitmapFactory.Options();
mHighResOptions.inPreferredConfig = Bitmap.Config.HARDWARE;
} else {
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 38235f4..375deb7 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -18,13 +18,7 @@
import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_BY_PUBLISHER;
-import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER;
-import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_QUIET_USER;
-import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SAFEMODE;
-import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED;
+
import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
@@ -40,7 +34,6 @@
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.ActivityOptions;
-import android.app.AlertDialog;
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetManager;
import android.content.ActivityNotFoundException;
@@ -48,12 +41,10 @@
import android.content.ComponentCallbacks2;
import android.content.Context;
import android.content.ContextWrapper;
-import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.SharedPreferences;
-import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.database.sqlite.SQLiteDatabase;
@@ -82,6 +73,7 @@
import android.view.Menu;
import android.view.MotionEvent;
import android.view.View;
+import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
@@ -101,10 +93,8 @@
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragLayer;
-import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.dynamicui.WallpaperColorInfo;
-import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.keyboard.CustomActionsPopup;
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
@@ -116,6 +106,8 @@
import com.android.launcher3.popup.PopupDataProvider;
import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.states.InternalStateHandler;
+import com.android.launcher3.states.RotationHelper;
+import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.uioverrides.UiFactory;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
@@ -156,7 +148,7 @@
* Default launcher application.
*/
public class Launcher extends BaseActivity
- implements LauncherExterns, View.OnClickListener, OnLongClickListener,
+ implements LauncherExterns, OnClickListener, OnLongClickListener,
LauncherModel.Callbacks, View.OnTouchListener, LauncherProviderChangeListener,
WallpaperColorInfo.OnThemeChangeListener {
public static final String TAG = "Launcher";
@@ -171,8 +163,8 @@
private static final int REQUEST_PICK_WALLPAPER = 10;
private static final int REQUEST_BIND_APPWIDGET = 11;
- private static final int REQUEST_BIND_PENDING_APPWIDGET = 12;
- private static final int REQUEST_RECONFIGURE_APPWIDGET = 13;
+ public static final int REQUEST_BIND_PENDING_APPWIDGET = 12;
+ public static final int REQUEST_RECONFIGURE_APPWIDGET = 13;
private static final int REQUEST_PERMISSION_CALL_PHONE = 14;
@@ -272,10 +264,9 @@
private final PointF mLastDispatchTouchEvent = new PointF();
public ViewGroupFocusHelper mFocusHandler;
- private boolean mRotationEnabled = false;
private boolean mAppLaunchSuccess;
- private RotationPrefChangeHandler mRotationPrefChangeHandler;
+ private RotationHelper mRotationHelper;
private ActionMode mCurrentActionMode;
@Override
@@ -327,20 +318,10 @@
setupViews();
mPopupDataProvider = new PopupDataProvider(this);
- mRotationEnabled = getResources().getBoolean(R.bool.allow_rotation);
- // In case we are on a device with locked rotation, we should look at preferences to check
- // if the user has specifically allowed rotation.
- if (!mRotationEnabled) {
- mRotationEnabled = Utilities.isAllowRotationPrefEnabled(getApplicationContext());
- mRotationPrefChangeHandler = new RotationPrefChangeHandler();
- mSharedPrefs.registerOnSharedPreferenceChangeListener(mRotationPrefChangeHandler);
- }
+ mRotationHelper = new RotationHelper(this);
boolean internalStateHandled = InternalStateHandler.handleCreate(this, getIntent());
if (internalStateHandled) {
- // Temporarily enable the rotation
- mRotationEnabled = true;
-
if (savedInstanceState != null) {
// InternalStateHandler has already set the appropriate state.
// We dont need to do anything.
@@ -372,7 +353,6 @@
// For handling default keys
setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
- updateRequestedOrientation();
setContentView(mLauncherView);
getRootView().dispatchInsets();
@@ -391,17 +371,11 @@
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onCreate(savedInstanceState);
}
+ mRotationHelper.initialize();
TraceHelper.endSection("Launcher-onCreate");
}
- public void updateRequestedOrientation() {
- // On large interfaces, or on devices that a user has specifically enabled screen rotation,
- // we want the screen to auto-rotate based on the current orientation
- setRequestedOrientation(mRotationEnabled
- ? SCREEN_ORIENTATION_UNSPECIFIED : SCREEN_ORIENTATION_NOSENSOR);
- }
-
@Override
public void onConfigurationChanged(Configuration newConfig) {
int diff = newConfig.diff(mOldConfig);
@@ -419,17 +393,22 @@
getStateManager().reapplyState();
// TODO: We can probably avoid rebind when only screen size changed.
- int currentPage = mWorkspace.getNextPage();
- if (mModel.startLoader(currentPage)) {
- mWorkspace.setCurrentPage(currentPage);
- setWorkspaceLoading(true);
- }
+ rebindModel();
}
mOldConfig.setTo(newConfig);
super.onConfigurationChanged(newConfig);
}
+ @Override
+ public void rebindModel() {
+ int currentPage = mWorkspace.getNextPage();
+ if (mModel.startLoader(currentPage)) {
+ mWorkspace.setCurrentPage(currentPage);
+ setWorkspaceLoading(true);
+ }
+ }
+
private void initDeviceProfile(InvariantDeviceProfile idp) {
// Load configuration-specific DeviceProfile
mDeviceProfile = idp.getDeviceProfile(this);
@@ -439,7 +418,11 @@
display.getSize(mwSize);
mDeviceProfile = mDeviceProfile.getMultiWindowProfile(this, mwSize);
}
- mModelWriter = mModel.getWriter(mDeviceProfile.isVerticalBarLayout());
+ mModelWriter = mModel.getWriter(mDeviceProfile.isVerticalBarLayout(), true);
+ }
+
+ public RotationHelper getRotationHelper() {
+ return mRotationHelper;
}
@Override
@@ -1027,7 +1010,7 @@
BubbleTextView favorite = (BubbleTextView) LayoutInflater.from(parent.getContext())
.inflate(R.layout.app_icon, parent, false);
favorite.applyFromShortcutInfo(info);
- favorite.setOnClickListener(this);
+ favorite.setOnClickListener(ItemClickHandler.INSTANCE);
favorite.setOnFocusChangeListener(mFocusHandler);
return favorite;
}
@@ -1381,10 +1364,7 @@
mModel.stopLoader();
LauncherAppState.getInstance(this).setLauncher(null);
}
-
- if (mRotationPrefChangeHandler != null) {
- mSharedPrefs.unregisterOnSharedPreferenceChangeListener(mRotationPrefChangeHandler);
- }
+ mRotationHelper.destroy();
try {
mAppWidgetHost.stopListening();
@@ -1683,6 +1663,7 @@
*
* @param v The view representing the clicked shortcut.
*/
+ @Override
public void onClick(View v) {
// Make sure that rogue clicks don't get through while allapps is launching, or after the
// view has detached (it's possible for this to happen if the view is removed mid touch).
@@ -1715,21 +1696,6 @@
}
return;
}
-
- Object tag = v.getTag();
- if (tag instanceof ShortcutInfo) {
- onClickAppShortcut(v);
- } else if (tag instanceof FolderInfo) {
- if (v instanceof FolderIcon) {
- onClickFolderIcon(v);
- }
- } else if (tag instanceof AppInfo) {
- startAppShortcutOrInfoActivity(v);
- } else if (tag instanceof LauncherAppWidgetInfo) {
- if (v instanceof PendingAppWidgetHostView) {
- onClickPendingWidget((PendingAppWidgetHostView) v);
- }
- }
}
@SuppressLint("ClickableViewAccessibility")
@@ -1738,173 +1704,6 @@
}
/**
- * Event handler for the app widget view which has not fully restored.
- */
- public void onClickPendingWidget(final PendingAppWidgetHostView v) {
- if (mIsSafeModeEnabled) {
- Toast.makeText(this, R.string.safemode_widget_error, Toast.LENGTH_SHORT).show();
- return;
- }
-
- final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) v.getTag();
- if (v.isReadyForClickSetup()) {
- LauncherAppWidgetProviderInfo appWidgetInfo =
- mAppWidgetManager.findProvider(info.providerName, info.user);
- if (appWidgetInfo == null) {
- return;
- }
- WidgetAddFlowHandler addFlowHandler = new WidgetAddFlowHandler(appWidgetInfo);
-
- if (info.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) {
- if (!info.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_ALLOCATED)) {
- // This should not happen, as we make sure that an Id is allocated during bind.
- return;
- }
- addFlowHandler.startBindFlow(this, info.appWidgetId, info,
- REQUEST_BIND_PENDING_APPWIDGET);
- } else {
- addFlowHandler.startConfigActivity(this, info, REQUEST_RECONFIGURE_APPWIDGET);
- }
- } else {
- final String packageName = info.providerName.getPackageName();
- onClickPendingAppItem(v, packageName, info.installProgress >= 0);
- }
- }
-
- private void onClickPendingAppItem(final View v, final String packageName,
- boolean downloadStarted) {
- if (downloadStarted) {
- // If the download has started, simply direct to the market app.
- startMarketIntentForPackage(v, packageName);
- return;
- }
- new AlertDialog.Builder(this)
- .setTitle(R.string.abandoned_promises_title)
- .setMessage(R.string.abandoned_promise_explanation)
- .setPositiveButton(R.string.abandoned_search, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialogInterface, int i) {
- startMarketIntentForPackage(v, packageName);
- }
- })
- .setNeutralButton(R.string.abandoned_clean_this,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- final UserHandle user = Process.myUserHandle();
- mWorkspace.removeAbandonedPromise(packageName, user);
- }
- })
- .create().show();
- }
-
- private void startMarketIntentForPackage(View v, String packageName) {
- ItemInfo item = (ItemInfo) v.getTag();
- Intent intent = new PackageManagerHelper(v.getContext()).getMarketIntent(packageName);
- startActivitySafely(v, intent, item);
- }
-
- /**
- * Event handler for an app shortcut click.
- *
- * @param v The view that was clicked. Must be a tagged with a {@link ShortcutInfo}.
- */
- protected void onClickAppShortcut(final View v) {
- if (LOGD) Log.d(TAG, "onClickAppShortcut");
- Object tag = v.getTag();
- if (!(tag instanceof ShortcutInfo)) {
- throw new IllegalArgumentException("Input must be a Shortcut");
- }
-
- // Open shortcut
- final ShortcutInfo shortcut = (ShortcutInfo) tag;
-
- if (shortcut.isDisabled()) {
- final int disabledFlags = shortcut.runtimeStatusFlags & ShortcutInfo.FLAG_DISABLED_MASK;
- if ((disabledFlags &
- ~FLAG_DISABLED_SUSPENDED &
- ~FLAG_DISABLED_QUIET_USER) == 0) {
- // If the app is only disabled because of the above flags, launch activity anyway.
- // Framework will tell the user why the app is suspended.
- } else {
- if (!TextUtils.isEmpty(shortcut.disabledMessage)) {
- // Use a message specific to this shortcut, if it has one.
- Toast.makeText(this, shortcut.disabledMessage, Toast.LENGTH_SHORT).show();
- return;
- }
- // Otherwise just use a generic error message.
- int error = R.string.activity_not_available;
- if ((shortcut.runtimeStatusFlags & FLAG_DISABLED_SAFEMODE) != 0) {
- error = R.string.safemode_shortcut_error;
- } else if ((shortcut.runtimeStatusFlags & FLAG_DISABLED_BY_PUBLISHER) != 0 ||
- (shortcut.runtimeStatusFlags & FLAG_DISABLED_LOCKED_USER) != 0) {
- error = R.string.shortcut_not_available;
- }
- Toast.makeText(this, error, Toast.LENGTH_SHORT).show();
- return;
- }
- }
-
- // Check for abandoned promise
- if ((v instanceof BubbleTextView) && shortcut.hasPromiseIconUi()) {
- String packageName = shortcut.intent.getComponent() != null ?
- shortcut.intent.getComponent().getPackageName() : shortcut.intent.getPackage();
- if (!TextUtils.isEmpty(packageName)) {
- onClickPendingAppItem(v, packageName,
- shortcut.hasStatusFlag(ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE));
- return;
- }
- }
-
- // Start activities
- startAppShortcutOrInfoActivity(v);
- }
-
- private void startAppShortcutOrInfoActivity(View v) {
- ItemInfo item = (ItemInfo) v.getTag();
- Intent intent;
- if (item instanceof PromiseAppInfo) {
- PromiseAppInfo promiseAppInfo = (PromiseAppInfo) item;
- intent = promiseAppInfo.getMarketIntent(this);
- } else {
- intent = item.getIntent();
- }
- if (intent == null) {
- throw new IllegalArgumentException("Input must have a valid intent");
- }
- if (item instanceof ShortcutInfo) {
- ShortcutInfo si = (ShortcutInfo) item;
- if (si.hasStatusFlag(ShortcutInfo.FLAG_SUPPORTS_WEB_UI)
- && intent.getAction() == Intent.ACTION_VIEW) {
- // make a copy of the intent that has the package set to null
- // we do this because the platform sometimes disables instant
- // apps temporarily (triggered by the user) and fallbacks to the
- // web ui. This only works though if the package isn't set
- intent = new Intent(intent);
- intent.setPackage(null);
- }
- }
- startActivitySafely(v, intent, item);
- }
-
- /**
- * Event handler for a folder icon click.
- *
- * @param v The view that was clicked. Must be an instance of {@link FolderIcon}.
- */
- protected void onClickFolderIcon(View v) {
- if (LOGD) Log.d(TAG, "onClickFolder");
- if (!(v instanceof FolderIcon)){
- throw new IllegalArgumentException("Input must be a FolderIcon");
- }
-
- Folder folder = ((FolderIcon) v).getFolder();
- if (!folder.isOpen() && !folder.isDestroyed()) {
- // Open the requested folder
- folder.animateOpen();
- }
- }
-
- /**
* Event handler for the wallpaper picker button that appears after a long press
* on the home screen.
*/
@@ -2094,45 +1893,25 @@
}
}
- CellLayout.CellInfo longClickCellInfo = null;
- View itemUnderLongClick = null;
- if (v.getTag() instanceof ItemInfo) {
- ItemInfo info = (ItemInfo) v.getTag();
- longClickCellInfo = new CellLayout.CellInfo(v, info);
- itemUnderLongClick = longClickCellInfo.cell;
- mPendingRequestArgs = null;
- }
-
// The hotseat touch handling does not go through Workspace, and we always allow long press
// on hotseat items.
if (!mDragController.isDragging()) {
- if (itemUnderLongClick == null) {
- // User long pressed on empty space
- if (mWorkspace.isPageRearrangeEnabled()) {
- mWorkspace.startReordering(v);
- getUserEventDispatcher().logActionOnContainer(Action.Touch.LONGPRESS,
- Action.Direction.NONE, ContainerType.OVERVIEW);
- } else {
- if (ignoreLongPressToOverview) {
- return false;
- }
- getUserEventDispatcher().logActionOnContainer(Action.Touch.LONGPRESS,
- Action.Direction.NONE, ContainerType.WORKSPACE,
- mWorkspace.getCurrentPage());
- UiFactory.onWorkspaceLongPress(this, mLastDispatchTouchEvent);
- }
- mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
- HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
+ // User long pressed on empty space
+ if (mWorkspace.isPageRearrangeEnabled()) {
+ mWorkspace.startReordering(v);
+ getUserEventDispatcher().logActionOnContainer(Action.Touch.LONGPRESS,
+ Action.Direction.NONE, ContainerType.OVERVIEW);
} else {
- final boolean isAllAppsButton =
- !FeatureFlags.NO_ALL_APPS_ICON && isHotseatLayout(v) &&
- mDeviceProfile.inv.isAllAppsButtonRank(mHotseat.getOrderInHotseat(
- longClickCellInfo.cellX, longClickCellInfo.cellY));
- if (!(itemUnderLongClick instanceof Folder || isAllAppsButton)) {
- // User long pressed on an item
- mWorkspace.startDrag(longClickCellInfo, new DragOptions());
+ if (ignoreLongPressToOverview) {
+ return false;
}
+ getUserEventDispatcher().logActionOnContainer(Action.Touch.LONGPRESS,
+ Action.Direction.NONE, ContainerType.WORKSPACE,
+ mWorkspace.getCurrentPage());
+ UiFactory.onWorkspaceLongPress(this, mLastDispatchTouchEvent);
}
+ mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
+ HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
}
return true;
}
@@ -2727,10 +2506,6 @@
mModel.refreshAndBindWidgetsAndShortcuts(packageUser);
}
- public boolean isRotationEnabled () {
- return mRotationEnabled;
- }
-
/**
* $ adb shell dumpsys activity com.android.launcher3.Launcher [--all]
*/
@@ -2868,18 +2643,6 @@
return ((Launcher) ((ContextWrapper) context).getBaseContext());
}
- private class RotationPrefChangeHandler implements OnSharedPreferenceChangeListener {
-
- @Override
- public void onSharedPreferenceChanged(
- SharedPreferences sharedPreferences, String key) {
- if (Utilities.ALLOW_ROTATION_PREFERENCE_KEY.equals(key)) {
- // Recreate the activity so that it initializes the rotation preference again.
- recreate();
- }
- }
- }
-
@Override
public void onActionModeStarted(ActionMode mode) {
super.onActionModeStarted(mode);
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 6646b78..04a32f7 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -135,6 +135,8 @@
};
public interface Callbacks {
+ public void rebindModel();
+
public int getCurrentWorkspaceScreen();
public void clearPendingBinds();
public void startBinding();
@@ -196,8 +198,9 @@
enqueueModelUpdateTask(new AddWorkspaceItemsTask(itemList));
}
- public ModelWriter getWriter(boolean hasVerticalHotseat) {
- return new ModelWriter(mApp.getContext(), sBgDataModel, hasVerticalHotseat);
+ public ModelWriter getWriter(boolean hasVerticalHotseat, boolean verifyChanges) {
+ return new ModelWriter(mApp.getContext(), this, sBgDataModel,
+ hasVerticalHotseat, verifyChanges);
}
static void checkItemInfoLocked(
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 54df1da..4e6bcdc 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -20,12 +20,13 @@
import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
import static com.android.launcher3.anim.Interpolators.ACCEL_2;
+import static com.android.launcher3.states.RotationHelper.REQUEST_NONE;
import android.view.View;
import android.view.animation.Interpolator;
-import com.android.launcher3.uioverrides.AllAppsState;
import com.android.launcher3.states.SpringLoadedState;
+import com.android.launcher3.uioverrides.AllAppsState;
import com.android.launcher3.uioverrides.FastOverviewState;
import com.android.launcher3.uioverrides.OverviewState;
import com.android.launcher3.uioverrides.UiFactory;
@@ -211,6 +212,8 @@
public void onStateTransitionEnd(Launcher launcher) {
if (this == NORMAL) {
UiFactory.resetOverview(launcher);
+ // Clear any rotation locks when going to normal state
+ launcher.getRotationHelper().setCurrentStateRequest(REQUEST_NONE);
}
}
diff --git a/src/com/android/launcher3/SettingsActivity.java b/src/com/android/launcher3/SettingsActivity.java
index d40ac8f..6a4e93b 100644
--- a/src/com/android/launcher3/SettingsActivity.java
+++ b/src/com/android/launcher3/SettingsActivity.java
@@ -65,7 +65,6 @@
*/
public static class LauncherSettingsFragment extends PreferenceFragment {
- private SystemDisplayRotationLockObserver mRotationLockObserver;
private IconBadgingObserver mIconBadgingObserver;
@Override
@@ -76,22 +75,6 @@
ContentResolver resolver = getActivity().getContentResolver();
- // Setup allow rotation preference
- Preference rotationPref = findPreference(Utilities.ALLOW_ROTATION_PREFERENCE_KEY);
- if (getResources().getBoolean(R.bool.allow_rotation)) {
- // Launcher supports rotation by default. No need to show this setting.
- getPreferenceScreen().removePreference(rotationPref);
- } else {
- mRotationLockObserver = new SystemDisplayRotationLockObserver(rotationPref, resolver);
-
- // Register a content observer to listen for system setting changes while
- // this UI is active.
- mRotationLockObserver.register(Settings.System.ACCELEROMETER_ROTATION);
-
- // Initialize the UI once
- rotationPref.setDefaultValue(Utilities.getAllowRotationDefaultValue(getActivity()));
- }
-
ButtonPreference iconBadgingPref =
(ButtonPreference) findPreference(ICON_BADGING_PREFERENCE_KEY);
if (!Utilities.ATLEAST_OREO) {
@@ -119,10 +102,6 @@
@Override
public void onDestroy() {
- if (mRotationLockObserver != null) {
- mRotationLockObserver.unregister();
- mRotationLockObserver = null;
- }
if (mIconBadgingObserver != null) {
mIconBadgingObserver.unregister();
mIconBadgingObserver = null;
@@ -132,28 +111,6 @@
}
/**
- * Content observer which listens for system auto-rotate setting changes, and enables/disables
- * the launcher rotation setting accordingly.
- */
- private static class SystemDisplayRotationLockObserver extends SettingsObserver.System {
-
- private final Preference mRotationPref;
-
- public SystemDisplayRotationLockObserver(
- Preference rotationPref, ContentResolver resolver) {
- super(resolver);
- mRotationPref = rotationPref;
- }
-
- @Override
- public void onSettingChanged(boolean enabled) {
- mRotationPref.setEnabled(enabled);
- mRotationPref.setSummary(enabled
- ? R.string.allow_rotation_desc : R.string.allow_rotation_blocked_desc);
- }
- }
-
- /**
* Content observer which listens for system badging setting changes,
* and updates the launcher badging setting subtext accordingly.
*/
diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java
index ec608ca..8588c7a 100644
--- a/src/com/android/launcher3/ShortcutInfo.java
+++ b/src/com/android/launcher3/ShortcutInfo.java
@@ -79,7 +79,7 @@
* A message to display when the user tries to start a disabled shortcut.
* This is currently only used for deep shortcuts.
*/
- CharSequence disabledMessage;
+ public CharSequence disabledMessage;
public int status;
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 31dab97..cabccbf 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -125,29 +125,10 @@
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
- public static final String ALLOW_ROTATION_PREFERENCE_KEY = "pref_allowRotation";
-
public static boolean isPropertyEnabled(String propertyName) {
return Log.isLoggable(propertyName, Log.VERBOSE);
}
- public static boolean isAllowRotationPrefEnabled(Context context) {
- return getPrefs(context).getBoolean(ALLOW_ROTATION_PREFERENCE_KEY,
- getAllowRotationDefaultValue(context));
- }
-
- public static boolean getAllowRotationDefaultValue(Context context) {
- if (ATLEAST_NOUGAT) {
- // If the device was scaled, used the original dimensions to determine if rotation
- // is allowed of not.
- Resources res = context.getResources();
- int originalSmallestWidth = res.getConfiguration().smallestScreenWidthDp
- * res.getDisplayMetrics().densityDpi / DisplayMetrics.DENSITY_DEVICE_STABLE;
- return originalSmallestWidth >= 600;
- }
- return false;
- }
-
/**
* Given a coordinate relative to the descendant, find the coordinate in a parent view's
* coordinates.
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 7c42b48..f329f5e 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -81,6 +81,7 @@
import com.android.launcher3.pageindicators.WorkspacePageIndicator;
import com.android.launcher3.popup.PopupContainerWithArrow;
import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
+import com.android.launcher3.touch.ItemLongClickListener;
import com.android.launcher3.uioverrides.UiFactory;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
@@ -935,10 +936,9 @@
Log.e(TAG, "Failed to add to item at (" + lp.cellX + "," + lp.cellY + ") to CellLayout");
}
- if (!(child instanceof Folder)) {
- child.setHapticFeedbackEnabled(false);
- child.setOnLongClickListener(mLongClickListener);
- }
+ child.setHapticFeedbackEnabled(false);
+ child.setOnLongClickListener(ItemLongClickListener.INSTANCE_WORKSPACE);
+
if (child instanceof DropTarget) {
mDragController.addDropTarget((DropTarget) child);
}
@@ -1635,7 +1635,6 @@
new DragPreviewProvider(child), options);
}
-
public DragView beginDragShared(View child, DragSource source, ItemInfo dragObject,
DragPreviewProvider previewProvider, DragOptions dragOptions) {
child.clearFocus();
@@ -2970,8 +2969,9 @@
+ "Workspace#onDropCompleted. Please file a bug. ");
}
}
- if (d.cancelled && mDragInfo != null && mDragInfo.cell != null) {
- mDragInfo.cell.setVisibility(VISIBLE);
+ View cell = getHomescreenIconByItemId(d.originalDragInfo.id);
+ if (d.cancelled && cell != null) {
+ cell.setVisibility(VISIBLE);
}
mDragInfo = null;
}
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index 77ac9de..f6d0248 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -137,6 +137,8 @@
propertySetter.setViewAlpha(mLauncher.getHotseat(), state.getHoseatAlpha(mLauncher),
pageAlphaProvider.interpolator);
+ propertySetter.setViewAlpha(mLauncher.getWorkspace().getPageIndicator(),
+ state.getHoseatAlpha(mLauncher), pageAlphaProvider.interpolator);
// Set scrim
propertySetter.setFloat(ViewScrim.get(mWorkspace), ViewScrim.PROGRESS,
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 2cd8b1d..4398f6e 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -25,6 +25,7 @@
import com.android.launcher3.FolderInfo;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
+import com.android.launcher3.touch.ItemLongClickListener;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherSettings;
@@ -358,29 +359,14 @@
mDragInfo.dragType = DragType.WIDGET;
}
- CellLayout.CellInfo cellInfo = new CellLayout.CellInfo(item, info);
-
Rect pos = new Rect();
mLauncher.getDragLayer().getDescendantRectRelativeToSelf(item, pos);
mLauncher.getDragController().prepareAccessibleDrag(pos.centerX(), pos.centerY());
-
- Folder folder = Folder.getOpen(mLauncher);
- if (folder != null) {
- if (!folder.getItemsInReadingOrder().contains(item)) {
- folder.close(true);
- folder = null;
- }
- }
-
mLauncher.getDragController().addDragListener(this);
DragOptions options = new DragOptions();
options.isAccessibleDrag = true;
- if (folder != null) {
- folder.startDrag(cellInfo.cell, options);
- } else {
- mLauncher.getWorkspace().startDrag(cellInfo, options);
- }
+ ItemLongClickListener.beginDrag(item, mLauncher, info, options);
}
@Override
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 82bdef9..3fe5d7a 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -18,7 +18,9 @@
import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
import android.content.Context;
-import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Point;
import android.graphics.Rect;
import android.os.Process;
import android.support.annotation.NonNull;
@@ -32,49 +34,43 @@
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
-import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
-import android.widget.RelativeLayout;
import com.android.launcher3.AppInfo;
-import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.BubbleTextView.BubbleTextShadowHandler;
-import com.android.launcher3.ClickShadowView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.DragSource;
-import com.android.launcher3.DropTarget;
import com.android.launcher3.DropTarget.DragObject;
import com.android.launcher3.Insettable;
import com.android.launcher3.InsettableFrameLayout;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
-import com.android.launcher3.anim.SpringAnimationHandler;
import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.dragndrop.DragController;
-import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.graphics.ColorScrim;
import com.android.launcher3.keyboard.FocusedItemDecorator;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.BottomUserEducationView;
+import com.android.launcher3.views.RecyclerViewFastScroller;
+import com.android.launcher3.views.SpringRelativeLayout;
/**
* The all apps view container.
*/
-public class AllAppsContainerView extends RelativeLayout implements DragSource,
- OnLongClickListener, Insettable, BubbleTextShadowHandler, OnDeviceProfileChangeListener {
+public class AllAppsContainerView extends SpringRelativeLayout implements DragSource,
+ Insettable, OnDeviceProfileChangeListener {
private final Launcher mLauncher;
private final AdapterHolder[] mAH;
- private final ClickShadowView mTouchFeedbackView;
private final ItemInfoMatcher mPersonalMatcher = ItemInfoMatcher.ofUser(Process.myUserHandle());
private final ItemInfoMatcher mWorkMatcher = ItemInfoMatcher.not(mPersonalMatcher);
private final AllAppsStore mAllAppsStore = new AllAppsStore();
+ private final Paint mNavBarScrimPaint;
+ private int mNavBarScrimHeight = 0;
+
private SearchUiManager mSearchUiManager;
private View mSearchContainer;
private AllAppsPagedView mViewPager;
@@ -85,6 +81,9 @@
private boolean mUsingTabs;
private boolean mSearchModeWhileUsingTabs = false;
+ private RecyclerViewFastScroller mTouchHandler;
+ private final Point mFastScrollerOffset = new Point();
+
public AllAppsContainerView(Context context) {
this(context, null);
}
@@ -100,34 +99,24 @@
mLauncher.addOnDeviceProfileChangeListener(this);
mSearchQueryBuilder = new SpannableStringBuilder();
-
Selection.setSelection(mSearchQueryBuilder, 0);
- mTouchFeedbackView = new ClickShadowView(context);
- // Make the feedback view large enough to hold the blur bitmap.
- int size = mLauncher.getDeviceProfile().allAppsIconSizePx
- + mTouchFeedbackView.getExtraSize();
- addView(mTouchFeedbackView, size, size);
-
mAH = new AdapterHolder[2];
mAH[AdapterHolder.MAIN] = new AdapterHolder(false /* isWork */);
mAH[AdapterHolder.WORK] = new AdapterHolder(true /* isWork */);
+ mNavBarScrimPaint = new Paint();
+ mNavBarScrimPaint.setColor(Themes.getAttrColor(context, R.attr.allAppsNavBarScrimColor));
+
mAllAppsStore.addUpdateListener(this::onAppsUpdated);
// Attach a scrim to be drawn behind all-apps and hotseat
new ColorScrim(this, Themes.getAttrColor(context, R.attr.allAppsScrimColor), DEACCEL_2)
.attach();
- }
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- applyTouchDelegate();
- }
-
- private void applyTouchDelegate() {
- // TODO: Reimplement once fast scroller is fixed.
+ addSpringView(R.id.all_apps_header);
+ addSpringView(R.id.apps_list_view);
+ addSpringView(R.id.all_apps_tabs_view_pager);
}
public AllAppsStore getAppsStore() {
@@ -135,12 +124,6 @@
}
@Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
- applyTouchDelegate();
- }
-
- @Override
public void onDeviceProfileChanged(DeviceProfile dp) {
for (AdapterHolder holder : mAH) {
if (holder.recyclerView != null) {
@@ -165,11 +148,6 @@
}
}
- @Override
- public void setPressedIcon(BubbleTextView icon, Bitmap background) {
- mTouchFeedbackView.setPressedIcon(icon, background);
- }
-
/**
* Returns whether the view itself will handle the touch event or not.
*/
@@ -180,7 +158,38 @@
return true;
}
AllAppsRecyclerView rv = getActiveRecyclerView();
- return rv == null || rv.shouldContainerScroll(ev, mLauncher.getDragLayer());
+ if (rv == null) {
+ return true;
+ }
+ if (rv.getScrollbar().getThumbOffsetY() >= 0 &&
+ mLauncher.getDragLayer().isEventOverView(rv.getScrollbar(), ev)) {
+ return false;
+ }
+ return rv.shouldContainerScroll(ev, mLauncher.getDragLayer());
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ AllAppsRecyclerView rv = getActiveRecyclerView();
+ if (rv != null &&
+ rv.getScrollbar().isHitInParent(ev.getX(), ev.getY(), mFastScrollerOffset)) {
+ mTouchHandler = rv.getScrollbar();
+ }
+ }
+ if (mTouchHandler != null) {
+ return mTouchHandler.handleTouchEvent(ev, mFastScrollerOffset);
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ if (mTouchHandler != null) {
+ mTouchHandler.handleTouchEvent(ev, mFastScrollerOffset);
+ return true;
+ }
+ return false;
}
public AllAppsRecyclerView getActiveRecyclerView() {
@@ -204,7 +213,7 @@
mHeader.reset();
}
// Reset the search bar and base recycler view after transitioning home
- mSearchUiManager.reset();
+ mSearchUiManager.resetSearch();
}
@Override
@@ -238,37 +247,6 @@
}
@Override
- public boolean onLongClick(final View v) {
- // When we have exited all apps or are in transition, disregard long clicks
- if (!mLauncher.isInState(LauncherState.ALL_APPS) ||
- mLauncher.getWorkspace().isSwitchingState()) return false;
- // Return if global dragging is not enabled or we are already dragging
- if (!mLauncher.isDraggingEnabled()) return false;
- if (mLauncher.getDragController().isDragging()) return false;
-
- // Start the drag
- final DragController dragController = mLauncher.getDragController();
- dragController.addDragListener(new DragController.DragListener() {
- @Override
- public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
- v.setVisibility(INVISIBLE);
- }
-
- @Override
- public void onDragEnd() {
- v.setVisibility(VISIBLE);
- dragController.removeDragListener(this);
- }
- });
-
- DeviceProfile grid = mLauncher.getDeviceProfile();
- DragOptions options = new DragOptions();
- options.intrinsicIconScaleFactor = (float) grid.allAppsIconSizePx / grid.iconSizePx;
- mLauncher.getWorkspace().beginDragShared(v, this, options);
- return false;
- }
-
- @Override
public void onDropCompleted(View target, DragObject d, boolean success) { }
@Override
@@ -291,25 +269,26 @@
ViewGroup.MarginLayoutParams mlp = (MarginLayoutParams) getLayoutParams();
if (grid.isVerticalBarLayout()) {
mlp.leftMargin = insets.left;
- mlp.topMargin = insets.top;
mlp.rightMargin = insets.right;
setPadding(grid.workspacePadding.left, 0, grid.workspacePadding.right, 0);
} else {
- mlp.leftMargin = mlp.rightMargin = mlp.topMargin = 0;
+ mlp.leftMargin = mlp.rightMargin = 0;
setPadding(0, 0, 0, 0);
}
setLayoutParams(mlp);
- View navBarBg = findViewById(R.id.nav_bar_bg);
- ViewGroup.LayoutParams navBarBgLp = navBarBg.getLayoutParams();
- navBarBgLp.height = insets.bottom;
- navBarBg.setLayoutParams(navBarBgLp);
-
+ mNavBarScrimHeight = insets.bottom;
InsettableFrameLayout.dispatchInsets(this, insets);
}
- public SpringAnimationHandler getSpringAnimationHandler() {
- return mUsingTabs ? null : mAH[AdapterHolder.MAIN].animationHandler;
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ super.dispatchDraw(canvas);
+
+ if (mNavBarScrimHeight > 0) {
+ canvas.drawRect(0, getHeight() - mNavBarScrimHeight, getWidth(), getHeight(),
+ mNavBarScrimPaint);
+ }
}
private void rebindAdapters(boolean showTabs) {
@@ -338,8 +317,6 @@
mAllAppsStore.registerIconContainer(mAH[AdapterHolder.MAIN].recyclerView);
mAllAppsStore.registerIconContainer(mAH[AdapterHolder.WORK].recyclerView);
-
- applyTouchDelegate();
}
private void replaceRVContainer(boolean showTabs) {
@@ -370,7 +347,6 @@
public void onTabChanged(int pos) {
mHeader.setMainActive(pos == 0);
reset();
- applyTouchDelegate();
if (mAH[pos].recyclerView != null) {
mAH[pos].recyclerView.bindFastScrollbar();
@@ -457,7 +433,6 @@
public final AllAppsGridAdapter adapter;
final LinearLayoutManager layoutManager;
- final SpringAnimationHandler animationHandler;
final AlphabeticalAppsList appsList;
final Rect padding = new Rect();
AllAppsRecyclerView recyclerView;
@@ -465,25 +440,21 @@
AdapterHolder(boolean isWork) {
appsList = new AlphabeticalAppsList(mLauncher, mAllAppsStore, isWork);
- adapter = new AllAppsGridAdapter(mLauncher, appsList, mLauncher,
- AllAppsContainerView.this, true);
+ adapter = new AllAppsGridAdapter(mLauncher, appsList);
appsList.setAdapter(adapter);
- animationHandler = adapter.getSpringAnimationHandler();
layoutManager = adapter.getLayoutManager();
}
void setup(@NonNull View rv, @Nullable ItemInfoMatcher matcher) {
appsList.updateItemFilter(matcher);
recyclerView = (AllAppsRecyclerView) rv;
+ recyclerView.setEdgeEffectFactory(createEdgeEffectFactory());
recyclerView.setApps(appsList, mUsingTabs);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(adapter);
recyclerView.setHasFixedSize(true);
// No animations will occur when changes occur to the items in this RecyclerView.
recyclerView.setItemAnimator(null);
- if (FeatureFlags.LAUNCHER3_PHYSICS && animationHandler != null) {
- recyclerView.setSpringAnimationHandler(animationHandler);
- }
FocusedItemDecorator focusedItemDecorator = new FocusedItemDecorator(recyclerView);
recyclerView.addItemDecoration(focusedItemDecorator);
adapter.setIconFocusListener(focusedItemDecorator.getFocusListener());
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index a61521c..27fc53a 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -18,8 +18,6 @@
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
-import android.support.animation.DynamicAnimation;
-import android.support.animation.SpringAnimation;
import android.support.v4.view.accessibility.AccessibilityEventCompat;
import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
import android.support.v4.view.accessibility.AccessibilityRecordCompat;
@@ -38,11 +36,10 @@
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.AlphabeticalAppsList.AdapterItem;
-import com.android.launcher3.anim.SpringAnimationHandler;
import com.android.launcher3.compat.UserManagerCompat;
-import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.touch.ItemClickHandler;
+import com.android.launcher3.touch.ItemLongClickListener;
import com.android.launcher3.util.PackageManagerHelper;
import java.util.List;
@@ -71,7 +68,6 @@
// 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;
- public static final int VIEW_TYPE_MASK_HAS_SPRINGS = VIEW_TYPE_MASK_ICON;
public interface BindViewCallback {
@@ -182,8 +178,6 @@
private final AlphabeticalAppsList mApps;
private final GridLayoutManager mGridLayoutMgr;
private final GridSpanSizer mGridSizer;
- private final View.OnClickListener mIconClickListener;
- private final View.OnLongClickListener mIconLongClickListener;
private final int mAppsPerRow;
@@ -195,10 +189,7 @@
// The intent to send off to the market app, updated each time the search query changes.
private Intent mMarketSearchIntent;
- private final SpringAnimationHandler<ViewHolder> mSpringAnimationHandler;
-
- public AllAppsGridAdapter(Launcher launcher, AlphabeticalAppsList apps, View.OnClickListener
- iconClickListener, View.OnLongClickListener iconLongClickListener, boolean springAnim) {
+ public AllAppsGridAdapter(Launcher launcher, AlphabeticalAppsList apps) {
Resources res = launcher.getResources();
mLauncher = launcher;
mApps = apps;
@@ -207,23 +198,11 @@
mGridLayoutMgr = new AppsGridLayoutManager(launcher);
mGridLayoutMgr.setSpanSizeLookup(mGridSizer);
mLayoutInflater = LayoutInflater.from(launcher);
- mIconClickListener = iconClickListener;
- mIconLongClickListener = iconLongClickListener;
- if (FeatureFlags.LAUNCHER3_PHYSICS && springAnim) {
- mSpringAnimationHandler = new SpringAnimationHandler<>(
- SpringAnimationHandler.Y_DIRECTION, new AllAppsSpringAnimationFactory());
- } else {
- mSpringAnimationHandler = null;
- }
mAppsPerRow = mLauncher.getDeviceProfile().inv.numColumns;
mGridLayoutMgr.setSpanCount(mAppsPerRow);
}
- public SpringAnimationHandler getSpringAnimationHandler() {
- return mSpringAnimationHandler;
- }
-
public static boolean isDividerViewType(int viewType) {
return isViewType(viewType, VIEW_TYPE_MASK_DIVIDER);
}
@@ -270,8 +249,8 @@
case VIEW_TYPE_ICON:
BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate(
R.layout.all_apps_icon, parent, false);
- icon.setOnClickListener(mIconClickListener);
- icon.setOnLongClickListener(mIconLongClickListener);
+ icon.setOnClickListener(ItemClickHandler.INSTANCE);
+ icon.setOnLongClickListener(ItemLongClickListener.INSTANCE_ALL_APPS);
icon.setLongPressTimeout(ViewConfiguration.getLongPressTimeout());
icon.setOnFocusChangeListener(mIconFocusListener);
@@ -344,22 +323,6 @@
}
@Override
- public void onViewAttachedToWindow(ViewHolder holder) {
- int type = holder.getItemViewType();
- if (mSpringAnimationHandler != null && isViewType(type, VIEW_TYPE_MASK_HAS_SPRINGS)) {
- mSpringAnimationHandler.add(holder.itemView, holder);
- }
- }
-
- @Override
- public void onViewDetachedFromWindow(ViewHolder holder) {
- int type = holder.getItemViewType();
- if (mSpringAnimationHandler != null && isViewType(type, VIEW_TYPE_MASK_HAS_SPRINGS)) {
- mSpringAnimationHandler.remove(holder.itemView);
- }
- }
-
- @Override
public boolean onFailedToRecycleView(ViewHolder holder) {
// Always recycle and we will reset the view when it is bound
return true;
@@ -376,104 +339,4 @@
return item.viewType;
}
- /**
- * Helper class to set the SpringAnimation values for an item in the adapter.
- */
- private class AllAppsSpringAnimationFactory
- implements SpringAnimationHandler.AnimationFactory<ViewHolder> {
- private static final float DEFAULT_MAX_VALUE_PX = 100;
- private static final float DEFAULT_MIN_VALUE_PX = -DEFAULT_MAX_VALUE_PX;
-
- // Damping ratio range is [0, 1]
- private static final float SPRING_DAMPING_RATIO = 0.55f;
-
- // Stiffness is a non-negative number.
- private static final float MIN_SPRING_STIFFNESS = 580f;
- private static final float MAX_SPRING_STIFFNESS = 900f;
-
- // The amount by which each adjacent rows' stiffness will differ.
- private static final float ROW_STIFFNESS_COEFFICIENT = 50f;
-
- // The percentage by which we multiply each row to create the row factor.
- private static final float ROW_PERCENTAGE = 0.3f;
-
- @Override
- public SpringAnimation initialize(ViewHolder vh) {
- return SpringAnimationHandler.forView(vh.itemView, DynamicAnimation.TRANSLATION_Y, 0);
- }
-
- /**
- * @param spring A new or recycled SpringAnimation.
- * @param vh The ViewHolder that {@param spring} is related to.
- */
- @Override
- public void update(SpringAnimation spring, ViewHolder vh) {
- int appPosition = vh.getAdapterPosition();
- int col = appPosition % mAppsPerRow;
- int row = appPosition / mAppsPerRow;
-
- int numTotalRows = mApps.getNumAppRows() - 1; // zero-based count
- if (row > (numTotalRows / 2)) {
- // Mirror the rows so that the top row acts the same as the bottom row.
- row = Math.abs(numTotalRows - row);
- }
-
- calculateSpringValues(spring, row, col);
- }
-
- @Override
- public void setDefaultValues(SpringAnimation spring) {
- calculateSpringValues(spring, 0, mAppsPerRow / 2);
- }
-
- /**
- * We manipulate the stiffness, min, and max values based on the items distance to the
- * first row and the items distance to the center column to create the ^-shaped motion
- * effect.
- */
- private void calculateSpringValues(SpringAnimation spring, int row, int col) {
- float rowFactor = (1 + row) * ROW_PERCENTAGE;
- float colFactor = getColumnFactor(col, mAppsPerRow);
-
- float minValue = DEFAULT_MIN_VALUE_PX * (rowFactor + colFactor);
- float maxValue = DEFAULT_MAX_VALUE_PX * (rowFactor + colFactor);
-
- float stiffness = Utilities.boundToRange(
- MAX_SPRING_STIFFNESS - (row * ROW_STIFFNESS_COEFFICIENT),
- MIN_SPRING_STIFFNESS,
- MAX_SPRING_STIFFNESS);
-
- spring.setMinValue(minValue)
- .setMaxValue(maxValue)
- .getSpring()
- .setStiffness(stiffness)
- .setDampingRatio(SPRING_DAMPING_RATIO);
- }
-
- /**
- * Increase the column factor as the distance increases between the column and the center
- * column(s).
- */
- private float getColumnFactor(int col, int numCols) {
- float centerColumn = numCols / 2;
- int distanceToCenter = (int) Math.abs(col - centerColumn);
-
- boolean evenNumberOfColumns = numCols % 2 == 0;
- if (evenNumberOfColumns && col < centerColumn) {
- distanceToCenter -= 1;
- }
-
- float factor = 0;
- while (distanceToCenter > 0) {
- if (distanceToCenter == 1) {
- factor += 0.2f;
- } else {
- factor += 0.1f;
- }
- --distanceToCenter;
- }
-
- return factor;
- }
- }
}
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index 53d19eb..a7447b7 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -17,14 +17,12 @@
import static android.view.View.MeasureSpec.UNSPECIFIED;
-import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
-import android.util.Property;
import android.util.SparseIntArray;
import android.view.MotionEvent;
import android.view.View;
@@ -35,12 +33,8 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
-import com.android.launcher3.anim.SpringAnimationHandler;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.graphics.DrawableFactory;
import com.android.launcher3.logging.UserEventDispatcher.LogContainerProvider;
-import com.android.launcher3.touch.OverScroll;
-import com.android.launcher3.touch.SwipeDetector;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import com.android.launcher3.views.RecyclerViewFastScroller;
@@ -64,23 +58,6 @@
private AllAppsBackgroundDrawable mEmptySearchBackground;
private int mEmptySearchBackgroundTopOffset;
- private SpringAnimationHandler mSpringAnimationHandler;
- private OverScrollHelper mOverScrollHelper;
- private SwipeDetector mPullDetector;
- private float mContentTranslationY = 0;
- public static final Property<AllAppsRecyclerView, Float> CONTENT_TRANS_Y =
- new Property<AllAppsRecyclerView, Float>(Float.class, "appsRecyclerViewContentTransY") {
- @Override
- public Float get(AllAppsRecyclerView allAppsRecyclerView) {
- return allAppsRecyclerView.getContentTranslationY();
- }
-
- @Override
- public void set(AllAppsRecyclerView allAppsRecyclerView, Float y) {
- allAppsRecyclerView.setContentTranslationY(y);
- }
- };
-
public AllAppsRecyclerView(Context context) {
this(context, null);
}
@@ -97,33 +74,11 @@
int defStyleRes) {
super(context, attrs, defStyleAttr);
Resources res = getResources();
- addOnItemTouchListener(this);
mEmptySearchBackgroundTopOffset = res.getDimensionPixelSize(
R.dimen.all_apps_empty_search_bg_top_offset);
-
- mOverScrollHelper = new OverScrollHelper();
- mPullDetector = new SwipeDetector(getContext(), mOverScrollHelper, SwipeDetector.VERTICAL);
- mPullDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_BOTH, true);
-
mNumAppsPerRow = LauncherAppState.getIDP(context).numColumns;
}
- public void setSpringAnimationHandler(SpringAnimationHandler springAnimationHandler) {
- if (FeatureFlags.LAUNCHER3_PHYSICS) {
- mSpringAnimationHandler = springAnimationHandler;
- addOnScrollListener(new SpringMotionOnScrollListener());
- }
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent e) {
- mPullDetector.onTouchEvent(e);
- if (FeatureFlags.LAUNCHER3_PHYSICS && mSpringAnimationHandler != null) {
- mSpringAnimationHandler.addMovement(e);
- }
- return super.onTouchEvent(e);
- }
-
/**
* Sets the list of apps in this view, used to determine the fastscroll position.
*/
@@ -171,26 +126,6 @@
}
@Override
- protected void dispatchDraw(Canvas canvas) {
- canvas.translate(0, mContentTranslationY);
- super.dispatchDraw(canvas);
- canvas.translate(0, -mContentTranslationY);
- }
-
- public float getContentTranslationY() {
- return mContentTranslationY;
- }
-
- /**
- * Use this method instead of calling {@link #setTranslationY(float)}} directly to avoid drawing
- * on top of other Views.
- */
- public void setContentTranslationY(float y) {
- mContentTranslationY = y;
- invalidate();
- }
-
- @Override
protected boolean verifyDrawable(Drawable who) {
return who == mEmptySearchBackground || super.verifyDrawable(who);
}
@@ -232,8 +167,7 @@
@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
- mPullDetector.onTouchEvent(e);
- boolean result = super.onInterceptTouchEvent(e) || mOverScrollHelper.isInOverScroll();
+ boolean result = super.onInterceptTouchEvent(e);
if (!result && e.getAction() == MotionEvent.ACTION_DOWN
&& mEmptySearchBackground != null && mEmptySearchBackground.getAlpha() > 0) {
mEmptySearchBackground.setHotspot(e.getX(), e.getY());
@@ -481,114 +415,4 @@
y + mEmptySearchBackground.getIntrinsicHeight());
}
- private class SpringMotionOnScrollListener extends RecyclerView.OnScrollListener {
-
- @Override
- public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
- if (mOverScrollHelper.isInOverScroll()) {
- // OverScroll will handle animating the springs.
- return;
- }
-
- // We only start the spring animation when we hit the top/bottom, to ensure
- // that all of the animations start at the same time.
- if (dy < 0 && !canScrollVertically(-1)) {
- mSpringAnimationHandler.animateToFinalPosition(0, 1);
- } else if (dy > 0 && !canScrollVertically(1)) {
- mSpringAnimationHandler.animateToFinalPosition(0, -1);
- }
- }
- }
-
- private class OverScrollHelper implements SwipeDetector.Listener {
-
- private static final float MAX_RELEASE_VELOCITY = 5000; // px / s
- private static final float MAX_OVERSCROLL_PERCENTAGE = 0.07f;
-
- private boolean mIsInOverScroll;
-
- // We use this value to calculate the actual amount the user has overscrolled.
- private float mFirstDisplacement = 0;
-
- private boolean mAlreadyScrollingUp;
- private int mFirstScrollYOnScrollUp;
-
- @Override
- public void onDragStart(boolean start) {
- }
-
- @Override
- public boolean onDrag(float displacement, float velocity) {
- boolean isScrollingUp = displacement > 0;
- if (isScrollingUp) {
- if (!mAlreadyScrollingUp) {
- mFirstScrollYOnScrollUp = getCurrentScrollY();
- mAlreadyScrollingUp = true;
- }
- } else {
- mAlreadyScrollingUp = false;
- }
-
- // Only enter overscroll if the user is interacting with the RecyclerView directly
- // and if one of the following criteria are met:
- // - User scrolls down when they're already at the bottom.
- // - User starts scrolling up, hits the top, and continues scrolling up.
- boolean wasInOverScroll = mIsInOverScroll;
- mIsInOverScroll = !mScrollbar.isDraggingThumb() &&
- ((!canScrollVertically(1) && displacement < 0) ||
- (!canScrollVertically(-1) && isScrollingUp && mFirstScrollYOnScrollUp != 0));
-
- if (wasInOverScroll && !mIsInOverScroll) {
- // Exit overscroll. This can happen when the user is in overscroll and then
- // scrolls the opposite way.
- reset(false /* shouldSpring */);
- } else if (mIsInOverScroll) {
- if (Float.compare(mFirstDisplacement, 0) == 0) {
- // Because users can scroll before entering overscroll, we need to
- // subtract the amount where the user was not in overscroll.
- mFirstDisplacement = displacement;
- }
- float overscrollY = displacement - mFirstDisplacement;
- setContentTranslationY(getDampedOverScroll(overscrollY));
- }
-
- return mIsInOverScroll;
- }
-
- @Override
- public void onDragEnd(float velocity, boolean fling) {
- reset(mIsInOverScroll /* shouldSpring */);
- }
-
- private void reset(boolean shouldSpring) {
- float y = getContentTranslationY();
- if (Float.compare(y, 0) != 0) {
- if (mSpringAnimationHandler != null && shouldSpring) {
- // We calculate our own velocity to give the springs the desired effect.
- float velocity = y / getDampedOverScroll(getHeight()) * MAX_RELEASE_VELOCITY;
- // We want to negate the velocity because we are moving to 0 from -1 due to the
- // downward motion. (y-axis -1 is above 0).
- mSpringAnimationHandler.animateToPositionWithVelocity(0, -1, -velocity);
- }
-
- ObjectAnimator.ofFloat(AllAppsRecyclerView.this,
- AllAppsRecyclerView.CONTENT_TRANS_Y, 0)
- .setDuration(100)
- .start();
- }
- mIsInOverScroll = false;
- mFirstDisplacement = 0;
- mFirstScrollYOnScrollUp = 0;
- mAlreadyScrollingUp = false;
- }
-
- public boolean isInOverScroll() {
- return mIsInOverScroll;
- }
-
- private float getDampedOverScroll(float y) {
- return OverScroll.dampedScroll(y, getHeight());
- }
- }
-
}
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderView.java b/src/com/android/launcher3/allapps/FloatingHeaderView.java
index 416469f..a0dc5a3 100644
--- a/src/com/android/launcher3/allapps/FloatingHeaderView.java
+++ b/src/com/android/launcher3/allapps/FloatingHeaderView.java
@@ -27,7 +27,6 @@
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
-import android.widget.RelativeLayout;
import com.android.launcher3.R;
@@ -68,6 +67,7 @@
private int mTranslationY;
private boolean mForwardToRecyclerView;
+ protected boolean mTabsHidden;
protected int mMaxTranslation;
public FloatingHeaderView(@NonNull Context context) {
@@ -85,6 +85,7 @@
}
public void setup(AllAppsContainerView.AdapterHolder[] mAH, boolean tabsHidden) {
+ mTabsHidden = tabsHidden;
mTabLayout.setVisibility(tabsHidden ? View.GONE : View.VISIBLE);
mMainRV = setupRV(mMainRV, mAH[AllAppsContainerView.AdapterHolder.MAIN].recyclerView);
mWorkRV = setupRV(mWorkRV, mAH[AllAppsContainerView.AdapterHolder.WORK].recyclerView);
@@ -105,7 +106,13 @@
}
public int getMaxTranslation() {
- return mMaxTranslation;
+ if (mMaxTranslation == 0 && mTabsHidden) {
+ return getResources().getDimensionPixelSize(R.dimen.all_apps_search_bar_bottom_padding);
+ } else if (mMaxTranslation > 0 && mTabsHidden) {
+ return mMaxTranslation + getPaddingTop();
+ } else {
+ return mMaxTranslation;
+ }
}
private boolean canSnapAt(int currentScrollY) {
diff --git a/src/com/android/launcher3/allapps/SearchUiManager.java b/src/com/android/launcher3/allapps/SearchUiManager.java
index bb17ed5..d8568f8 100644
--- a/src/com/android/launcher3/allapps/SearchUiManager.java
+++ b/src/com/android/launcher3/allapps/SearchUiManager.java
@@ -37,7 +37,7 @@
/**
* Notifies the search manager to close any active search session.
*/
- void reset();
+ void resetSearch();
/**
* Called before dispatching a key event, in case the search manager wants to initialize
diff --git a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
index a56c8b8..dd80dac 100644
--- a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
+++ b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
@@ -15,6 +15,12 @@
*/
package com.android.launcher3.allapps.search;
+import static android.view.View.MeasureSpec.EXACTLY;
+import static android.view.View.MeasureSpec.getSize;
+import static android.view.View.MeasureSpec.makeMeasureSpec;
+
+import static com.android.launcher3.graphics.IconNormalizer.ICON_VISIBLE_AREA_FACTOR;
+
import android.content.Context;
import android.graphics.Rect;
import android.support.animation.FloatValueHolder;
@@ -29,7 +35,7 @@
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.View;
-import android.widget.FrameLayout;
+import android.view.ViewGroup.MarginLayoutParams;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.ExtendedEditText;
@@ -47,20 +53,16 @@
/**
* Layout to contain the All-apps search UI.
*/
-public class AppsSearchContainerLayout extends FrameLayout
+public class AppsSearchContainerLayout extends ExtendedEditText
implements SearchUiManager, AllAppsSearchBarController.Callbacks,
AllAppsStore.OnUpdateListener {
+
private final Launcher mLauncher;
- private final int mMinHeight;
- private final int mSearchBoxHeight;
private final AllAppsSearchBarController mSearchBarController;
private final SpannableStringBuilder mSearchQueryBuilder;
- private ExtendedEditText mSearchInput;
private AlphabeticalAppsList mApps;
- private View mDivider;
- private HeaderElevationController mElevationController;
private AllAppsContainerView mAppsView;
private SpringAnimation mSpring;
@@ -76,39 +78,22 @@
super(context, attrs, defStyleAttr);
mLauncher = Launcher.getLauncher(context);
- mMinHeight = getResources().getDimensionPixelSize(R.dimen.all_apps_search_bar_height);
- mSearchBoxHeight = getResources()
- .getDimensionPixelSize(R.dimen.all_apps_search_bar_field_height);
mSearchBarController = new AllAppsSearchBarController();
mSearchQueryBuilder = new SpannableStringBuilder();
Selection.setSelection(mSearchQueryBuilder, 0);
- // Note: This spring does nothing.
- mSpring = new SpringAnimation(new FloatValueHolder()).setSpring(new SpringForce(0));
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mSearchInput = findViewById(R.id.search_box_input);
- mDivider = findViewById(R.id.search_divider);
- mElevationController = new HeaderElevationController(mDivider);
-
// Update the hint to contain the icon.
// Prefix the original hint with two spaces. The first space gets replaced by the icon
// using span. The second space is used for a singe space character between the hint
// and the icon.
- SpannableString spanned = new SpannableString(" " + mSearchInput.getHint());
+ SpannableString spanned = new SpannableString(" " + getHint());
spanned.setSpan(new TintedDrawableSpan(getContext(), R.drawable.ic_allapps_search),
0, 1, Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
- mSearchInput.setHint(spanned);
+ setHint(spanned);
- DeviceProfile dp = mLauncher.getDeviceProfile();
- if (!dp.isVerticalBarLayout()) {
- LayoutParams lp = (LayoutParams) mDivider.getLayoutParams();
- lp.leftMargin = lp.rightMargin = dp.edgeMarginPx;
- }
+ // Note: This spring does nothing.
+ mSpring = new SpringAnimation(new FloatValueHolder()).setSpring(new SpringForce(0));
}
@Override
@@ -125,21 +110,39 @@
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ // Update the width to match the grid padding
DeviceProfile dp = mLauncher.getDeviceProfile();
- if (!dp.isVerticalBarLayout()) {
- getLayoutParams().height = dp.getInsets().top + mMinHeight;
- }
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ int myRequestedWidth = getSize(widthMeasureSpec);
+ int rowWidth = myRequestedWidth - mAppsView.getActiveRecyclerView().getPaddingLeft()
+ - mAppsView.getActiveRecyclerView().getPaddingRight();
+
+ int cellWidth = DeviceProfile.calculateCellWidth(rowWidth, dp.inv.numHotseatIcons);
+ int iconVisibleSize = Math.round(ICON_VISIBLE_AREA_FACTOR * dp.iconSizePx);
+ int iconPadding = cellWidth - iconVisibleSize;
+
+ int myWidth = rowWidth - iconPadding + getPaddingLeft() + getPaddingRight();
+ super.onMeasure(makeMeasureSpec(myWidth, EXACTLY), heightMeasureSpec);
}
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+
+ // Shift the widget horizontally so that its centered in the parent (b/63428078)
+ View parent = (View) getParent();
+ int availableWidth = parent.getWidth() - parent.getPaddingLeft() - parent.getPaddingRight();
+ int myWidth = right - left;
+ int expectedLeft = parent.getPaddingLeft() + (availableWidth - myWidth) / 2;
+ int shift = expectedLeft - left;
+ setTranslationX(shift);
+ }
@Override
public void initialize(AllAppsContainerView appsView) {
mApps = appsView.getApps();
mAppsView = appsView;
- appsView.addElevationController(mElevationController);
mSearchBarController.initialize(
- new DefaultAppSearchAlgorithm(mApps.getApps()), mSearchInput, mLauncher, this);
+ new DefaultAppSearchAlgorithm(mApps.getApps()), this, mLauncher, this);
}
@Override
@@ -153,8 +156,7 @@
}
@Override
- public void reset() {
- mElevationController.reset();
+ public void resetSearch() {
mSearchBarController.reset();
}
@@ -200,7 +202,6 @@
}
private void notifyResultChanged() {
- mElevationController.reset();
mAppsView.onSearchResultsChanged();
}
@@ -214,9 +215,9 @@
if (!dp.isVerticalBarLayout()) {
Rect insets = dp.getInsets();
int hotseatBottom = bottom - dp.hotseatBarBottomPaddingPx - insets.bottom;
- int searchTopMargin = insets.top + (mMinHeight - mSearchBoxHeight)
- + ((MarginLayoutParams) getLayoutParams()).bottomMargin;
- listener.onScrollRangeChanged(hotseatBottom - searchTopMargin);
+ MarginLayoutParams mlp = ((MarginLayoutParams) getLayoutParams());
+ int myBot = mlp.topMargin + (int) getTranslationY() + mlp.height;
+ listener.onScrollRangeChanged(hotseatBottom - myBot);
} else {
listener.onScrollRangeChanged(bottom);
}
diff --git a/src/com/android/launcher3/allapps/search/HeaderElevationController.java b/src/com/android/launcher3/allapps/search/HeaderElevationController.java
deleted file mode 100644
index 7cd32b2..0000000
--- a/src/com/android/launcher3/allapps/search/HeaderElevationController.java
+++ /dev/null
@@ -1,81 +0,0 @@
-package com.android.launcher3.allapps.search;
-
-import android.content.res.Resources;
-import android.graphics.Outline;
-import android.support.v7.widget.RecyclerView;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewOutlineProvider;
-
-import com.android.launcher3.BaseRecyclerView;
-import com.android.launcher3.R;
-
-/**
- * Helper class for controlling the header elevation in response to RecyclerView scroll.
- */
-public class HeaderElevationController extends RecyclerView.OnScrollListener {
-
- private final View mHeader;
- private final View mHeaderChild;
- private final float mMaxElevation;
- private final float mScrollToElevation;
-
- private int mCurrentY = 0;
-
- public HeaderElevationController(View header) {
- mHeader = header;
- final Resources res = mHeader.getContext().getResources();
- mMaxElevation = res.getDimension(R.dimen.all_apps_header_max_elevation);
- mScrollToElevation = res.getDimension(R.dimen.all_apps_header_scroll_to_elevation);
-
- // We need to provide a custom outline so the shadow only appears on the bottom edge.
- // The top, left and right edges are all extended out to match parent's edge, so that
- // the shadow is clipped by the parent.
- final ViewOutlineProvider vop = new ViewOutlineProvider() {
- @Override
- public void getOutline(View view, Outline outline) {
- // Set the left and top to be at the parents edge. Since the coordinates are
- // relative to this view,
- // (x = -view.getLeft()) for this view => (x = 0) for parent
- final int left = -view.getLeft();
- final int top = -view.getTop();
-
- // Since the view is centered align, the spacing on left and right are same.
- // Add same spacing on the right to reach parent's edge.
- final int right = view.getWidth() - left;
- final int bottom = view.getHeight();
- final int offset = (int) mMaxElevation;
- outline.setRect(left - offset, top - offset, right + offset, bottom);
- }
- };
- mHeader.setOutlineProvider(vop);
- mHeaderChild = ((ViewGroup) mHeader).getChildAt(0);
- }
-
- public void reset() {
- mCurrentY = 0;
- onScroll(mCurrentY);
- }
-
- @Override
- public final void onScrolled(RecyclerView recyclerView, int dx, int dy) {
- mCurrentY = ((BaseRecyclerView) recyclerView).getCurrentScrollY();
- onScroll(mCurrentY);
- }
-
- private void onScroll(int scrollY) {
- float elevationPct = Math.min(scrollY, mScrollToElevation) / mScrollToElevation;
- float newElevation = mMaxElevation * elevationPct;
- if (Float.compare(mHeader.getElevation(), newElevation) != 0) {
- mHeader.setElevation(newElevation);
-
- // To simulate a scrolling effect for the header, we translate the header down, and
- // its content up by the same amount, so that it gets clipped by the parent, making it
- // look like the content was scrolled out of the view.
- int shift = Math.min(mHeader.getHeight(), scrollY);
- mHeader.setTranslationY(-shift);
- mHeaderChild.setTranslationY(shift);
- }
- }
-
-}
diff --git a/src/com/android/launcher3/anim/SpringAnimationHandler.java b/src/com/android/launcher3/anim/SpringAnimationHandler.java
deleted file mode 100644
index 29a2430..0000000
--- a/src/com/android/launcher3/anim/SpringAnimationHandler.java
+++ /dev/null
@@ -1,265 +0,0 @@
-/*
- * Copyright (C) 2017 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.anim;
-
-import android.support.animation.FloatPropertyCompat;
-import android.support.animation.SpringAnimation;
-import android.support.animation.SpringForce;
-import android.support.annotation.IntDef;
-import android.util.Log;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
-import android.view.View;
-
-import com.android.launcher3.R;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-
-/**
- * Handler class that manages springs for a set of views that should all move based on the same
- * {@link MotionEvent}s.
- *
- * Supports setting either X or Y velocity on the list of springs added to this handler.
- */
-public class SpringAnimationHandler<T> {
-
- private static final String TAG = "SpringAnimationHandler";
- private static final boolean DEBUG = false;
-
- private static final float VELOCITY_DAMPING_FACTOR = 0.175f;
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({Y_DIRECTION, X_DIRECTION})
- public @interface Direction {}
- public static final int Y_DIRECTION = 0;
- public static final int X_DIRECTION = 1;
- private int mVelocityDirection;
-
- private VelocityTracker mVelocityTracker;
- private float mCurrentVelocity = 0;
- private boolean mShouldComputeVelocity = false;
-
- private AnimationFactory<T> mAnimationFactory;
-
- private ArrayList<SpringAnimation> mAnimations = new ArrayList<>();
-
- /**
- * @param direction Either {@link #X_DIRECTION} or {@link #Y_DIRECTION}.
- * Determines which direction we use to calculate and set the velocity.
- * @param factory The AnimationFactory is responsible for initializing and updating the
- * SpringAnimations added to this class.
- */
- public SpringAnimationHandler(@Direction int direction, AnimationFactory<T> factory) {
- mVelocityDirection = direction;
- mAnimationFactory = factory;
- }
-
- /**
- * Adds a spring to the list of springs handled by this class.
- * @param spring The new spring to be added.
- * @param setDefaultValues If True, sets the spring to the default
- * {@link AnimationFactory} values.
- */
- public void add(SpringAnimation spring, boolean setDefaultValues) {
- if (setDefaultValues) {
- mAnimationFactory.setDefaultValues(spring);
- }
- spring.setStartVelocity(mCurrentVelocity);
- mAnimations.add(spring);
- }
-
- public AnimationFactory<T> getFactory() {
- return mAnimationFactory;
- }
-
- /**
- * Adds a new or recycled animation to the list of springs handled by this class.
- *
- * @param view The view the spring is attached to.
- * @param object Used to initialize and update the spring.
- */
- public void add(View view, T object) {
- SpringAnimation spring = (SpringAnimation) view.getTag(R.id.spring_animation_tag);
- if (spring == null) {
- spring = mAnimationFactory.initialize(object);
- view.setTag(R.id.spring_animation_tag, spring);
- }
- mAnimationFactory.update(spring, object);
- add(spring, false /* setDefaultValues */);
- }
-
- /**
- * Stops and removes the spring attached to {@param view}.
- */
- public void remove(View view) {
- remove((SpringAnimation) view.getTag(R.id.spring_animation_tag));
- }
-
- public void remove(SpringAnimation animation) {
- if (animation.canSkipToEnd()) {
- animation.skipToEnd();
- }
- mAnimations.remove(animation);
- }
-
- public void addMovement(MotionEvent event) {
- int action = event.getActionMasked();
- if (DEBUG) Log.d(TAG, "addMovement#action=" + action);
- switch (action) {
- case MotionEvent.ACTION_CANCEL:
- case MotionEvent.ACTION_DOWN:
- reset();
- break;
- }
-
- getVelocityTracker().addMovement(event);
- mShouldComputeVelocity = true;
- }
-
- public void animateToFinalPosition(float position, int startValue) {
- animateToFinalPosition(position, startValue, mShouldComputeVelocity);
- }
-
- /**
- * @param position The final animation position.
- * @param startValue < 0 if scrolling from start to end; > 0 if scrolling from end to start
- * The magnitude of the number changes how the spring will move.
- * @param setVelocity If true, we set the velocity to {@link #mCurrentVelocity} before
- * starting the animation.
- */
- private void animateToFinalPosition(float position, int startValue, boolean setVelocity) {
- if (DEBUG) {
- Log.d(TAG, "animateToFinalPosition#position=" + position + ", startValue=" + startValue);
- }
-
- if (mShouldComputeVelocity) {
- mCurrentVelocity = computeVelocity();
- }
-
- int size = mAnimations.size();
- for (int i = 0; i < size; ++i) {
- mAnimations.get(i).setStartValue(startValue);
- if (setVelocity) {
- mAnimations.get(i).setStartVelocity(mCurrentVelocity);
- }
- mAnimations.get(i).animateToFinalPosition(position);
- }
-
- reset();
- }
-
- /**
- * Similar to {@link #animateToFinalPosition(float, int)}, but used in cases where we want to
- * manually set the velocity.
- */
- public void animateToPositionWithVelocity(float position, int startValue, float velocity) {
- if (DEBUG) {
- Log.d(TAG, "animateToPosition#pos=" + position + ", start=" + startValue
- + ", velocity=" + velocity);
- }
-
- mCurrentVelocity = velocity;
- mShouldComputeVelocity = false;
- animateToFinalPosition(position, startValue, true);
- }
-
-
- public boolean isRunning() {
- // All the animations run at the same time so we can just check the first one.
- return !mAnimations.isEmpty() && mAnimations.get(0).isRunning();
- }
-
- public void skipToEnd() {
- if (DEBUG) Log.d(TAG, "setStartVelocity#skipToEnd");
- if (DEBUG) Log.v(TAG, "setStartVelocity#skipToEnd", new Exception());
-
- int size = mAnimations.size();
- for (int i = 0; i < size; ++i) {
- if (mAnimations.get(i).canSkipToEnd()) {
- mAnimations.get(i).skipToEnd();
- }
- }
- }
-
- public void reset() {
- if (mVelocityTracker != null) {
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- }
- mCurrentVelocity = 0;
- mShouldComputeVelocity = false;
- }
-
-
- private float computeVelocity() {
- getVelocityTracker().computeCurrentVelocity(1000 /* millis */);
-
- float velocity = isVerticalDirection()
- ? getVelocityTracker().getYVelocity()
- : getVelocityTracker().getXVelocity();
- velocity *= VELOCITY_DAMPING_FACTOR;
-
- if (DEBUG) Log.d(TAG, "computeVelocity=" + velocity);
- return velocity;
- }
-
- private boolean isVerticalDirection() {
- return mVelocityDirection == Y_DIRECTION;
- }
-
- private VelocityTracker getVelocityTracker() {
- if (mVelocityTracker == null) {
- mVelocityTracker = VelocityTracker.obtain();
- }
- return mVelocityTracker;
- }
-
- /**
- * This interface is used to initialize and update the SpringAnimations added to the
- * {@link SpringAnimationHandler}.
- *
- * @param <T> The object that each SpringAnimation is attached to.
- */
- public interface AnimationFactory<T> {
-
- /**
- * Initializes a new Spring for {@param object}.
- */
- SpringAnimation initialize(T object);
-
- /**
- * Updates the value of {@param spring} based on {@param object}.
- */
- void update(SpringAnimation spring, T object);
-
- /**
- * Sets the factory default values for the given {@param spring}.
- */
- void setDefaultValues(SpringAnimation spring);
- }
-
- /**
- * Helper method to create a new SpringAnimation for {@param view}.
- */
- public static SpringAnimation forView(View view, FloatPropertyCompat property, float finalPos) {
- SpringAnimation spring = new SpringAnimation(view, property, finalPos);
- spring.setSpring(new SpringForce(finalPos));
- return spring;
- }
-
-}
diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java
index e494bea..28645dc 100644
--- a/src/com/android/launcher3/config/BaseFlags.java
+++ b/src/com/android/launcher3/config/BaseFlags.java
@@ -35,8 +35,6 @@
public static final boolean LAUNCHER3_DIRECT_SCROLL = true;
// When enabled the promise icon is visible in all apps while installation an app.
public static final boolean LAUNCHER3_PROMISE_APPS_IN_ALL_APPS = false;
- // When enabled allows use of physics based motions in the Launcher.
- public static final boolean LAUNCHER3_PHYSICS = true;
// When enabled allows use of spring motions on the icons.
public static final boolean LAUNCHER3_SPRING_ICONS = true;
diff --git a/src/com/android/launcher3/dragndrop/AddItemActivity.java b/src/com/android/launcher3/dragndrop/AddItemActivity.java
index db199c1..95e1034 100644
--- a/src/com/android/launcher3/dragndrop/AddItemActivity.java
+++ b/src/com/android/launcher3/dragndrop/AddItemActivity.java
@@ -16,6 +16,11 @@
package com.android.launcher3.dragndrop;
+import static com.android.launcher3.logging.LoggerUtils.newCommandAction;
+import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
+import static com.android.launcher3.logging.LoggerUtils.newItemTarget;
+import static com.android.launcher3.logging.LoggerUtils.newLauncherEvent;
+
import android.annotation.TargetApi;
import android.app.ActivityOptions;
import android.appwidget.AppWidgetManager;
@@ -23,7 +28,6 @@
import android.content.ClipDescription;
import android.content.Intent;
import android.content.pm.LauncherApps.PinItemRequest;
-import android.content.res.Configuration;
import android.graphics.Canvas;
import android.graphics.Point;
import android.graphics.PointF;
@@ -43,7 +47,6 @@
import com.android.launcher3.LauncherAppWidgetHost;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.compat.LauncherAppsCompatVO;
import com.android.launcher3.model.WidgetItem;
@@ -55,11 +58,6 @@
import com.android.launcher3.widget.WidgetHostViewLoader;
import com.android.launcher3.widget.WidgetImageView;
-import static com.android.launcher3.logging.LoggerUtils.newCommandAction;
-import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
-import static com.android.launcher3.logging.LoggerUtils.newItemTarget;
-import static com.android.launcher3.logging.LoggerUtils.newLauncherEvent;
-
@TargetApi(Build.VERSION_CODES.O)
public class AddItemActivity extends BaseActivity implements OnLongClickListener, OnTouchListener {
@@ -154,15 +152,7 @@
.setPackage(getPackageName())
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
- if (!getResources().getBoolean(R.bool.allow_rotation) &&
- !Utilities.isAllowRotationPrefEnabled(this) &&
- (getResources().getConfiguration().orientation ==
- Configuration.ORIENTATION_LANDSCAPE && !isInMultiWindowMode())) {
- // If we are starting the drag in landscape even though home is locked in portrait,
- // restart the home activity to temporarily allow rotation.
- homeIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
- }
-
+ listener.initWhenReady();
startActivity(homeIntent,
ActivityOptions.makeCustomAnimation(this, 0, android.R.anim.fade_out).toBundle());
mFinishOnPause = true;
diff --git a/src/com/android/launcher3/dragndrop/BaseItemDragListener.java b/src/com/android/launcher3/dragndrop/BaseItemDragListener.java
index 9638a75..df4a7c1 100644
--- a/src/com/android/launcher3/dragndrop/BaseItemDragListener.java
+++ b/src/com/android/launcher3/dragndrop/BaseItemDragListener.java
@@ -17,6 +17,8 @@
package com.android.launcher3.dragndrop;
import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.states.RotationHelper.REQUEST_LOCK;
+import static com.android.launcher3.states.RotationHelper.REQUEST_NONE;
import android.content.ClipDescription;
import android.content.Intent;
@@ -79,6 +81,7 @@
AbstractFloatingView.closeAllOpenViews(launcher, alreadyOnHome);
launcher.getStateManager().goToState(NORMAL, alreadyOnHome /* animated */);
launcher.getDragLayer().setOnDragListener(this);
+ launcher.getRotationHelper().setStateHandlerRequest(REQUEST_LOCK);
mLauncher = launcher;
mDragController = launcher.getDragController();
@@ -157,6 +160,7 @@
}
private void postCleanup() {
+ clearReference();
if (mLauncher != null) {
// Remove any drag params from the launcher intent since the drag operation is complete.
Intent newIntent = new Intent(mLauncher.getIntent());
@@ -164,16 +168,12 @@
mLauncher.setIntent(newIntent);
}
- new Handler(Looper.getMainLooper()).post(new Runnable() {
- @Override
- public void run() {
- removeListener();
- }
- });
+ new Handler(Looper.getMainLooper()).post(this::removeListener);
}
public void removeListener() {
if (mLauncher != null) {
+ mLauncher.getRotationHelper().setStateHandlerRequest(REQUEST_NONE);
mLauncher.getDragLayer().setOnDragListener(null);
}
}
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index 301070c..f5d0b24 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -709,12 +709,14 @@
public void onViewAdded(View child) {
super.onViewAdded(child);
updateChildIndices();
+ UiFactory.onLauncherStateOrFocusChanged(mLauncher);
}
@Override
public void onViewRemoved(View child) {
super.onViewRemoved(child);
updateChildIndices();
+ UiFactory.onLauncherStateOrFocusChanged(mLauncher);
}
@Override
diff --git a/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java b/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
index 1c6f77c..5576d91 100644
--- a/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
+++ b/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
@@ -36,7 +36,7 @@
import com.android.launcher3.R;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.folder.PreviewBackground;
-import com.android.launcher3.uioverrides.UiFactory;
+import com.android.launcher3.graphics.BitmapRenderer;
import com.android.launcher3.util.Preconditions;
/**
@@ -113,7 +113,7 @@
final float previewShiftX = shiftFactor * previewWidth;
final float previewShiftY = shiftFactor * previewHeight;
- Bitmap previewBitmap = UiFactory.createFromRenderer(previewWidth, previewHeight, false,
+ Bitmap previewBitmap = BitmapRenderer.createHardwareBitmap(previewWidth, previewHeight,
(canvas) -> {
int count = canvas.save();
canvas.translate(previewShiftX, previewShiftY);
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 13926db..6c94273 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -63,6 +63,7 @@
import com.android.launcher3.dragndrop.BaseItemDragListener;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.DragView;
+import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.widget.PendingAddShortcutInfo;
@@ -159,14 +160,14 @@
.inflate(resId, group, false);
icon.setClipToPadding(false);
- icon.mFolderName = (BubbleTextView) icon.findViewById(R.id.folder_icon_name);
+ icon.mFolderName = icon.findViewById(R.id.folder_icon_name);
icon.mFolderName.setText(folderInfo.title);
icon.mFolderName.setCompoundDrawablePadding(0);
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) icon.mFolderName.getLayoutParams();
lp.topMargin = grid.iconSizePx + grid.iconDrawablePaddingPx;
icon.setTag(folderInfo);
- icon.setOnClickListener(launcher);
+ icon.setOnClickListener(ItemClickHandler.INSTANCE);
icon.mInfo = folderInfo;
icon.mLauncher = launcher;
icon.mBadgeRenderer = launcher.getDeviceProfile().mBadgeRenderer;
diff --git a/src/com/android/launcher3/graphics/BitmapRenderer.java b/src/com/android/launcher3/graphics/BitmapRenderer.java
index 4652ded..3d11c44 100644
--- a/src/com/android/launcher3/graphics/BitmapRenderer.java
+++ b/src/com/android/launcher3/graphics/BitmapRenderer.java
@@ -15,9 +15,40 @@
*/
package com.android.launcher3.graphics;
+import android.annotation.TargetApi;
+import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.Picture;
+import android.os.Build;
-public interface BitmapRenderer {
+import com.android.launcher3.Utilities;
- void render(Canvas out);
+public class BitmapRenderer {
+
+ public static final boolean USE_HARDWARE_BITMAP = Utilities.ATLEAST_P;
+
+ public static Bitmap createSoftwareBitmap(int width, int height, Renderer renderer) {
+ Bitmap result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ renderer.draw(new Canvas(result));
+ return result;
+ }
+
+ @TargetApi(Build.VERSION_CODES.P)
+ public static Bitmap createHardwareBitmap(int width, int height, Renderer renderer) {
+ if (!USE_HARDWARE_BITMAP) {
+ return createSoftwareBitmap(width, height, renderer);
+ }
+
+ Picture picture = new Picture();
+ renderer.draw(picture.beginRecording(width, height));
+ picture.endRecording();
+ return Bitmap.createBitmap(picture);
+ }
+
+ /**
+ * Interface representing a bitmap draw operation.
+ */
+ public interface Renderer {
+ void draw(Canvas out);
+ }
}
diff --git a/src/com/android/launcher3/graphics/DragPreviewProvider.java b/src/com/android/launcher3/graphics/DragPreviewProvider.java
index b770785..e60a2c7 100644
--- a/src/com/android/launcher3/graphics/DragPreviewProvider.java
+++ b/src/com/android/launcher3/graphics/DragPreviewProvider.java
@@ -24,19 +24,17 @@
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
-import android.graphics.Region.Op;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.view.View;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.Launcher;
-import com.android.launcher3.widget.LauncherAppWidgetHostView;
import com.android.launcher3.R;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.folder.FolderIcon;
-import com.android.launcher3.uioverrides.UiFactory;
import com.android.launcher3.util.UiThreadHelper;
+import com.android.launcher3.widget.LauncherAppWidgetHostView;
import java.nio.ByteBuffer;
@@ -119,28 +117,26 @@
* Responsibility for the bitmap is transferred to the caller.
*/
public Bitmap createDragBitmap() {
- float scale = 1f;
int width = mView.getWidth();
int height = mView.getHeight();
- boolean forceSoftwareRenderer = false;
if (mView instanceof BubbleTextView) {
Drawable d = ((BubbleTextView) mView).getIcon();
Rect bounds = getDrawableBounds(d);
width = bounds.width();
height = bounds.height();
} else if (mView instanceof LauncherAppWidgetHostView) {
- scale = ((LauncherAppWidgetHostView) mView).getScaleToFit();
+ float scale = ((LauncherAppWidgetHostView) mView).getScaleToFit();
width = (int) (mView.getWidth() * scale);
height = (int) (mView.getHeight() * scale);
// Use software renderer for widgets as we know that they already work
- forceSoftwareRenderer = true;
+ return BitmapRenderer.createSoftwareBitmap(width + blurSizeOutline,
+ height + blurSizeOutline, (c) -> drawDragView(c, scale));
}
- final float scaleFinal = scale;
- return UiFactory.createFromRenderer(width + blurSizeOutline, height + blurSizeOutline,
- forceSoftwareRenderer, (c) -> drawDragView(c, scaleFinal));
+ return BitmapRenderer.createHardwareBitmap(width + blurSizeOutline,
+ height + blurSizeOutline, (c) -> drawDragView(c, 1));
}
public final void generateDragOutline(Bitmap preview) {
diff --git a/src/com/android/launcher3/graphics/HolographicOutlineHelper.java b/src/com/android/launcher3/graphics/HolographicOutlineHelper.java
deleted file mode 100644
index ebfe1e7..0000000
--- a/src/com/android/launcher3/graphics/HolographicOutlineHelper.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Copyright (C) 2008 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.graphics;
-
-import static com.android.launcher3.ItemInfoWithIcon.FLAG_ADAPTIVE_ICON;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.BlurMaskFilter;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffXfermode;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.util.SparseArray;
-
-import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.ItemInfoWithIcon;
-import com.android.launcher3.R;
-import com.android.launcher3.uioverrides.UiFactory;
-
-/**
- * Utility class to generate shadow and outline effect, which are used for click feedback
- * and drag-n-drop respectively.
- */
-public class HolographicOutlineHelper {
-
- /**
- * Bitmap used as shadow for Adaptive icons
- */
- public static final Bitmap ADAPTIVE_ICON_SHADOW_BITMAP =
- Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8);
-
- private static HolographicOutlineHelper sInstance;
-
- private final Canvas mCanvas = new Canvas();
- private final Paint mBlurPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
- private final Paint mErasePaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
-
- private final float mShadowBitmapShift;
- private final BlurMaskFilter mShadowBlurMaskFilter;
-
- // We have 4 different icon sizes: homescreen, hotseat, folder & all-apps
- private final SparseArray<Bitmap> mBitmapCache = new SparseArray<>(4);
-
- private HolographicOutlineHelper(Context context) {
- mShadowBitmapShift = context.getResources().getDimension(R.dimen.blur_size_click_shadow);
- mShadowBlurMaskFilter = new BlurMaskFilter(mShadowBitmapShift, BlurMaskFilter.Blur.NORMAL);
- mErasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
- }
-
- public static HolographicOutlineHelper getInstance(Context context) {
- if (sInstance == null) {
- sInstance = new HolographicOutlineHelper(context.getApplicationContext());
- }
- return sInstance;
- }
-
- public Bitmap createMediumDropShadow(BubbleTextView view) {
- if (view.getTag() instanceof ItemInfoWithIcon &&
- ((((ItemInfoWithIcon) view.getTag()).runtimeStatusFlags & FLAG_ADAPTIVE_ICON)
- != 0)) {
- return ADAPTIVE_ICON_SHADOW_BITMAP;
- }
- Drawable drawable = view.getIcon();
- if (drawable == null) {
- return null;
- }
-
- float scaleX = view.getScaleX();
- float scaleY = view.getScaleY();
- Rect rect = drawable.getBounds();
-
- int bitmapWidth = (int) (rect.width() * scaleX);
- int bitmapHeight = (int) (rect.height() * scaleY);
- if (bitmapHeight <= 0 || bitmapWidth <= 0) {
- return null;
- }
-
- int key = (bitmapWidth << 16) | bitmapHeight;
- Bitmap cache = mBitmapCache.get(key);
- if (cache == null) {
- cache = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ALPHA_8);
- mCanvas.setBitmap(cache);
- mBitmapCache.put(key, cache);
- } else {
- mCanvas.setBitmap(cache);
- mCanvas.drawColor(Color.BLACK, PorterDuff.Mode.CLEAR);
- }
-
- int saveCount = mCanvas.save();
- mCanvas.scale(scaleX, scaleY);
- mCanvas.translate(-rect.left, -rect.top);
- if (!UiFactory.USE_HARDWARE_BITMAP) {
- // TODO: Outline generation requires alpha extraction, which is costly for
- // hardware bitmaps. Instead use canvas layer operations once its available.
- drawable.draw(mCanvas);
- }
- mCanvas.restoreToCount(saveCount);
- mCanvas.setBitmap(null);
-
- mBlurPaint.setMaskFilter(mShadowBlurMaskFilter);
-
- int extraSize = (int) (2 * mShadowBitmapShift);
-
- int resultWidth = bitmapWidth + extraSize;
- int resultHeight = bitmapHeight + extraSize;
- key = (resultWidth << 16) | resultHeight;
- Bitmap result = mBitmapCache.get(key);
- if (result == null) {
- result = Bitmap.createBitmap(resultWidth, resultHeight, Bitmap.Config.ALPHA_8);
- mCanvas.setBitmap(result);
- } else {
- // Use put instead of delete, to avoid unnecessary shrinking of cache array
- mBitmapCache.put(key, null);
- mCanvas.setBitmap(result);
- mCanvas.drawColor(Color.BLACK, PorterDuff.Mode.CLEAR);
- }
- mCanvas.drawBitmap(cache, mShadowBitmapShift, mShadowBitmapShift, mBlurPaint);
- mCanvas.setBitmap(null);
- return result;
- }
-
- public void recycleShadowBitmap(Bitmap bitmap) {
- if (bitmap != null && bitmap != ADAPTIVE_ICON_SHADOW_BITMAP) {
- mBitmapCache.put((bitmap.getWidth() << 16) | bitmap.getHeight(), bitmap);
- }
- }
-}
diff --git a/src/com/android/launcher3/graphics/IconNormalizer.java b/src/com/android/launcher3/graphics/IconNormalizer.java
index bd20c87..5d99ba0 100644
--- a/src/com/android/launcher3/graphics/IconNormalizer.java
+++ b/src/com/android/launcher3/graphics/IconNormalizer.java
@@ -61,6 +61,9 @@
private static final float PIXEL_DIFF_PERCENTAGE_THRESHOLD = 0.005f;
private static final float SCALE_NOT_INITIALIZED = 0;
+ // Ratio of the diameter of an normalized circular icon to the actual icon size.
+ public static final float ICON_VISIBLE_AREA_FACTOR = 0.92f;
+
private final int mMaxSize;
private final Bitmap mBitmap;
private final Bitmap mBitmapARGB;
diff --git a/src/com/android/launcher3/graphics/LauncherIcons.java b/src/com/android/launcher3/graphics/LauncherIcons.java
index 8abfdea..4a9cdd9 100644
--- a/src/com/android/launcher3/graphics/LauncherIcons.java
+++ b/src/com/android/launcher3/graphics/LauncherIcons.java
@@ -52,7 +52,6 @@
import com.android.launcher3.model.PackageItemInfo;
import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.shortcuts.ShortcutInfoCompat;
-import com.android.launcher3.uioverrides.UiFactory;
import com.android.launcher3.util.Provider;
import com.android.launcher3.util.Themes;
@@ -349,7 +348,7 @@
final ItemInfoWithIcon badge = getShortcutInfoBadge(shortcutInfo, cache);
result.color = badge.iconColor;
- result.icon = UiFactory.createFromRenderer(mIconBitmapSize, mIconBitmapSize, false, (c) -> {
+ result.icon = BitmapRenderer.createHardwareBitmap(mIconBitmapSize, mIconBitmapSize, (c) -> {
getShadowGenerator().recreateIcon(unbadgedfinal, c);
badgeWithDrawable(c, new FastBitmapDrawable(badge));
});
diff --git a/src/com/android/launcher3/graphics/PreloadIconDrawable.java b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
index ea55ba4..42ba191 100644
--- a/src/com/android/launcher3/graphics/PreloadIconDrawable.java
+++ b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
@@ -162,9 +162,9 @@
}
@Override
- public void draw(Canvas canvas) {
+ public void drawInternal(Canvas canvas, Rect bounds) {
if (mRanFinishAnimation) {
- super.draw(canvas);
+ super.drawInternal(canvas, bounds);
return;
}
@@ -172,15 +172,13 @@
mProgressPaint.setColor(mIndicatorColor);
mProgressPaint.setAlpha(mTrackAlpha);
if (mShadowBitmap != null) {
- canvas.drawBitmap(mShadowBitmap, getBounds().left, getBounds().top, mProgressPaint);
+ canvas.drawBitmap(mShadowBitmap, bounds.left, bounds.top, mProgressPaint);
}
canvas.drawPath(mScaledProgressPath, mProgressPaint);
int saveCount = canvas.save();
- Rect bounds = getBounds();
-
canvas.scale(mIconScale, mIconScale, bounds.exactCenterX(), bounds.exactCenterY());
- super.draw(canvas);
+ super.drawInternal(canvas, bounds);
canvas.restoreToCount(saveCount);
}
diff --git a/src/com/android/launcher3/model/BaseModelUpdateTask.java b/src/com/android/launcher3/model/BaseModelUpdateTask.java
index 9aa30e7..fcdc088 100644
--- a/src/com/android/launcher3/model/BaseModelUpdateTask.java
+++ b/src/com/android/launcher3/model/BaseModelUpdateTask.java
@@ -29,7 +29,6 @@
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.MultiHashMap;
import com.android.launcher3.widget.WidgetListRowEntry;
-import com.android.launcher3.widget.WidgetsListAdapter;
import java.util.ArrayList;
import java.util.concurrent.Executor;
@@ -80,19 +79,18 @@
*/
public final void scheduleCallbackTask(final CallbackTask task) {
final Callbacks callbacks = mModel.getCallback();
- mUiExecutor.execute(new Runnable() {
- public void run() {
- Callbacks cb = mModel.getCallback();
- if (callbacks == cb && cb != null) {
- task.execute(callbacks);
- }
+ mUiExecutor.execute(() -> {
+ Callbacks cb = mModel.getCallback();
+ if (callbacks == cb && cb != null) {
+ task.execute(callbacks);
}
});
}
public ModelWriter getModelWriter() {
- // Updates from model task, do not deal with icon position in hotseat.
- return mModel.getWriter(false /* hasVerticalHotseat */);
+ // Updates from model task, do not deal with icon position in hotseat. Also no need to
+ // verify changes as the ModelTasks always push the changes to callbacks
+ return mModel.getWriter(false /* hasVerticalHotseat */, false /* verifyChanges */);
}
diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index 8640401..fff1e69 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -106,6 +106,11 @@
public final WidgetsModel widgetsModel = new WidgetsModel();
/**
+ * Id when the model was last bound
+ */
+ public int lastBindId = 0;
+
+ /**
* Clears all the data
*/
public synchronized void clear() {
diff --git a/src/com/android/launcher3/model/LoaderResults.java b/src/com/android/launcher3/model/LoaderResults.java
index 5acc790..5d4a352 100644
--- a/src/com/android/launcher3/model/LoaderResults.java
+++ b/src/com/android/launcher3/model/LoaderResults.java
@@ -25,7 +25,6 @@
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetInfo;
-import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherModel.Callbacks;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.MainThreadExecutor;
@@ -37,7 +36,6 @@
import com.android.launcher3.util.MultiHashMap;
import com.android.launcher3.util.ViewOnDrawExecutor;
import com.android.launcher3.widget.WidgetListRowEntry;
-import com.android.launcher3.widget.WidgetsListAdapter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -100,6 +98,7 @@
workspaceItems.addAll(mBgDataModel.workspaceItems);
appWidgets.addAll(mBgDataModel.appWidgets);
orderedScreenIds.addAll(mBgDataModel.workspaceScreens);
+ mBgDataModel.lastBindId++;
}
final int currentScreen;
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index c615050..9d1ff83 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -16,6 +16,11 @@
package com.android.launcher3.model;
+import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER;
+import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SAFEMODE;
+import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED;
+import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
+
import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -25,7 +30,6 @@
import android.content.pm.LauncherActivityInfo;
import android.content.pm.PackageInstaller;
import android.graphics.Bitmap;
-import android.graphics.drawable.AdaptiveIconDrawable;
import android.os.Handler;
import android.os.Process;
import android.os.UserHandle;
@@ -36,7 +40,6 @@
import com.android.launcher3.AllAppsList;
import com.android.launcher3.AppInfo;
-import com.android.launcher3.ClickShadowView;
import com.android.launcher3.FolderInfo;
import com.android.launcher3.IconCache;
import com.android.launcher3.InstallShortcutReceiver;
@@ -75,11 +78,6 @@
import java.util.Map;
import java.util.concurrent.CancellationException;
-import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER;
-import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SAFEMODE;
-import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED;
-import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
-
/**
* Runnable for the thread that loads the contents of the launcher:
* - workspace icons
@@ -146,9 +144,7 @@
TraceHelper.beginSection(TAG);
try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
- TraceHelper.partitionSection(TAG, "step 1.1: loading UI resources");
- loadUiResources();
- TraceHelper.partitionSection(TAG, "step 1.2: loading workspace");
+ TraceHelper.partitionSection(TAG, "step 1.1: loading workspace");
loadWorkspace();
verifyNotStopped();
@@ -211,15 +207,6 @@
this.notify();
}
- public void loadUiResources() {
- if (Utilities.ATLEAST_OREO) {
- LauncherIcons li = LauncherIcons.obtain(mApp.getContext());
- ClickShadowView.setAdaptiveIconScaleFactor(li.getNormalizer()
- .getScale(new AdaptiveIconDrawable(null, null), null, null, null));
- li.recycle();
- }
- }
-
private void loadWorkspace() {
final Context context = mApp.getContext();
final ContentResolver contentResolver = context.getContentResolver();
diff --git a/src/com/android/launcher3/model/ModelWriter.java b/src/com/android/launcher3/model/ModelWriter.java
index 40e0f49..72c703b 100644
--- a/src/com/android/launcher3/model/ModelWriter.java
+++ b/src/com/android/launcher3/model/ModelWriter.java
@@ -21,12 +21,15 @@
import android.content.ContentValues;
import android.content.Context;
import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
import android.util.Log;
import com.android.launcher3.FolderInfo;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel;
+import com.android.launcher3.LauncherModel.Callbacks;
import com.android.launcher3.LauncherProvider;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherSettings.Favorites;
@@ -50,15 +53,23 @@
public static final boolean DEBUG_DELETE = true;
private final Context mContext;
+ private final LauncherModel mModel;
private final BgDataModel mBgDataModel;
+ private final Handler mUiHandler;
+
private final Executor mWorkerExecutor;
private final boolean mHasVerticalHotseat;
+ private final boolean mVerifyChanges;
- public ModelWriter(Context context, BgDataModel dataModel, boolean hasVerticalHotseat) {
+ public ModelWriter(Context context, LauncherModel model, BgDataModel dataModel,
+ boolean hasVerticalHotseat, boolean verifyChanges) {
mContext = context;
+ mModel = model;
mBgDataModel = dataModel;
mWorkerExecutor = new LooperExecutor(LauncherModel.getWorkerLooper());
mHasVerticalHotseat = hasVerticalHotseat;
+ mVerifyChanges = verifyChanges;
+ mUiHandler = new Handler(Looper.getMainLooper());
}
private void updateItemInfoProps(
@@ -214,15 +225,16 @@
item.id = Settings.call(cr, Settings.METHOD_NEW_ITEM_ID).getLong(Settings.EXTRA_VALUE);
writer.put(Favorites._ID, item.id);
- final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
- mWorkerExecutor.execute(new Runnable() {
- public void run() {
- cr.insert(Favorites.CONTENT_URI, writer.getValues(mContext));
+ ModelVerifier verifier = new ModelVerifier();
- synchronized (mBgDataModel) {
- checkItemInfoLocked(item.id, item, stackTrace);
- mBgDataModel.addItem(mContext, item, true);
- }
+ final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
+ mWorkerExecutor.execute(() -> {
+ cr.insert(Favorites.CONTENT_URI, writer.getValues(mContext));
+
+ synchronized (mBgDataModel) {
+ checkItemInfoLocked(item.id, item, stackTrace);
+ mBgDataModel.addItem(mContext, item, true);
+ verifier.verifyModel();
}
});
}
@@ -253,6 +265,7 @@
}
FileLog.d(TAG, "Finished deleting items");
}
+ ModelVerifier verifier = new ModelVerifier();
mWorkerExecutor.execute(() -> {
for (ItemInfo item : items) {
@@ -260,6 +273,7 @@
mContext.getContentResolver().delete(uri, null, null);
mBgDataModel.removeItem(mContext, item);
+ verifier.verifyModel();
}
});
}
@@ -273,6 +287,8 @@
FileLog.d(TAG, "Deleting folder " + info, new Exception());
}
+ ModelVerifier verifier = new ModelVerifier();
+
mWorkerExecutor.execute(() -> {
ContentResolver cr = mContext.getContentResolver();
cr.delete(LauncherSettings.Favorites.CONTENT_URI,
@@ -282,6 +298,7 @@
cr.delete(LauncherSettings.Favorites.getContentUri(info.id), null, null);
mBgDataModel.removeItem(mContext, info);
+ verifier.verifyModel();
});
}
@@ -336,6 +353,7 @@
private abstract class UpdateItemBaseRunnable implements Runnable {
private final StackTraceElement[] mStackTrace;
+ private final ModelVerifier mVerifier = new ModelVerifier();
UpdateItemBaseRunnable() {
mStackTrace = new Throwable().getStackTrace();
@@ -380,7 +398,45 @@
} else {
mBgDataModel.workspaceItems.remove(modelItem);
}
+ mVerifier.verifyModel();
}
}
}
+
+ /**
+ * Utility class to verify model updates are propagated properly to the callback.
+ */
+ public class ModelVerifier {
+
+ final int startId;
+
+ ModelVerifier() {
+ startId = mBgDataModel.lastBindId;
+ }
+
+ void verifyModel() {
+ if (!mVerifyChanges || mModel.getCallback() == null) {
+ return;
+ }
+
+ int executeId = mBgDataModel.lastBindId;
+
+ mUiHandler.post(() -> {
+ int currentId = mBgDataModel.lastBindId;
+ if (currentId > executeId) {
+ // Model was already bound after job was executed.
+ return;
+ }
+ if (executeId == startId) {
+ // Bound model has not changed during the job
+ return;
+ }
+ // Bound model was changed between submitting the job and executing the job
+ Callbacks callbacks = mModel.getCallback();
+ if (callbacks != null) {
+ callbacks.rebindModel();
+ }
+ });
+ }
+ }
}
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index b3ef7bb..e427a81 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -77,6 +77,7 @@
import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.shortcuts.DeepShortcutView;
import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
+import com.android.launcher3.touch.ItemLongClickListener;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.Themes;
@@ -907,12 +908,9 @@
@Override
public boolean onLongClick(View v) {
+ if (!ItemLongClickListener.canStartDrag(mLauncher)) return false;
// Return early if not the correct view
if (!(v.getParent() instanceof DeepShortcutView)) return false;
- // Return early if global dragging is not enabled
- if (!mLauncher.isDraggingEnabled()) return false;
- // Return early if an item is already being dragged (e.g. when long-pressing two shortcuts)
- if (mLauncher.getDragController().isDragging()) return false;
// Long clicked on a shortcut.
DeepShortcutView sv = (DeepShortcutView) v.getParent();
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutView.java b/src/com/android/launcher3/shortcuts/DeepShortcutView.java
index 450a690..9ad266b 100644
--- a/src/com/android/launcher3/shortcuts/DeepShortcutView.java
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutView.java
@@ -30,6 +30,7 @@
import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.Utilities;
import com.android.launcher3.popup.PopupContainerWithArrow;
+import com.android.launcher3.touch.ItemClickHandler;
/**
* A {@link android.widget.FrameLayout} that contains a {@link DeepShortcutView}.
@@ -120,7 +121,7 @@
mBubbleText.setText(usingLongLabel ? longLabel : mDetail.getShortLabel());
// TODO: Add the click handler to this view directly and not the child view.
- mBubbleText.setOnClickListener(Launcher.getLauncher(getContext()));
+ mBubbleText.setOnClickListener(ItemClickHandler.INSTANCE);
mBubbleText.setOnLongClickListener(container);
mBubbleText.setOnTouchListener(container);
}
diff --git a/src/com/android/launcher3/states/RotationHelper.java b/src/com/android/launcher3/states/RotationHelper.java
new file mode 100644
index 0000000..8f83648
--- /dev/null
+++ b/src/com/android/launcher3/states/RotationHelper.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2018 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.states;
+
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.provider.Settings.System.ACCELEROMETER_ROTATION;
+import static android.provider.Settings.System.getUriFor;
+
+import android.app.Activity;
+import android.content.ContentResolver;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.provider.Settings;
+
+import com.android.launcher3.R;
+
+/**
+ * Utility class to manage launcher rotation
+ */
+public class RotationHelper extends ContentObserver {
+
+ public static final int REQUEST_NONE = 0;
+ public static final int REQUEST_ROTATE = 1;
+ public static final int REQUEST_LOCK = 2;
+
+ private final Activity mActivity;
+ private final ContentResolver mCr;
+
+ private final boolean mIgnoreAutoRotateSettings;
+ private boolean mAutoRotateEnabled;
+
+ /**
+ * Rotation request made by {@link InternalStateHandler}. This supersedes any other request.
+ */
+ private int mStateHandlerRequest = REQUEST_NONE;
+ /**
+ * Rotation request made by a Launcher State
+ */
+ private int mCurrentStateRequest = REQUEST_NONE;
+
+ // This is used to defer setting rotation flags until the activity is being created
+ private boolean mInitialized;
+ public boolean mDestroyed;
+
+ private int mLastActivityFlags = -1;
+
+ public RotationHelper(Activity activity) {
+ super(new Handler());
+ mActivity = activity;
+
+ // On large devices we do not handle auto-rotate differently.
+ mIgnoreAutoRotateSettings = mActivity.getResources().getBoolean(R.bool.allow_rotation);
+ if (!mIgnoreAutoRotateSettings) {
+ mCr = mActivity.getContentResolver();
+ mCr.registerContentObserver(getUriFor(ACCELEROMETER_ROTATION), false, this);
+ mAutoRotateEnabled = Settings.System.getInt(mCr, ACCELEROMETER_ROTATION, 1) == 1;
+ } else {
+ mCr = null;
+ }
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ mAutoRotateEnabled = Settings.System.getInt(mCr, ACCELEROMETER_ROTATION, 1) == 1;
+ notifyChange();
+ }
+
+ public void setStateHandlerRequest(int request) {
+ if (mStateHandlerRequest != request) {
+ mStateHandlerRequest = request;
+ notifyChange();
+ }
+ }
+
+ public void setCurrentStateRequest(int request) {
+ if (mCurrentStateRequest != request) {
+ mCurrentStateRequest = request;
+ notifyChange();
+ }
+ }
+
+ public void initialize() {
+ if (!mInitialized) {
+ mInitialized = true;
+ notifyChange();
+ }
+ }
+
+ public void destroy() {
+ if (!mDestroyed) {
+ mDestroyed = true;
+ if (mCr != null) {
+ mCr.unregisterContentObserver(this);
+ }
+ }
+ }
+
+ private void notifyChange() {
+ if (!mInitialized || mDestroyed) {
+ return;
+ }
+
+ final int activityFlags;
+ if (mStateHandlerRequest != REQUEST_NONE) {
+ activityFlags = mStateHandlerRequest == REQUEST_LOCK ?
+ SCREEN_ORIENTATION_LOCKED : SCREEN_ORIENTATION_UNSPECIFIED;
+ } else if (mCurrentStateRequest == REQUEST_LOCK) {
+ activityFlags = SCREEN_ORIENTATION_LOCKED;
+ } else if (mIgnoreAutoRotateSettings || mCurrentStateRequest == REQUEST_ROTATE) {
+ activityFlags = SCREEN_ORIENTATION_UNSPECIFIED;
+ } else if (mAutoRotateEnabled) {
+ // If auto rotation is on, lock to device orientation
+ activityFlags = SCREEN_ORIENTATION_NOSENSOR;
+ } else {
+ // If auto rotation is off, allow rotation on the activity, in case the user is using
+ // forced rotation.
+ activityFlags = SCREEN_ORIENTATION_UNSPECIFIED;
+ }
+ if (activityFlags != mLastActivityFlags) {
+ mLastActivityFlags = activityFlags;
+ mActivity.setRequestedOrientation(mLastActivityFlags);
+ }
+ }
+}
diff --git a/src/com/android/launcher3/states/SpringLoadedState.java b/src/com/android/launcher3/states/SpringLoadedState.java
index 4f8456f..89a9e2d 100644
--- a/src/com/android/launcher3/states/SpringLoadedState.java
+++ b/src/com/android/launcher3/states/SpringLoadedState.java
@@ -16,10 +16,9 @@
package com.android.launcher3.states;
import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_TRANSITION_MS;
+import static com.android.launcher3.states.RotationHelper.REQUEST_LOCK;
-import android.content.pm.ActivityInfo;
import android.graphics.Rect;
-import android.os.Handler;
import android.view.View;
import com.android.launcher3.DeviceProfile;
@@ -38,10 +37,6 @@
FLAG_DISABLE_ACCESSIBILITY | FLAG_DISABLE_RESTORE | FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED |
FLAG_DISABLE_PAGE_CLIPPING | FLAG_PAGE_BACKGROUNDS;
- // Determines how long to wait after a rotation before restoring the screen orientation to
- // match the sensor state.
- private static final int RESTORE_SCREEN_ORIENTATION_DELAY = 500;
-
public SpringLoadedState(int id) {
super(id, ContainerType.OVERVIEW, SPRING_LOADED_TRANSITION_MS, STATE_FLAGS);
}
@@ -84,30 +79,16 @@
ws.showPageIndicatorAtCurrentScroll();
ws.getPageIndicator().setShouldAutoHide(false);
- // Lock the orientation:
- if (launcher.isRotationEnabled()) {
- launcher.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);
- }
-
// Prevent any Un/InstallShortcutReceivers from updating the db while we are
// in spring loaded mode
InstallShortcutReceiver.enableInstallQueue(InstallShortcutReceiver.FLAG_DRAG_AND_DROP);
+ launcher.getRotationHelper().setCurrentStateRequest(REQUEST_LOCK);
}
@Override
public void onStateDisabled(final Launcher launcher) {
launcher.getWorkspace().getPageIndicator().setShouldAutoHide(true);
- // Unlock rotation lock
- if (launcher.isRotationEnabled()) {
- new Handler().postDelayed(new Runnable() {
- @Override
- public void run() {
- launcher.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
- }
- }, RESTORE_SCREEN_ORIENTATION_DELAY);
- }
-
// Re-enable any Un/InstallShortcutReceiver and now process any queued items
InstallShortcutReceiver.disableAndFlushInstallQueue(
InstallShortcutReceiver.FLAG_DRAG_AND_DROP, launcher);
diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java
new file mode 100644
index 0000000..f2f5592
--- /dev/null
+++ b/src/com/android/launcher3/touch/ItemClickHandler.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2018 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.touch;
+
+import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_BY_PUBLISHER;
+import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER;
+import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_QUIET_USER;
+import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SAFEMODE;
+import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED;
+import static com.android.launcher3.Launcher.REQUEST_BIND_PENDING_APPWIDGET;
+import static com.android.launcher3.Launcher.REQUEST_RECONFIGURE_APPWIDGET;
+
+import android.app.AlertDialog;
+import android.content.Intent;
+import android.os.Process;
+import android.text.TextUtils;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Toast;
+
+import com.android.launcher3.AppInfo;
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.FolderInfo;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppWidgetInfo;
+import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.PromiseAppInfo;
+import com.android.launcher3.R;
+import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.compat.AppWidgetManagerCompat;
+import com.android.launcher3.folder.Folder;
+import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.util.PackageManagerHelper;
+import com.android.launcher3.widget.PendingAppWidgetHostView;
+import com.android.launcher3.widget.WidgetAddFlowHandler;
+
+/**
+ * Class for handling clicks on workspace and all-apps items
+ */
+public class ItemClickHandler {
+
+ /**
+ * Instance used for click handling on items
+ */
+ public static final OnClickListener INSTANCE = ItemClickHandler::onClick;
+
+ private static void onClick(View v) {
+ // Make sure that rogue clicks don't get through while allapps is launching, or after the
+ // view has detached (it's possible for this to happen if the view is removed mid touch).
+ if (v.getWindowToken() == null) {
+ return;
+ }
+
+ Launcher launcher = Launcher.getLauncher(v.getContext());
+ if (!launcher.getWorkspace().isFinishedSwitchingState()) {
+ return;
+ }
+
+ Object tag = v.getTag();
+ if (tag instanceof ShortcutInfo) {
+ onClickAppShortcut(v, (ShortcutInfo) tag, launcher);
+ } else if (tag instanceof FolderInfo) {
+ if (v instanceof FolderIcon) {
+ onClickFolderIcon(v);
+ }
+ } else if (tag instanceof AppInfo) {
+ startAppShortcutOrInfoActivity(v, (AppInfo) tag, launcher);
+ } else if (tag instanceof LauncherAppWidgetInfo) {
+ if (v instanceof PendingAppWidgetHostView) {
+ onClickPendingWidget((PendingAppWidgetHostView) v, launcher);
+ }
+ }
+ }
+
+ /**
+ * Event handler for a folder icon click.
+ *
+ * @param v The view that was clicked. Must be an instance of {@link FolderIcon}.
+ */
+ private static void onClickFolderIcon(View v) {
+ Folder folder = ((FolderIcon) v).getFolder();
+ if (!folder.isOpen() && !folder.isDestroyed()) {
+ // Open the requested folder
+ folder.animateOpen();
+ }
+ }
+
+ /**
+ * Event handler for the app widget view which has not fully restored.
+ */
+ private static void onClickPendingWidget(PendingAppWidgetHostView v, Launcher launcher) {
+ if (launcher.getPackageManager().isSafeMode()) {
+ Toast.makeText(launcher, R.string.safemode_widget_error, Toast.LENGTH_SHORT).show();
+ return;
+ }
+
+ final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) v.getTag();
+ if (v.isReadyForClickSetup()) {
+ LauncherAppWidgetProviderInfo appWidgetInfo = AppWidgetManagerCompat
+ .getInstance(launcher).findProvider(info.providerName, info.user);
+ if (appWidgetInfo == null) {
+ return;
+ }
+ WidgetAddFlowHandler addFlowHandler = new WidgetAddFlowHandler(appWidgetInfo);
+
+ if (info.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) {
+ if (!info.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_ALLOCATED)) {
+ // This should not happen, as we make sure that an Id is allocated during bind.
+ return;
+ }
+ addFlowHandler.startBindFlow(launcher, info.appWidgetId, info,
+ REQUEST_BIND_PENDING_APPWIDGET);
+ } else {
+ addFlowHandler.startConfigActivity(launcher, info, REQUEST_RECONFIGURE_APPWIDGET);
+ }
+ } else {
+ final String packageName = info.providerName.getPackageName();
+ onClickPendingAppItem(v, launcher, packageName, info.installProgress >= 0);
+ }
+ }
+
+ private static void onClickPendingAppItem(View v, Launcher launcher, String packageName,
+ boolean downloadStarted) {
+ if (downloadStarted) {
+ // If the download has started, simply direct to the market app.
+ startMarketIntentForPackage(v, launcher, packageName);
+ return;
+ }
+ new AlertDialog.Builder(launcher)
+ .setTitle(R.string.abandoned_promises_title)
+ .setMessage(R.string.abandoned_promise_explanation)
+ .setPositiveButton(R.string.abandoned_search,
+ (d, i) -> startMarketIntentForPackage(v, launcher, packageName))
+ .setNeutralButton(R.string.abandoned_clean_this,
+ (d, i) -> launcher.getWorkspace()
+ .removeAbandonedPromise(packageName, Process.myUserHandle()))
+ .create().show();
+ }
+
+ private static void startMarketIntentForPackage(View v, Launcher launcher, String packageName) {
+ ItemInfo item = (ItemInfo) v.getTag();
+ Intent intent = new PackageManagerHelper(launcher).getMarketIntent(packageName);
+ launcher.startActivitySafely(v, intent, item);
+ }
+
+ /**
+ * Event handler for an app shortcut click.
+ *
+ * @param v The view that was clicked. Must be a tagged with a {@link ShortcutInfo}.
+ */
+ private static void onClickAppShortcut(View v, ShortcutInfo shortcut, Launcher launcher) {
+ if (shortcut.isDisabled()) {
+ final int disabledFlags = shortcut.runtimeStatusFlags & ShortcutInfo.FLAG_DISABLED_MASK;
+ if ((disabledFlags &
+ ~FLAG_DISABLED_SUSPENDED &
+ ~FLAG_DISABLED_QUIET_USER) == 0) {
+ // If the app is only disabled because of the above flags, launch activity anyway.
+ // Framework will tell the user why the app is suspended.
+ } else {
+ if (!TextUtils.isEmpty(shortcut.disabledMessage)) {
+ // Use a message specific to this shortcut, if it has one.
+ Toast.makeText(launcher, shortcut.disabledMessage, Toast.LENGTH_SHORT).show();
+ return;
+ }
+ // Otherwise just use a generic error message.
+ int error = R.string.activity_not_available;
+ if ((shortcut.runtimeStatusFlags & FLAG_DISABLED_SAFEMODE) != 0) {
+ error = R.string.safemode_shortcut_error;
+ } else if ((shortcut.runtimeStatusFlags & FLAG_DISABLED_BY_PUBLISHER) != 0 ||
+ (shortcut.runtimeStatusFlags & FLAG_DISABLED_LOCKED_USER) != 0) {
+ error = R.string.shortcut_not_available;
+ }
+ Toast.makeText(launcher, error, Toast.LENGTH_SHORT).show();
+ return;
+ }
+ }
+
+ // Check for abandoned promise
+ if ((v instanceof BubbleTextView) && shortcut.hasPromiseIconUi()) {
+ String packageName = shortcut.intent.getComponent() != null ?
+ shortcut.intent.getComponent().getPackageName() : shortcut.intent.getPackage();
+ if (!TextUtils.isEmpty(packageName)) {
+ onClickPendingAppItem(v, launcher, packageName,
+ shortcut.hasStatusFlag(ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE));
+ return;
+ }
+ }
+
+ // Start activities
+ startAppShortcutOrInfoActivity(v, shortcut, launcher);
+ }
+
+ private static void startAppShortcutOrInfoActivity(View v, ItemInfo item, Launcher launcher) {
+ Intent intent;
+ if (item instanceof PromiseAppInfo) {
+ PromiseAppInfo promiseAppInfo = (PromiseAppInfo) item;
+ intent = promiseAppInfo.getMarketIntent(launcher);
+ } else {
+ intent = item.getIntent();
+ }
+ if (intent == null) {
+ throw new IllegalArgumentException("Input must have a valid intent");
+ }
+ if (item instanceof ShortcutInfo) {
+ ShortcutInfo si = (ShortcutInfo) item;
+ if (si.hasStatusFlag(ShortcutInfo.FLAG_SUPPORTS_WEB_UI)
+ && intent.getAction() == Intent.ACTION_VIEW) {
+ // make a copy of the intent that has the package set to null
+ // we do this because the platform sometimes disables instant
+ // apps temporarily (triggered by the user) and fallbacks to the
+ // web ui. This only works though if the package isn't set
+ intent = new Intent(intent);
+ intent.setPackage(null);
+ }
+ }
+ launcher.startActivitySafely(v, intent, item);
+ }
+}
diff --git a/src/com/android/launcher3/touch/ItemLongClickListener.java b/src/com/android/launcher3/touch/ItemLongClickListener.java
new file mode 100644
index 0000000..f10a695
--- /dev/null
+++ b/src/com/android/launcher3/touch/ItemLongClickListener.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2018 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.touch;
+
+import static android.view.View.INVISIBLE;
+import static android.view.View.VISIBLE;
+
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.LauncherState.OVERVIEW;
+
+import android.view.View;
+import android.view.View.OnLongClickListener;
+
+import com.android.launcher3.CellLayout;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.DropTarget;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.dragndrop.DragController;
+import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.folder.Folder;
+
+/**
+ * Class to handle long-clicks on workspace items and start drag as a result.
+ */
+public class ItemLongClickListener {
+
+ public static OnLongClickListener INSTANCE_WORKSPACE =
+ ItemLongClickListener::onWorkspaceItemLongClick;
+
+ public static OnLongClickListener INSTANCE_ALL_APPS =
+ ItemLongClickListener::onAllAppsItemLongClick;
+
+ private static boolean onWorkspaceItemLongClick(View v) {
+ Launcher launcher = Launcher.getLauncher(v.getContext());
+ if (!canStartDrag(launcher)) return false;
+ if (!launcher.isInState(NORMAL) && !launcher.isInState(OVERVIEW)) return false;
+ if (!(v.getTag() instanceof ItemInfo)) return false;
+
+ launcher.setWaitingForResult(null);
+ beginDrag(v, launcher, (ItemInfo) v.getTag(), new DragOptions());
+ return true;
+ }
+
+ public static void beginDrag(View v, Launcher launcher, ItemInfo info,
+ DragOptions dragOptions) {
+ if (info.container >= 0) {
+ Folder folder = Folder.getOpen(launcher);
+ if (folder != null) {
+ if (!folder.getItemsInReadingOrder().contains(v)) {
+ folder.close(true);
+ } else {
+ folder.startDrag(v, dragOptions);
+ return;
+ }
+ }
+ }
+
+ CellLayout.CellInfo longClickCellInfo = new CellLayout.CellInfo(v, info);
+ launcher.getWorkspace().startDrag(longClickCellInfo, dragOptions);
+ }
+
+ private static boolean onAllAppsItemLongClick(View v) {
+ Launcher launcher = Launcher.getLauncher(v.getContext());
+ if (!canStartDrag(launcher)) return false;
+ // When we have exited all apps or are in transition, disregard long clicks
+ if (!launcher.isInState(LauncherState.ALL_APPS) ||
+ launcher.getWorkspace().isSwitchingState()) return false;
+
+ // Start the drag
+ final DragController dragController = launcher.getDragController();
+ dragController.addDragListener(new DragController.DragListener() {
+ @Override
+ public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
+ v.setVisibility(INVISIBLE);
+ }
+
+ @Override
+ public void onDragEnd() {
+ v.setVisibility(VISIBLE);
+ dragController.removeDragListener(this);
+ }
+ });
+
+ DeviceProfile grid = launcher.getDeviceProfile();
+ DragOptions options = new DragOptions();
+ options.intrinsicIconScaleFactor = (float) grid.allAppsIconSizePx / grid.iconSizePx;
+ launcher.getWorkspace().beginDragShared(v, launcher.getAppsView(), options);
+ return false;
+ }
+
+ public static boolean canStartDrag(Launcher launcher) {
+ if (launcher == null) {
+ return false;
+ }
+ // We prevent dragging when we are loading the workspace as it is possible to pick up a view
+ // that is subsequently removed from the workspace in startBinding().
+ if (launcher.isWorkspaceLocked()) return false;
+ // Return early if an item is already being dragged (e.g. when long-pressing two shortcuts)
+ if (launcher.getDragController().isDragging()) return false;
+
+ return true;
+ }
+}
diff --git a/src/com/android/launcher3/util/FocusLogic.java b/src/com/android/launcher3/util/FocusLogic.java
index b80e94d..b793f54 100644
--- a/src/com/android/launcher3/util/FocusLogic.java
+++ b/src/com/android/launcher3/util/FocusLogic.java
@@ -177,7 +177,10 @@
}
int cx = ((CellLayout.LayoutParams) cell.getLayoutParams()).cellX;
int cy = ((CellLayout.LayoutParams) cell.getLayoutParams()).cellY;
- matrix[invert ? (m - cx - 1) : cx][cy] = i;
+ int x = invert ? (m - cx - 1) : cx;
+ if (x < m && cy < n) { // check if view fits into matrix, else skip
+ matrix[x][cy] = i;
+ }
}
if (DEBUG) {
printMatrix(matrix);
diff --git a/src/com/android/launcher3/util/VerticalSwipeController.java b/src/com/android/launcher3/util/VerticalSwipeController.java
index a647378..ae5bfd5 100644
--- a/src/com/android/launcher3/util/VerticalSwipeController.java
+++ b/src/com/android/launcher3/util/VerticalSwipeController.java
@@ -18,12 +18,10 @@
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
-import static com.android.launcher3.anim.SpringAnimationHandler.Y_DIRECTION;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
-import android.support.animation.SpringAnimation;
import android.util.Log;
import android.view.MotionEvent;
@@ -31,13 +29,10 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.Utilities;
-import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.launcher3.anim.SpringAnimationHandler;
import com.android.launcher3.touch.SwipeDetector;
import com.android.launcher3.touch.SwipeDetector.Direction;
-import java.util.ArrayList;
/**
* Handles vertical touch gesture on the DragLayer allowing transitioning from
@@ -68,8 +63,6 @@
// Ratio of transition process [0, 1] to drag displacement (px)
private float mProgressMultiplier;
- protected SpringAnimationHandler[] mSpringHandlers;
-
public VerticalSwipeController(Launcher l, LauncherState baseState) {
this(l, baseState, ALL_APPS, SwipeDetector.VERTICAL);
}
@@ -104,29 +97,6 @@
}
}
- protected void initSprings() {
- AllAppsContainerView appsView = mLauncher.getAppsView();
-
- SpringAnimationHandler handler = appsView.getSpringAnimationHandler();
- if (handler == null) {
- mSpringHandlers = new SpringAnimationHandler[0];
- return;
- }
-
- ArrayList<SpringAnimationHandler> handlers = new ArrayList<>();
- handlers.add(handler);
-
- SpringAnimation searchSpring = appsView.getSearchUiManager().getSpringForFling();
- if (searchSpring != null) {
- SpringAnimationHandler searchHandler =
- new SpringAnimationHandler(Y_DIRECTION, handler.getFactory());
- searchHandler.add(searchSpring, true /* setDefaultValues */);
- handlers.add(searchHandler);
- }
-
- mSpringHandlers = handlers.toArray(new SpringAnimationHandler[handlers.size()]);
- }
-
@Override
public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
@@ -155,10 +125,6 @@
mDetector.setDetectableScrollConditions(
directionsToDetectScroll, ignoreSlopWhenSettling);
-
- if (mSpringHandlers == null) {
- initSprings();
- }
}
if (mNoIntercept) {
@@ -173,9 +139,6 @@
@Override
public boolean onControllerTouchEvent(MotionEvent ev) {
- for (SpringAnimationHandler h : mSpringHandlers) {
- h.addMovement(ev);
- }
return mDetector.onTouchEvent(ev);
}
@@ -198,10 +161,6 @@
mCurrentAnimation.pause();
mStartProgress = mCurrentAnimation.getProgressFraction();
}
-
- for (SpringAnimationHandler h : mSpringHandlers) {
- h.skipToEnd();
- }
}
protected boolean isTransitionFlipped() {
@@ -244,13 +203,6 @@
}
}
- if (fling && targetState == mTargetState) {
- for (SpringAnimationHandler h : mSpringHandlers) {
- // The icons are moving upwards, so we go to 0 from 1. (y-axis 1 is below 0.)
- h.animateToFinalPosition(0 /* pos */, 1 /* startValue */);
- }
- }
-
mCurrentAnimation.setEndAction(() -> {
mLauncher.getStateManager().goToState(targetState, false);
onTransitionComplete(fling, targetState == mToState);
diff --git a/src/com/android/launcher3/views/RecyclerViewFastScroller.java b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
index 58c9148..1cd6699 100644
--- a/src/com/android/launcher3/views/RecyclerViewFastScroller.java
+++ b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
@@ -22,6 +22,8 @@
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
+import android.graphics.Point;
+import android.graphics.Rect;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.util.Property;
@@ -43,6 +45,7 @@
public class RecyclerViewFastScroller extends View {
private static final int SCROLL_DELTA_THRESHOLD_DP = 4;
+ private static final Rect sTempRect = new Rect();
private static final Property<RecyclerViewFastScroller, Integer> TRACK_WIDTH =
new Property<RecyclerViewFastScroller, Integer>(Integer.class, "width") {
@@ -204,9 +207,9 @@
* Handles the touch event and determines whether to show the fast scroller (or updates it if
* it is already showing).
*/
- public boolean handleTouchEvent(MotionEvent ev) {
- int x = (int) ev.getX();
- int y = (int) ev.getY();
+ public boolean handleTouchEvent(MotionEvent ev, Point offset) {
+ int x = (int) ev.getX() - offset.x;
+ int y = (int) ev.getY() - offset.y;
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
// Keep track of the down positions
@@ -260,7 +263,6 @@
}
private void calcTouchOffsetAndPrepToFastScroll(int downY, int lastY) {
- mRv.getParent().requestDisallowInterceptTouchEvent(true);
mIsDragging = true;
if (mCanThumbDetach) {
mIsThumbDetached = true;
@@ -358,4 +360,16 @@
mMaxWidth, mRv.getScrollbarTrackHeight() - mMaxWidth - height);
mPopupView.setTranslationY(top);
}
+
+ public boolean isHitInParent(float x, float y, Point outOffset) {
+ if (mThumbOffsetY < 0) {
+ return false;
+ }
+ getHitRect(sTempRect);
+ sTempRect.top += mRv.getScrollBarTop();
+ if (outOffset != null) {
+ outOffset.set(sTempRect.left, sTempRect.top);
+ }
+ return sTempRect.contains((int) x, (int) y);
+ }
}
diff --git a/src/com/android/launcher3/views/SpringRelativeLayout.java b/src/com/android/launcher3/views/SpringRelativeLayout.java
new file mode 100644
index 0000000..090b3e6
--- /dev/null
+++ b/src/com/android/launcher3/views/SpringRelativeLayout.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2018 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.graphics.Canvas;
+import android.support.animation.FloatPropertyCompat;
+import android.support.animation.SpringAnimation;
+import android.support.animation.SpringForce;
+import android.support.annotation.NonNull;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.RecyclerView.EdgeEffectFactory;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.SparseBooleanArray;
+import android.view.View;
+import android.widget.EdgeEffect;
+import android.widget.RelativeLayout;
+
+import static android.support.animation.SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY;
+import static android.support.animation.SpringForce.STIFFNESS_LOW;
+import static android.support.animation.SpringForce.STIFFNESS_MEDIUM;
+
+public class SpringRelativeLayout extends RelativeLayout {
+
+ private static final float STIFFNESS = (STIFFNESS_MEDIUM + STIFFNESS_LOW) / 2;
+ private static final float DAMPING_RATIO = DAMPING_RATIO_MEDIUM_BOUNCY;
+ private static final float VELOCITY_MULTIPLIER = 0.3f;
+
+ private static final FloatPropertyCompat<SpringRelativeLayout> DAMPED_SCROLL =
+ new FloatPropertyCompat<SpringRelativeLayout>("value") {
+
+ @Override
+ public float getValue(SpringRelativeLayout object) {
+ return object.mDampedScrollShift;
+ }
+
+ @Override
+ public void setValue(SpringRelativeLayout object, float value) {
+ object.setDampedScrollShift(value);
+ }
+ };
+
+ private final SparseBooleanArray mSpringViews = new SparseBooleanArray();
+ private final SpringAnimation mSpring;
+
+ private float mDampedScrollShift = 0;
+
+ public SpringRelativeLayout(Context context) {
+ this(context, null);
+ }
+
+ public SpringRelativeLayout(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public SpringRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ mSpring = new SpringAnimation(this, DAMPED_SCROLL, 0);
+ mSpring.setSpring(new SpringForce(0)
+ .setStiffness(STIFFNESS)
+ .setDampingRatio(DAMPING_RATIO));
+ }
+
+ public void addSpringView(int id) {
+ mSpringViews.put(id, true);
+ }
+
+ @Override
+ protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
+ if (mDampedScrollShift != 0 && mSpringViews.get(child.getId())) {
+ canvas.translate(0, mDampedScrollShift);
+ boolean result = super.drawChild(canvas, child, drawingTime);
+ canvas.translate(0, -mDampedScrollShift);
+ return result;
+ }
+ return super.drawChild(canvas, child, drawingTime);
+ }
+
+ private void setDampedScrollShift(float shift) {
+ if (shift != mDampedScrollShift) {
+ mDampedScrollShift = shift;
+ invalidate();
+ }
+ }
+
+ private void finishScrollWithVelocity(float velocity) {
+ mSpring.setStartVelocity(velocity);
+ mSpring.setStartValue(mDampedScrollShift);
+ mSpring.start();
+ }
+
+ public EdgeEffectFactory createEdgeEffectFactory() {
+ return new SpringEdgeEffectFactory();
+ }
+
+ private class SpringEdgeEffectFactory extends EdgeEffectFactory {
+
+ @NonNull @Override
+ protected EdgeEffect createEdgeEffect(RecyclerView view, int direction) {
+ switch (direction) {
+ case DIRECTION_TOP:
+ return new SpringEdgeEffect(getContext(), +VELOCITY_MULTIPLIER);
+ case DIRECTION_BOTTOM:
+ return new SpringEdgeEffect(getContext(), -VELOCITY_MULTIPLIER);
+ }
+ return super.createEdgeEffect(view, direction);
+ }
+ }
+
+ private class SpringEdgeEffect extends EdgeEffect {
+
+ private final float mVelocityMultiplier;
+
+ private float mDistance;
+
+ public SpringEdgeEffect(Context context, float velocityMultiplier) {
+ super(context);
+ mVelocityMultiplier = velocityMultiplier;
+ }
+
+ @Override
+ public boolean draw(Canvas canvas) {
+ return false;
+ }
+
+ @Override
+ public void onAbsorb(int velocity) {
+ finishScrollWithVelocity(velocity * mVelocityMultiplier);
+ }
+
+ @Override
+ public void onPull(float deltaDistance, float displacement) {
+ mDistance += deltaDistance * (mVelocityMultiplier / 3f);
+ setDampedScrollShift(mDistance * getHeight());
+ }
+
+ @Override
+ public void onRelease() {
+ mDistance = 0;
+ finishScrollWithVelocity(0);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/launcher3/views/TopRoundedCornerView.java b/src/com/android/launcher3/views/TopRoundedCornerView.java
index ba223c4..3ba8ca3 100644
--- a/src/com/android/launcher3/views/TopRoundedCornerView.java
+++ b/src/com/android/launcher3/views/TopRoundedCornerView.java
@@ -17,12 +17,14 @@
import android.content.Context;
import android.graphics.Canvas;
+import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.widget.FrameLayout;
import com.android.launcher3.R;
+import com.android.launcher3.util.Themes;
/**
* View with top rounded corners.
@@ -33,23 +35,41 @@
private final Path mClipPath = new Path();
private float[] mRadii;
+ private final Paint mNavBarScrimPaint;
+ private int mNavBarScrimHeight = 0;
+
public TopRoundedCornerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
int radius = getResources().getDimensionPixelSize(R.dimen.bg_round_rect_radius);
mRadii = new float[] {radius, radius, radius, radius, 0, 0, 0, 0};
+
+ mNavBarScrimPaint = new Paint();
+ mNavBarScrimPaint.setColor(Themes.getAttrColor(context, R.attr.allAppsNavBarScrimColor));
}
public TopRoundedCornerView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
+ public void setNavBarScrimHeight(int height) {
+ if (mNavBarScrimHeight != height) {
+ mNavBarScrimHeight = height;
+ invalidate();
+ }
+ }
+
@Override
public void draw(Canvas canvas) {
canvas.save();
canvas.clipPath(mClipPath);
super.draw(canvas);
canvas.restore();
+
+ if (mNavBarScrimHeight > 0) {
+ canvas.drawRect(0, getHeight() - mNavBarScrimHeight, getWidth(), getHeight(),
+ mNavBarScrimPaint);
+ }
}
@Override
diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java
index c51842d..10708d6 100644
--- a/src/com/android/launcher3/widget/BaseWidgetSheet.java
+++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java
@@ -32,6 +32,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.graphics.ColorScrim;
+import com.android.launcher3.touch.ItemLongClickListener;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import com.android.launcher3.util.SystemUiController;
@@ -71,7 +72,7 @@
@Override
public final boolean onLongClick(View v) {
- if (!mLauncher.isDraggingEnabled()) return false;
+ if (!ItemLongClickListener.canStartDrag(mLauncher)) return false;
if (v instanceof WidgetCell) {
return beginDraggingWidget((WidgetCell) v);
diff --git a/src/com/android/launcher3/widget/PendingAppWidgetHostView.java b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
index 6970833..961799d 100644
--- a/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
@@ -43,6 +43,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.graphics.DrawableFactory;
import com.android.launcher3.model.PackageItemInfo;
+import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.util.Themes;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
@@ -83,7 +84,7 @@
setElevation(getResources().getDimension(R.dimen.pending_widget_elevation));
updateAppWidget(null);
- setOnClickListener(mLauncher);
+ setOnClickListener(ItemClickHandler.INSTANCE);
if (info.pendingItemInfo == null) {
info.pendingItemInfo = new PackageItemInfo(info.providerName.getPackageName());
diff --git a/src/com/android/launcher3/widget/WidgetsFullSheet.java b/src/com/android/launcher3/widget/WidgetsFullSheet.java
index 48f8afe..b31feed 100644
--- a/src/com/android/launcher3/widget/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsFullSheet.java
@@ -23,7 +23,6 @@
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
-import android.view.View;
import android.view.animation.AnimationUtils;
import com.android.launcher3.Insettable;
@@ -31,6 +30,8 @@
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetHost.ProviderChangedListener;
import com.android.launcher3.R;
+import com.android.launcher3.views.RecyclerViewFastScroller;
+import com.android.launcher3.views.TopRoundedCornerView;
/**
* Popup for showing the full list of available widgets
@@ -46,7 +47,6 @@
private final WidgetsListAdapter mAdapter;
- private View mNavBarScrim;
private WidgetsRecyclerView mRecyclerView;
public WidgetsFullSheet(Context context, AttributeSet attrs, int defStyleAttr) {
@@ -65,7 +65,6 @@
protected void onFinishInflate() {
super.onFinishInflate();
mContent = findViewById(R.id.container);
- mNavBarScrim = findViewById(R.id.nav_bar_bg);
mRecyclerView = findViewById(R.id.widgets_list_view);
mRecyclerView.setAdapter(mAdapter);
@@ -91,7 +90,6 @@
public void setInsets(Rect insets) {
mInsets.set(insets);
- mNavBarScrim.getLayoutParams().height = insets.bottom;
mRecyclerView.setPadding(
mRecyclerView.getPaddingLeft(), mRecyclerView.getPaddingTop(),
mRecyclerView.getPaddingRight(), insets.bottom);
@@ -100,6 +98,8 @@
} else {
clearNavBarColor();
}
+
+ ((TopRoundedCornerView) mContent).setNavBarScrimHeight(mInsets.bottom);
requestLayout();
}
@@ -195,7 +195,11 @@
// Disable swipe down when recycler view is scrolling
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
mNoIntercept = false;
- if (mLauncher.getDragLayer().isEventOverView(mContent, ev)) {
+ RecyclerViewFastScroller scroller = mRecyclerView.getScrollbar();
+ if (scroller.getThumbOffsetY() >= 0 &&
+ mLauncher.getDragLayer().isEventOverView(scroller, ev)) {
+ mNoIntercept = true;
+ } else if (mLauncher.getDragLayer().isEventOverView(mContent, ev)) {
mNoIntercept = !mRecyclerView.shouldContainerScroll(ev, mLauncher.getDragLayer());
}
}
diff --git a/src/com/android/launcher3/widget/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/WidgetsRecyclerView.java
index 89c88a4..124058e 100644
--- a/src/com/android/launcher3/widget/WidgetsRecyclerView.java
+++ b/src/com/android/launcher3/widget/WidgetsRecyclerView.java
@@ -17,8 +17,12 @@
package com.android.launcher3.widget;
import android.content.Context;
+import android.graphics.Point;
import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.RecyclerView.OnItemTouchListener;
import android.util.AttributeSet;
+import android.view.MotionEvent;
import android.view.View;
import com.android.launcher3.BaseRecyclerView;
@@ -27,13 +31,15 @@
/**
* The widgets recycler view.
*/
-public class WidgetsRecyclerView extends BaseRecyclerView {
+public class WidgetsRecyclerView extends BaseRecyclerView implements OnItemTouchListener {
- private static final String TAG = "WidgetsRecyclerView";
private WidgetsListAdapter mAdapter;
private final int mScrollbarTop;
+ private final Point mFastScrollerOffset = new Point();
+ private boolean mTouchDownOnScroller;
+
public WidgetsRecyclerView(Context context) {
this(context, null);
}
@@ -46,6 +52,7 @@
// API 21 and below only support 3 parameter ctor.
super(context, attrs, defStyleAttr);
mScrollbarTop = getResources().getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin);
+ addOnItemTouchListener(this);
}
public WidgetsRecyclerView(Context context, AttributeSet attrs, int defStyleAttr,
@@ -56,7 +63,6 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- addOnItemTouchListener(this);
// create a layout manager with Launcher's context so that scroll position
// can be preserved during screen rotation.
setLayoutManager(new LinearLayoutManager(getContext()));
@@ -145,4 +151,26 @@
public int getScrollBarTop() {
return mScrollbarTop;
}
+
+ @Override
+ public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
+ if (e.getAction() == MotionEvent.ACTION_DOWN) {
+ mTouchDownOnScroller =
+ mScrollbar.isHitInParent(e.getX(), e.getY(), mFastScrollerOffset);
+ }
+ if (mTouchDownOnScroller) {
+ return mScrollbar.handleTouchEvent(e, mFastScrollerOffset);
+ }
+ return false;
+ }
+
+ @Override
+ public void onTouchEvent(RecyclerView rv, MotionEvent e) {
+ if (mTouchDownOnScroller) {
+ mScrollbar.handleTouchEvent(e, mFastScrollerOffset);
+ }
+ }
+
+ @Override
+ public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { }
}
\ No newline at end of file
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/OverviewPanel.java b/src_ui_overrides/com/android/launcher3/uioverrides/OverviewPanel.java
index 4168e11..616e25c 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/OverviewPanel.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/OverviewPanel.java
@@ -15,6 +15,7 @@
*/
package com.android.launcher3.uioverrides;
+import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.WorkspaceStateTransitionAnimation.NO_ANIM_PROPERTY_SETTER;
import android.content.Context;
@@ -174,7 +175,8 @@
}
private void setState(LauncherState state, PropertySetter setter) {
- setter.setViewAlpha(this, 1f - state.getHoseatAlpha(mLauncher), Interpolators.ACCEL);
+ float myAlpha = state == OVERVIEW ? 1 : 0;
+ setter.setViewAlpha(this, myAlpha, Interpolators.ACCEL);
}
public static int getButtonBarHeight(Launcher launcher) {
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
index a16ae48..de75ac9 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
@@ -16,25 +16,17 @@
package com.android.launcher3.uioverrides;
-import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
import android.graphics.PointF;
-import android.os.Bundle;
-import android.view.View;
import android.view.View.AccessibilityDelegate;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherStateManager.StateHandler;
-import com.android.launcher3.graphics.BitmapRenderer;
import com.android.launcher3.util.TouchController;
public class UiFactory {
- public static final boolean USE_HARDWARE_BITMAP = false;
-
public static TouchController[] createTouchControllers(Launcher launcher) {
return new TouchController[] {
new AllAppsSwipeController(launcher), new PinchToOverviewListener(launcher)};
@@ -54,13 +46,6 @@
launcher.getStateManager().goToState(OVERVIEW);
}
- public static Bitmap createFromRenderer(int width, int height, boolean forceSoftwareRenderer,
- BitmapRenderer renderer) {
- Bitmap result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- renderer.render(new Canvas(result));
- return result;
- }
-
public static void resetOverview(Launcher launcher) { }
public static void onLauncherStateOrFocusChanged(Launcher launcher) { }
diff --git a/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java b/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
index cf90afd..b217847 100644
--- a/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
+++ b/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
@@ -1,5 +1,11 @@
package com.android.launcher3.model;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
@@ -24,8 +30,8 @@
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel;
-import com.android.launcher3.LauncherModel.ModelUpdateTask;
import com.android.launcher3.LauncherModel.Callbacks;
+import com.android.launcher3.LauncherModel.ModelUpdateTask;
import com.android.launcher3.LauncherProvider;
import com.android.launcher3.graphics.BitmapInfo;
import com.android.launcher3.util.ComponentKey;
@@ -43,12 +49,6 @@
import java.util.List;
import java.util.concurrent.Executor;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
/**
* Base class for writing tests for Model update tasks.
*/
@@ -82,7 +82,7 @@
modelWriter = mock(ModelWriter.class);
when(appState.getModel()).thenReturn(model);
- when(model.getWriter(anyBoolean())).thenReturn(modelWriter);
+ when(model.getWriter(anyBoolean(), anyBoolean())).thenReturn(modelWriter);
when(model.getCallback()).thenReturn(callbacks);
myUser = Process.myUserHandle();
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 9e55127..011aa22 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -15,9 +15,6 @@
*/
package com.android.launcher3.ui;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-
import android.app.Instrumentation;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -37,22 +34,27 @@
import android.support.test.uiautomator.UiObject2;
import android.support.test.uiautomator.Until;
import android.view.MotionEvent;
+
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.MainThreadExecutor;
import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.testcomponent.AppWidgetNoConfig;
import com.android.launcher3.testcomponent.AppWidgetWithConfig;
+
+import org.junit.Before;
+
import java.util.Locale;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
-import org.junit.Before;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
/**
* Base class for all instrumentation tests providing various utility methods.
@@ -63,6 +65,7 @@
public static final long DEFAULT_BROADCAST_TIMEOUT_SECS = 5;
public static final long DEFAULT_UI_TIMEOUT = 3000;
+ public static final long LARGE_UI_TIMEOUT = 10000;
public static final long DEFAULT_WORKER_TIMEOUT_SECS = 5;
protected MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor();
@@ -78,11 +81,6 @@
}
protected void lockRotation(boolean naturalOrientation) throws RemoteException {
- Utilities.getPrefs(mTargetContext)
- .edit()
- .putBoolean(Utilities.ALLOW_ROTATION_PREFERENCE_KEY, !naturalOrientation)
- .commit();
-
if (naturalOrientation) {
mDevice.setOrientationNatural();
} else {
@@ -100,8 +98,13 @@
protected UiObject2 openAllApps() {
mDevice.waitForIdle();
if (FeatureFlags.NO_ALL_APPS_ICON) {
- // clicking on the page indicator brings up all apps tray on non tablets.
- findViewById(R.id.page_indicator).click();
+ UiObject2 hotseat = mDevice.wait(
+ Until.findObject(getSelectorForId(R.id.hotseat)), 2500);
+ Point start = hotseat.getVisibleCenter();
+ int endY = (int) (mDevice.getDisplayHeight() * 0.1f);
+ // 100 px/step
+ mDevice.swipe(start.x, start.y, start.x, endY, (start.y - endY) / 100);
+
} else {
mDevice.wait(Until.findObject(
By.desc(mTargetContext.getString(R.string.all_apps_button_label))),
diff --git a/tests/src/com/android/launcher3/ui/WorkTabTest.java b/tests/src/com/android/launcher3/ui/WorkTabTest.java
new file mode 100644
index 0000000..ccee7da
--- /dev/null
+++ b/tests/src/com/android/launcher3/ui/WorkTabTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2018, 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.ui;
+
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.Until;
+
+import com.android.launcher3.R;
+import com.android.launcher3.util.Condition;
+import com.android.launcher3.util.Wait;
+import com.android.launcher3.util.rule.LauncherActivityRule;
+import com.android.launcher3.util.rule.ShellCommandRule;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.assertTrue;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class WorkTabTest extends AbstractLauncherUiTest {
+ @Rule
+ public LauncherActivityRule mActivityMonitor = new LauncherActivityRule();
+ @Rule
+ public ShellCommandRule mDefaultLauncherRule = ShellCommandRule.setDefaultLauncher();
+
+ private int mProfileUserId;
+
+ @Before
+ public void createWorkProfile() throws Exception {
+ String output =
+ mDevice.executeShellCommand(
+ "pm create-user --profileOf 0 --managed TestProfile");
+ assertTrue("Failed to create work profile", output.startsWith("Success"));
+
+ String[] tokens = output.split("\\s+");
+ mProfileUserId = Integer.parseInt(tokens[tokens.length - 1]);
+
+ mDevice.executeShellCommand("am start-user " + mProfileUserId);
+ }
+
+ @After
+ public void removeWorkProfile() throws Exception {
+ mDevice.executeShellCommand("pm remove-user " + mProfileUserId);
+ }
+
+ @Test
+ public void workTabExists() {
+ mActivityMonitor.startLauncher();
+
+ // Open all apps and wait for load complete
+ final UiObject2 appsContainer = openAllApps();
+ assertTrue(Wait.atMost(Condition.minChildCount(appsContainer, 2), DEFAULT_UI_TIMEOUT));
+
+ assertTrue("Personal tab is missing",
+ mDevice.wait(Until.hasObject(getSelectorForId(R.id.tab_personal)),
+ LARGE_UI_TIMEOUT));
+ assertTrue("Work tab is missing",
+ mDevice.wait(Until.hasObject(getSelectorForId(R.id.tab_work)), LARGE_UI_TIMEOUT));
+ }
+}
\ No newline at end of file