diff options
Diffstat (limited to 'src/com/android/launcher3')
36 files changed, 975 insertions, 1545 deletions
diff --git a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java index 6e33d2a55..b249c9530 100644 --- a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java +++ b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java @@ -13,6 +13,7 @@ import android.support.annotation.WorkerThread; import android.util.Log; import com.android.launcher3.LauncherSettings.Favorites; +import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.model.LoaderTask; import com.android.launcher3.provider.RestoreDbTask; import com.android.launcher3.util.ContentWriter; @@ -26,7 +27,7 @@ public class AppWidgetsRestoredReceiver extends BroadcastReceiver { if (AppWidgetManager.ACTION_APPWIDGET_HOST_RESTORED.equals(intent.getAction())) { int hostId = intent.getIntExtra(AppWidgetManager.EXTRA_HOST_ID, 0); Log.d(TAG, "Widget ID map received for host:" + hostId); - if (hostId != Launcher.APPWIDGET_HOST_ID) { + if (hostId != LauncherAppWidgetHost.APPWIDGET_HOST_ID) { return; } @@ -38,7 +39,8 @@ public class AppWidgetsRestoredReceiver extends BroadcastReceiver { .postAtFrontOfQueue(new Runnable() { @Override public void run() { - restoreAppWidgetIds(context, asyncResult, oldIds, newIds); + restoreAppWidgetIds(context, oldIds, newIds); + asyncResult.finish(); } }); } else { @@ -51,9 +53,13 @@ public class AppWidgetsRestoredReceiver extends BroadcastReceiver { * Updates the app widgets whose id has changed during the restore process. */ @WorkerThread - static void restoreAppWidgetIds(Context context, PendingResult asyncResult, - int[] oldWidgetIds, int[] newWidgetIds) { - AppWidgetHost appWidgetHost = new AppWidgetHost(context, Launcher.APPWIDGET_HOST_ID); + static void restoreAppWidgetIds(Context context, int[] oldWidgetIds, int[] newWidgetIds) { + AppWidgetHost appWidgetHost = new LauncherAppWidgetHost(context); + if (FeatureFlags.GO_DISABLE_WIDGETS) { + Log.e(TAG, "Skipping widget ID remap as widgets not supported"); + appWidgetHost.deleteHost(); + return; + } if (!RestoreDbTask.isPending(context)) { // Someone has already gone through our DB once, probably LoaderTask. Skip any further // modifications of the DB. @@ -62,7 +68,6 @@ public class AppWidgetsRestoredReceiver extends BroadcastReceiver { Log.d(TAG, "Deleting widgetId: " + widgetId); appWidgetHost.deleteAppWidgetId(widgetId); } - asyncResult.finish(); return; } final ContentResolver cr = context.getContentResolver(); @@ -106,6 +111,5 @@ public class AppWidgetsRestoredReceiver extends BroadcastReceiver { if (app != null) { app.getModel().forceReload(); } - asyncResult.finish(); } } diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java index 2b59ede47..e49649502 100644 --- a/src/com/android/launcher3/BaseActivity.java +++ b/src/com/android/launcher3/BaseActivity.java @@ -19,6 +19,7 @@ package com.android.launcher3; import android.app.Activity; import android.content.Context; import android.content.ContextWrapper; +import android.content.Intent; import android.view.View.AccessibilityDelegate; import com.android.launcher3.logging.UserEventDispatcher; @@ -63,4 +64,9 @@ public abstract class BaseActivity extends Activity { } return mSystemUiController; } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + } } diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 3ae4316a9..7c258616f 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -209,10 +209,8 @@ public class Launcher extends BaseActivity private boolean mIsSafeModeEnabled; - public static final int APPWIDGET_HOST_ID = 1024; public static final int EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT = 500; private static final int ON_ACTIVITY_RESULT_ANIMATION_DELAY = 500; - private static final int ACTIVITY_START_DELAY = 1000; // How long to wait before the new-shortcut animation automatically pans the workspace private static final int NEW_APPS_PAGE_MOVE_DELAY = 500; @@ -397,7 +395,10 @@ public class Launcher extends BaseActivity mAppWidgetManager = AppWidgetManagerCompat.getInstance(this); - mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID); + mAppWidgetHost = new LauncherAppWidgetHost(this); + if (Utilities.ATLEAST_MARSHMALLOW) { + mAppWidgetHost.addProviderChangeListener(this); + } mAppWidgetHost.startListening(); // If we are getting an onCreate, we can actually preempt onResume and unset mPaused here, @@ -789,7 +790,7 @@ public class Launcher extends BaseActivity } @Override - protected void onActivityResult( + public void onActivityResult( final int requestCode, final int resultCode, final Intent data) { handleActivityResult(requestCode, resultCode, data); if (mLauncherCallbacks != null) { @@ -1468,23 +1469,25 @@ public class Launcher extends BaseActivity mWorkspace.addInScreen(view, info); } else { // Adding a shortcut to a Folder. - final long folderIconId = container; - FolderIcon folderIcon = (FolderIcon) mWorkspace.getFirstMatch(new ItemOperator() { - @Override - public boolean evaluate(ItemInfo info, View view) { - return info != null && info.id == folderIconId; - } - }); - + FolderIcon folderIcon = findFolderIcon(container); if (folderIcon != null) { FolderInfo folderInfo = (FolderInfo) folderIcon.getTag(); folderInfo.add(info, args.rank, false); } else { - Log.e(TAG, "Could not find folder with id " + folderIconId + " to add shortcut."); + Log.e(TAG, "Could not find folder with id " + container + " to add shortcut."); } } } + public FolderIcon findFolderIcon(final long folderIconId) { + return (FolderIcon) mWorkspace.getFirstMatch(new ItemOperator() { + @Override + public boolean evaluate(ItemInfo info, View view) { + return info != null && info.id == folderIconId; + } + }); + } + /** * Add a widget to the workspace. * diff --git a/src/com/android/launcher3/LauncherAppWidgetHost.java b/src/com/android/launcher3/LauncherAppWidgetHost.java index 6e8c59b66..5573c5c15 100644 --- a/src/com/android/launcher3/LauncherAppWidgetHost.java +++ b/src/com/android/launcher3/LauncherAppWidgetHost.java @@ -16,12 +16,20 @@ package com.android.launcher3; +import android.app.Activity; import android.appwidget.AppWidgetHost; import android.appwidget.AppWidgetHostView; +import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProviderInfo; +import android.content.ActivityNotFoundException; import android.content.Context; +import android.content.Intent; +import android.os.Handler; import android.util.SparseArray; import android.view.LayoutInflater; +import android.widget.Toast; + +import com.android.launcher3.config.FeatureFlags; import java.util.ArrayList; @@ -33,14 +41,16 @@ import java.util.ArrayList; */ public class LauncherAppWidgetHost extends AppWidgetHost { - private final ArrayList<Runnable> mProviderChangeListeners = new ArrayList<Runnable>(); + public static final int APPWIDGET_HOST_ID = 1024; + + private final ArrayList<ProviderChangedListener> mProviderChangeListeners = new ArrayList<>(); private final SparseArray<LauncherAppWidgetHostView> mViews = new SparseArray<>(); - private Launcher mLauncher; + private final Context mContext; - public LauncherAppWidgetHost(Launcher launcher, int hostId) { - super(launcher, hostId); - mLauncher = launcher; + public LauncherAppWidgetHost(Context context) { + super(context, APPWIDGET_HOST_ID); + mContext = context; } @Override @@ -53,6 +63,10 @@ public class LauncherAppWidgetHost extends AppWidgetHost { @Override public void startListening() { + if (FeatureFlags.GO_DISABLE_WIDGETS) { + return; + } + try { super.startListening(); } catch (Exception e) { @@ -66,24 +80,38 @@ public class LauncherAppWidgetHost extends AppWidgetHost { } } - public void addProviderChangeListener(Runnable callback) { + @Override + public void stopListening() { + if (FeatureFlags.GO_DISABLE_WIDGETS) { + return; + } + + super.stopListening(); + } + + @Override + public int allocateAppWidgetId() { + if (FeatureFlags.GO_DISABLE_WIDGETS) { + return AppWidgetManager.INVALID_APPWIDGET_ID; + } + + return super.allocateAppWidgetId(); + } + + public void addProviderChangeListener(ProviderChangedListener callback) { mProviderChangeListeners.add(callback); } - public void removeProviderChangeListener(Runnable callback) { + public void removeProviderChangeListener(ProviderChangedListener callback) { mProviderChangeListeners.remove(callback); } protected void onProvidersChanged() { if (!mProviderChangeListeners.isEmpty()) { - for (Runnable callback : new ArrayList<>(mProviderChangeListeners)) { - callback.run(); + for (ProviderChangedListener callback : new ArrayList<>(mProviderChangeListeners)) { + callback.notifyWidgetProvidersChanged(); } } - - if (Utilities.ATLEAST_MARSHMALLOW) { - mLauncher.notifyWidgetProvidersChanged(); - } } public AppWidgetHostView createView(Context context, int appWidgetId, @@ -109,7 +137,7 @@ public class LauncherAppWidgetHost extends AppWidgetHost { // will update. LauncherAppWidgetHostView view = mViews.get(appWidgetId); if (view == null) { - view = onCreateView(mLauncher, appWidgetId, appWidget); + view = onCreateView(mContext, appWidgetId, appWidget); } view.setAppWidget(appWidgetId, appWidget); view.switchToErrorView(); @@ -124,11 +152,11 @@ public class LauncherAppWidgetHost extends AppWidgetHost { @Override protected void onProviderChanged(int appWidgetId, AppWidgetProviderInfo appWidget) { LauncherAppWidgetProviderInfo info = LauncherAppWidgetProviderInfo.fromProviderInfo( - mLauncher, appWidget); + mContext, appWidget); super.onProviderChanged(appWidgetId, info); // The super method updates the dimensions of the providerInfo. Update the // launcher spans accordingly. - info.initSpans(mLauncher); + info.initSpans(mContext); } @Override @@ -142,4 +170,53 @@ public class LauncherAppWidgetHost extends AppWidgetHost { super.clearViews(); mViews.clear(); } + + public void startBindFlow(BaseActivity activity, + int appWidgetId, AppWidgetProviderInfo info, int requestCode) { + + if (FeatureFlags.GO_DISABLE_WIDGETS) { + sendActionCancelled(activity, requestCode); + return; + } + + Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND) + .putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId) + .putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER, info.provider) + .putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER_PROFILE, info.getProfile()); + // TODO: we need to make sure that this accounts for the options bundle. + // intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, options); + activity.startActivityForResult(intent, requestCode); + } + + + public void startConfigActivity(BaseActivity activity, int widgetId, int requestCode) { + if (FeatureFlags.GO_DISABLE_WIDGETS) { + sendActionCancelled(activity, requestCode); + return; + } + + try { + startAppWidgetConfigureActivityForResult(activity, widgetId, 0, requestCode, null); + } catch (ActivityNotFoundException | SecurityException e) { + Toast.makeText(activity, R.string.activity_not_found, Toast.LENGTH_SHORT).show(); + sendActionCancelled(activity, requestCode); + } + } + + private void sendActionCancelled(final BaseActivity activity, final int requestCode) { + new Handler().post(new Runnable() { + @Override + public void run() { + activity.onActivityResult(requestCode, Activity.RESULT_CANCELED, null); + } + }); + } + + /** + * Listener for getting notifications on provider changes. + */ + public interface ProviderChangedListener { + + void notifyWidgetProvidersChanged(); + } } diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 82bee0e4c..f1638fda2 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -133,7 +133,7 @@ public class LauncherModel extends BroadcastReceiver } }; - public interface Callbacks { + public interface Callbacks extends LauncherAppWidgetHost.ProviderChangedListener { public boolean setLoadOnResume(); public int getCurrentWorkspaceScreen(); public void clearPendingBinds(); @@ -159,7 +159,6 @@ public class LauncherModel extends BroadcastReceiver HashSet<String> packageNames, HashSet<ComponentName> components, UserHandle user); public void bindAppInfosRemoved(ArrayList<AppInfo> appInfos); - public void notifyWidgetProvidersChanged(); public void bindAllWidgets(MultiHashMap<PackageItemInfo, WidgetItem> widgets); public void onPageBoundSynchronously(int page); public void executeOnNextDraw(ViewOnDrawExecutor executor); diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index 4813571f5..dc83f36ad 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -1031,7 +1031,7 @@ public class LauncherProvider extends ContentProvider { } public AppWidgetHost newLauncherWidgetHost() { - return new AppWidgetHost(mContext, Launcher.APPWIDGET_HOST_ID); + return new LauncherAppWidgetHost(mContext); } @Override diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java index 185c8879d..87f3ddaf4 100644 --- a/src/com/android/launcher3/PagedView.java +++ b/src/com/android/launcher3/PagedView.java @@ -48,6 +48,7 @@ import android.view.animation.Interpolator; import com.android.launcher3.anim.PropertyListBuilder; import com.android.launcher3.pageindicators.PageIndicator; +import com.android.launcher3.touch.OverScroll; import com.android.launcher3.util.Themes; import com.android.launcher3.util.Thunk; @@ -68,10 +69,8 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc public static final int PAGE_SNAP_ANIMATION_DURATION = 750; protected static final int SLOW_PAGE_SNAP_ANIMATION_DURATION = 950; - // Overscroll constants + // OverScroll constants private final static int OVERSCROLL_PAGE_SNAP_ANIMATION_DURATION = 270; - private static final float OVERSCROLL_ACCELERATE_FACTOR = 2; - private static final float OVERSCROLL_DAMP_FACTOR = 0.07f; private static final float RETURN_TO_ORIGINAL_PAGE_THRESHOLD = 0.33f; // The page is moved more than halfway, automatically move to the next page on touch up. @@ -188,7 +187,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc // Convenience/caching private static final Matrix sTmpInvMatrix = new Matrix(); private static final float[] sTmpPoint = new float[2]; - private static final int[] sTmpIntPoint = new int[2]; private static final Rect sTmpRect = new Rect(); protected final Rect mInsets = new Rect(); @@ -233,8 +231,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc mMinSnapVelocity = (int) (MIN_SNAP_VELOCITY * density); setOnHierarchyChangeListener(this); setWillNotDraw(false); - - int edgeEffectColor = Themes.getAttrColor(getContext(), android.R.attr.colorEdgeEffect); } protected void setDefaultInterpolator(Interpolator interpolator) { @@ -1305,29 +1301,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc } } - // This curve determines how the effect of scrolling over the limits of the page dimishes - // as the user pulls further and further from the bounds - private float overScrollInfluenceCurve(float f) { - f -= 1.0f; - return f * f * f + 1.0f; - } - - protected float acceleratedOverFactor(float amount) { - int screenSize = getViewportWidth(); - - // We want to reach the max over scroll effect when the user has - // over scrolled half the size of the screen - float f = OVERSCROLL_ACCELERATE_FACTOR * (amount / screenSize); - - if (Float.compare(f, 0f) == 0) return 0; - - // Clamp this factor, f, to -1 < f < 1 - if (Math.abs(f) >= 1) { - f /= Math.abs(f); - } - return f; - } - // While layout transitions are occurring, a child's position may stray from its baseline // position. This method returns the magnitude of this stray at any given time. public int getLayoutTransitionOffsetForPage(int index) { @@ -1348,20 +1321,9 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc } protected void dampedOverScroll(float amount) { - int screenSize = getViewportWidth(); - - float f = (amount / screenSize); - - if (Float.compare(f, 0f) == 0) return; - - f = f / (Math.abs(f)) * (overScrollInfluenceCurve(Math.abs(f))); + if (Float.compare(amount, 0f) == 0) return; - // Clamp this factor, f, to -1 < f < 1 - if (Math.abs(f) >= 1) { - f /= Math.abs(f); - } - - int overScrollAmount = (int) Math.round(OVERSCROLL_DAMP_FACTOR * f * screenSize); + int overScrollAmount = OverScroll.dampedScroll(amount, getViewportWidth()); if (amount < 0) { mOverScrollX = overScrollAmount; super.scrollTo(mOverScrollX, getScrollY()); @@ -1376,14 +1338,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc dampedOverScroll(amount); } - protected float maxOverScroll() { - // Using the formula in overScroll, assuming that f = 1.0 (which it should generally not - // exceed). Used to find out how much extra wallpaper we need for the over scroll effect - float f = 1.0f; - f = f / (Math.abs(f)) * (overScrollInfluenceCurve(Math.abs(f))); - return OVERSCROLL_DAMP_FACTOR * f; - } - /** * return true if freescroll has been enabled, false otherwise */ diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index f781a3d1d..a2270d6c5 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -52,8 +52,10 @@ import android.view.accessibility.AccessibilityManager; import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; import android.widget.Toast; + import com.android.launcher3.Launcher.CustomContentCallbacks; import com.android.launcher3.Launcher.LauncherOverlay; +import com.android.launcher3.LauncherAppWidgetHost.ProviderChangedListener; import com.android.launcher3.UninstallDropTarget.DropTargetSource; import com.android.launcher3.accessibility.AccessibleDragListenerAdapter; import com.android.launcher3.accessibility.OverviewAccessibilityDelegate; @@ -86,6 +88,7 @@ import com.android.launcher3.util.VerticalFlingDetector; import com.android.launcher3.util.WallpaperOffsetInterpolator; import com.android.launcher3.widget.PendingAddShortcutInfo; import com.android.launcher3.widget.PendingAddWidgetInfo; + import java.util.ArrayList; import java.util.HashSet; import java.util.Set; @@ -4079,7 +4082,7 @@ public class Workspace extends PagedView * Used as a workaround to ensure that the AppWidgetService receives the * PACKAGE_ADDED broadcast before updating widgets. */ - private class DeferredWidgetRefresh implements Runnable { + private class DeferredWidgetRefresh implements Runnable, ProviderChangedListener { private final ArrayList<LauncherAppWidgetInfo> mInfos; private final LauncherAppWidgetHost mHost; private final Handler mHandler; @@ -4122,6 +4125,11 @@ public class Workspace extends PagedView } }); } + + @Override + public void notifyWidgetProvidersChanged() { + run(); + } } private class StateTransitionListener extends AnimatorListenerAdapter diff --git a/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java index 816121995..b7c500fa6 100644 --- a/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java +++ b/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java @@ -50,8 +50,7 @@ public class ShortcutMenuAccessibilityDelegate extends LauncherAccessibilityDele if ((host.getParent() instanceof DeepShortcutView)) { info.addAction(mActions.get(ADD_TO_WORKSPACE)); } else if (host instanceof NotificationMainView) { - NotificationMainView notificationView = (NotificationMainView) host; - if (notificationView.canChildBeDismissed(notificationView)) { + if (((NotificationMainView) host).canChildBeDismissed()) { info.addAction(mActions.get(DISMISS_NOTIFICATION)); } } @@ -88,8 +87,7 @@ public class ShortcutMenuAccessibilityDelegate extends LauncherAccessibilityDele if (!(host instanceof NotificationMainView)) { return false; } - NotificationMainView notificationView = (NotificationMainView) host; - notificationView.onChildDismissed(notificationView); + ((NotificationMainView) host).onChildDismissed(); announceConfirmation(R.string.notification_dismissed); return true; } diff --git a/src/com/android/launcher3/allapps/AllAppsCaretController.java b/src/com/android/launcher3/allapps/AllAppsCaretController.java index 622322bfc..583b49f7b 100644 --- a/src/com/android/launcher3/allapps/AllAppsCaretController.java +++ b/src/com/android/launcher3/allapps/AllAppsCaretController.java @@ -22,13 +22,14 @@ import android.view.animation.Interpolator; import com.android.launcher3.Launcher; import com.android.launcher3.R; import com.android.launcher3.pageindicators.CaretDrawable; +import com.android.launcher3.touch.SwipeDetector; public class AllAppsCaretController { // Determines when the caret should flip. Should be accessed via getThreshold() private static final float CARET_THRESHOLD = 0.015f; private static final float CARET_THRESHOLD_LAND = 0.5f; // The velocity at which the caret will peak (i.e. exhibit a 90 degree bend) - private static final float PEAK_VELOCITY = VerticalPullDetector.RELEASE_VELOCITY_PX_MS * .7f; + private static final float PEAK_VELOCITY = SwipeDetector.RELEASE_VELOCITY_PX_MS * .7f; private Launcher mLauncher; diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java index 657af15cb..494cd5ac5 100644 --- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java +++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java @@ -36,6 +36,8 @@ 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; @@ -60,7 +62,7 @@ public class AllAppsRecyclerView extends BaseRecyclerView implements LogContaine private SpringAnimationHandler mSpringAnimationHandler; private OverScrollHelper mOverScrollHelper; - private VerticalPullDetector mPullDetector; + private SwipeDetector mPullDetector; private float mContentTranslationY = 0; public static final Property<AllAppsRecyclerView, Float> CONTENT_TRANS_Y = @@ -97,9 +99,8 @@ public class AllAppsRecyclerView extends BaseRecyclerView implements LogContaine R.dimen.all_apps_empty_search_bg_top_offset); mOverScrollHelper = new OverScrollHelper(); - mPullDetector = new VerticalPullDetector(getContext()); - mPullDetector.setListener(mOverScrollHelper); - mPullDetector.setDetectableScrollConditions(VerticalPullDetector.DIRECTION_BOTH, true); + mPullDetector = new SwipeDetector(getContext(), mOverScrollHelper, SwipeDetector.VERTICAL); + mPullDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_BOTH, true); } public void setSpringAnimationHandler(SpringAnimationHandler springAnimationHandler) { @@ -198,8 +199,6 @@ public class AllAppsRecyclerView extends BaseRecyclerView implements LogContaine @Override public void onDraw(Canvas c) { - c.translate(0, mContentTranslationY); - // Draw the background if (mEmptySearchBackground != null && mEmptySearchBackground.getAlpha() > 0) { mEmptySearchBackground.draw(c); @@ -208,6 +207,13 @@ public class AllAppsRecyclerView extends BaseRecyclerView implements LogContaine super.onDraw(c); } + @Override + protected void dispatchDraw(Canvas canvas) { + canvas.translate(0, mContentTranslationY); + super.dispatchDraw(canvas); + canvas.translate(0, -mContentTranslationY); + } + public float getContentTranslationY() { return mContentTranslationY; } @@ -333,6 +339,22 @@ public class AllAppsRecyclerView extends BaseRecyclerView implements LogContaine mFastScrollHelper.onSetAdapter((AllAppsGridAdapter) adapter); } + @Override + protected float getBottomFadingEdgeStrength() { + // No bottom fading edge. + return 0; + } + + @Override + protected boolean isPaddingOffsetRequired() { + return true; + } + + @Override + protected int getTopPaddingOffset() { + return -getPaddingTop(); + } + /** * Updates the bounds for the scrollbar. */ @@ -502,7 +524,7 @@ public class AllAppsRecyclerView extends BaseRecyclerView implements LogContaine } } - private class OverScrollHelper implements VerticalPullDetector.Listener { + 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; @@ -589,37 +611,7 @@ public class AllAppsRecyclerView extends BaseRecyclerView implements LogContaine } private float getDampedOverScroll(float y) { - return dampedOverScroll(y, getHeight()) * MAX_OVERSCROLL_PERCENTAGE; - } - - /** - * This curve determines how the effect of scrolling over the limits of the page diminishes - * as the user pulls further and further from the bounds - * - * @param f The percentage of how much the user has overscrolled. - * @return A transformed percentage based on the influence curve. - */ - private float overScrollInfluenceCurve(float f) { - f -= 1.0f; - return f * f * f + 1.0f; - } - - /** - * @param amount The original amount overscrolled. - * @param max The maximum amount that the View can overscroll. - * @return The dampened overscroll amount. - */ - private float dampedOverScroll(float amount, float max) { - float f = amount / max; - if (Float.compare(f, 0) == 0) return 0; - f = f / (Math.abs(f)) * (overScrollInfluenceCurve(Math.abs(f))); - - // Clamp this factor, f, to -1 < f < 1 - if (Math.abs(f) >= 1) { - f /= Math.abs(f); - } - - return Math.round(f * max); + return OverScroll.dampedScroll(y, getHeight()); } } } diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java index 44e0a0474..6896b37d9 100644 --- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java +++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java @@ -25,6 +25,7 @@ import com.android.launcher3.Workspace; import com.android.launcher3.anim.SpringAnimationHandler; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.graphics.GradientView; +import com.android.launcher3.touch.SwipeDetector; import com.android.launcher3.userevent.nano.LauncherLogProto.Action; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; import com.android.launcher3.util.SystemUiController; @@ -41,7 +42,7 @@ import com.android.launcher3.util.TouchController; * If release velocity < THRES1, snap according to either top or bottom depending on whether it's * closer to top or closer to the page indicator. */ -public class AllAppsTransitionController implements TouchController, VerticalPullDetector.Listener, +public class AllAppsTransitionController implements TouchController, SwipeDetector.Listener, SearchUiManager.OnScrollRangeChangeListener { private static final String TAG = "AllAppsTrans"; @@ -51,8 +52,8 @@ public class AllAppsTransitionController implements TouchController, VerticalPul private final Interpolator mHotseatAccelInterpolator = new AccelerateInterpolator(1.5f); private final Interpolator mDecelInterpolator = new DecelerateInterpolator(3f); private final Interpolator mFastOutSlowInInterpolator = new FastOutSlowInInterpolator(); - private final VerticalPullDetector.ScrollInterpolator mScrollInterpolator - = new VerticalPullDetector.ScrollInterpolator(); + private final SwipeDetector.ScrollInterpolator mScrollInterpolator + = new SwipeDetector.ScrollInterpolator(); private static final float PARALLAX_COEFFICIENT = .125f; private static final int SINGLE_FRAME_MS = 16; @@ -68,7 +69,7 @@ public class AllAppsTransitionController implements TouchController, VerticalPul private float mStatusBarHeight; private final Launcher mLauncher; - private final VerticalPullDetector mDetector; + private final SwipeDetector mDetector; private final ArgbEvaluator mEvaluator; private final boolean mIsDarkTheme; @@ -104,8 +105,7 @@ public class AllAppsTransitionController implements TouchController, VerticalPul public AllAppsTransitionController(Launcher l) { mLauncher = l; - mDetector = new VerticalPullDetector(l); - mDetector.setListener(this); + mDetector = new SwipeDetector(l, this, SwipeDetector.VERTICAL); mShiftRange = DEFAULT_SHIFT_RANGE; mProgress = 1f; @@ -134,17 +134,17 @@ public class AllAppsTransitionController implements TouchController, VerticalPul if (mDetector.isIdleState()) { if (mLauncher.isAllAppsVisible()) { - directionsToDetectScroll |= VerticalPullDetector.DIRECTION_DOWN; + directionsToDetectScroll |= SwipeDetector.DIRECTION_NEGATIVE; } else { - directionsToDetectScroll |= VerticalPullDetector.DIRECTION_UP; + directionsToDetectScroll |= SwipeDetector.DIRECTION_POSITIVE; } } else { if (isInDisallowRecatchBottomZone()) { - directionsToDetectScroll |= VerticalPullDetector.DIRECTION_UP; + directionsToDetectScroll |= SwipeDetector.DIRECTION_POSITIVE; } else if (isInDisallowRecatchTopZone()) { - directionsToDetectScroll |= VerticalPullDetector.DIRECTION_DOWN; + directionsToDetectScroll |= SwipeDetector.DIRECTION_NEGATIVE; } else { - directionsToDetectScroll |= VerticalPullDetector.DIRECTION_BOTH; + directionsToDetectScroll |= SwipeDetector.DIRECTION_BOTH; ignoreSlopWhenSettling = true; } } @@ -358,7 +358,7 @@ public class AllAppsTransitionController implements TouchController, VerticalPul } private void calculateDuration(float velocity, float disp) { - mAnimationDuration = mDetector.calculateDuration(velocity, disp / mShiftRange); + mAnimationDuration = SwipeDetector.calculateDuration(velocity, disp / mShiftRange); } public boolean animateToAllApps(AnimatorSet animationOut, long duration) { diff --git a/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java index 21eb3fba0..26f6ec357 100644 --- a/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java +++ b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java @@ -71,10 +71,6 @@ public class DefaultAppSearchAlgorithm implements SearchAlgorithm { return result; } - public static boolean matches(AppInfo info, String query) { - return matches(info, query, StringMatcher.getInstance()); - } - public static boolean matches(AppInfo info, String query, StringMatcher matcher) { int queryLength = query.length(); diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompat.java b/src/com/android/launcher3/compat/AppWidgetManagerCompat.java index 3efbbfba5..4e00eae9d 100644 --- a/src/com/android/launcher3/compat/AppWidgetManagerCompat.java +++ b/src/com/android/launcher3/compat/AppWidgetManagerCompat.java @@ -16,15 +16,12 @@ package com.android.launcher3.compat; -import android.app.Activity; -import android.appwidget.AppWidgetHost; import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProviderInfo; import android.content.ComponentName; import android.content.Context; import android.os.Bundle; import android.os.UserHandle; -import android.support.annotation.NonNull; import android.support.annotation.Nullable; import com.android.launcher3.LauncherAppWidgetProviderInfo; @@ -76,9 +73,6 @@ public abstract class AppWidgetManagerCompat { public abstract boolean bindAppWidgetIdIfAllowed( int appWidgetId, AppWidgetProviderInfo info, Bundle options); - public abstract void startConfigActivity(AppWidgetProviderInfo info, int widgetId, - Activity activity, AppWidgetHost host, int requestCode); - public abstract LauncherAppWidgetProviderInfo findProvider( ComponentName provider, UserHandle user); diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java b/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java index f239f5c31..cb3bd6c7d 100644 --- a/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java +++ b/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java @@ -16,24 +16,21 @@ package com.android.launcher3.compat; -import android.app.Activity; -import android.appwidget.AppWidgetHost; import android.appwidget.AppWidgetProviderInfo; -import android.content.ActivityNotFoundException; import android.content.ComponentName; import android.content.Context; import android.os.Bundle; import android.os.UserHandle; import android.os.UserManager; import android.support.annotation.Nullable; -import android.widget.Toast; import com.android.launcher3.LauncherAppWidgetProviderInfo; -import com.android.launcher3.R; +import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.PackageUserKey; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -49,6 +46,9 @@ class AppWidgetManagerCompatVL extends AppWidgetManagerCompat { @Override public List<AppWidgetProviderInfo> getAllProviders(@Nullable PackageUserKey packageUser) { + if (FeatureFlags.GO_DISABLE_WIDGETS) { + return Collections.emptyList(); + } if (packageUser == null) { ArrayList<AppWidgetProviderInfo> providers = new ArrayList<AppWidgetProviderInfo>(); for (UserHandle user : mUserManager.getUserProfiles()) { @@ -71,24 +71,20 @@ class AppWidgetManagerCompatVL extends AppWidgetManagerCompat { @Override public boolean bindAppWidgetIdIfAllowed(int appWidgetId, AppWidgetProviderInfo info, Bundle options) { + if (FeatureFlags.GO_DISABLE_WIDGETS) { + return false; + } return mAppWidgetManager.bindAppWidgetIdIfAllowed( appWidgetId, info.getProfile(), info.provider, options); } @Override - public void startConfigActivity(AppWidgetProviderInfo info, int widgetId, Activity activity, - AppWidgetHost host, int requestCode) { - try { - host.startAppWidgetConfigureActivityForResult(activity, widgetId, 0, requestCode, null); - } catch (ActivityNotFoundException | SecurityException e) { - Toast.makeText(activity, R.string.activity_not_found, Toast.LENGTH_SHORT).show(); - } - } - - @Override public LauncherAppWidgetProviderInfo findProvider(ComponentName provider, UserHandle user) { - for (AppWidgetProviderInfo info : mAppWidgetManager - .getInstalledProvidersForProfile(user)) { + if (FeatureFlags.GO_DISABLE_WIDGETS) { + return null; + } + for (AppWidgetProviderInfo info : + getAllProviders(new PackageUserKey(provider.getPackageName(), user))) { if (info.provider.equals(provider)) { return LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, info); } @@ -99,6 +95,9 @@ class AppWidgetManagerCompatVL extends AppWidgetManagerCompat { @Override public HashMap<ComponentKey, AppWidgetProviderInfo> getAllProvidersMap() { HashMap<ComponentKey, AppWidgetProviderInfo> result = new HashMap<>(); + if (FeatureFlags.GO_DISABLE_WIDGETS) { + return result; + } for (UserHandle user : mUserManager.getUserProfiles()) { for (AppWidgetProviderInfo info : mAppWidgetManager.getInstalledProvidersForProfile(user)) { diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompatVO.java b/src/com/android/launcher3/compat/AppWidgetManagerCompatVO.java index 1c48a13bd..44158edbf 100644 --- a/src/com/android/launcher3/compat/AppWidgetManagerCompatVO.java +++ b/src/com/android/launcher3/compat/AppWidgetManagerCompatVO.java @@ -20,8 +20,10 @@ import android.appwidget.AppWidgetProviderInfo; import android.content.Context; import android.support.annotation.Nullable; +import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.util.PackageUserKey; +import java.util.Collections; import java.util.List; class AppWidgetManagerCompatVO extends AppWidgetManagerCompatVL { @@ -32,6 +34,9 @@ class AppWidgetManagerCompatVO extends AppWidgetManagerCompatVL { @Override public List<AppWidgetProviderInfo> getAllProviders(@Nullable PackageUserKey packageUser) { + if (FeatureFlags.GO_DISABLE_WIDGETS) { + return Collections.emptyList(); + } if (packageUser == null) { return super.getAllProviders(null); } diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java new file mode 100644 index 000000000..7964dd15d --- /dev/null +++ b/src/com/android/launcher3/config/BaseFlags.java @@ -0,0 +1,73 @@ +/* + * 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.config; + +/** + * Defines a set of flags used to control various launcher behaviors. + * + * All the flags should be defined here with appropriate default values. To override a value, + * redefine it in {@link FeatureFlags}. + * + * This class is kept package-private to prevent direct access. + */ +abstract class BaseFlags { + + BaseFlags() {} + + public static final boolean IS_DOGFOOD_BUILD = false; + + // Custom flags go below this + public static boolean LAUNCHER3_DISABLE_ICON_NORMALIZATION = false; + public static boolean LAUNCHER3_LEGACY_FOLDER_ICON = false; + public static boolean LAUNCHER3_DISABLE_PINCH_TO_OVERVIEW = false; + public static boolean LAUNCHER3_ALL_APPS_PULL_UP = true; + public static boolean LAUNCHER3_NEW_FOLDER_ANIMATION = true; + // When enabled allows to use any point on the fast scrollbar to start dragging. + public static final boolean LAUNCHER3_DIRECT_SCROLL = true; + // When enabled while all-apps open, the soft input will be set to adjust resize . + public static final boolean LAUNCHER3_UPDATE_SOFT_INPUT_MODE = false; + // 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 uses the AllAppsRadialGradientAndScrimDrawable for all apps + public static final boolean LAUNCHER3_GRADIENT_ALL_APPS = true; + // 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; + + // Feature flag to enable moving the QSB on the 0th screen of the workspace. + public static final boolean QSB_ON_FIRST_SCREEN = true; + // When enabled the all-apps icon is not added to the hotseat. + public static final boolean NO_ALL_APPS_ICON = true; + // When enabled fling down gesture on the first workspace triggers search. + public static final boolean PULLDOWN_SEARCH = false; + // When enabled the status bar may show dark icons based on the top of the wallpaper. + public static final boolean LIGHT_STATUS_BAR = false; + // When enabled icons are badged with the number of notifications associated with that app. + public static final boolean BADGE_ICONS = true; + // When enabled, icons not supporting {@link AdaptiveIconDrawable} will be wrapped in {@link FixedScaleDrawable}. + public static final boolean LEGACY_ICON_TREATMENT = true; + // When enabled, adaptive icons would have shadows baked when being stored to icon cache. + public static final boolean ADAPTIVE_ICON_SHADOW = true; + // When enabled, app discovery will be enabled if service is implemented + public static final boolean DISCOVERY_ENABLED = false; + // When enabled, the qsb will be moved to the hotseat. + public static final boolean QSB_IN_HOTSEAT = true; + + // Features to control Launcher3Go behavior + public static final boolean GO_DISABLE_WIDGETS = false; +} diff --git a/src/com/android/launcher3/dragndrop/AddItemActivity.java b/src/com/android/launcher3/dragndrop/AddItemActivity.java index 01893bdaf..c843e7266 100644 --- a/src/com/android/launcher3/dragndrop/AddItemActivity.java +++ b/src/com/android/launcher3/dragndrop/AddItemActivity.java @@ -16,14 +16,8 @@ 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.AppWidgetHost; import android.appwidget.AppWidgetManager; import android.content.ClipData; import android.content.ClipDescription; @@ -45,8 +39,8 @@ import android.view.View.OnTouchListener; import com.android.launcher3.BaseActivity; import com.android.launcher3.InstallShortcutReceiver; import com.android.launcher3.InvariantDeviceProfile; -import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppState; +import com.android.launcher3.LauncherAppWidgetHost; import com.android.launcher3.LauncherAppWidgetProviderInfo; import com.android.launcher3.R; import com.android.launcher3.Utilities; @@ -61,6 +55,11 @@ import com.android.launcher3.widget.PendingAddWidgetInfo; 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 { @@ -78,7 +77,7 @@ public class AddItemActivity extends BaseActivity implements OnLongClickListener private LivePreviewWidgetCell mWidgetCell; // Widget request specific options. - private AppWidgetHost mAppWidgetHost; + private LauncherAppWidgetHost mAppWidgetHost; private AppWidgetManagerCompat mAppWidgetManager; private PendingAddWidgetInfo mPendingWidgetInfo; private int mPendingBindWidgetId; @@ -212,7 +211,7 @@ public class AddItemActivity extends BaseActivity implements OnLongClickListener mWidgetCell.setPreview(PinItemDragListener.getPreview(mRequest)); mAppWidgetManager = AppWidgetManagerCompat.getInstance(this); - mAppWidgetHost = new AppWidgetHost(this, Launcher.APPWIDGET_HOST_ID); + mAppWidgetHost = new LauncherAppWidgetHost(this); mPendingWidgetInfo = new PendingAddWidgetInfo(widgetInfo); mPendingWidgetInfo.spanX = Math.min(mIdp.numColumns, widgetInfo.spanX); @@ -256,13 +255,8 @@ public class AddItemActivity extends BaseActivity implements OnLongClickListener } // request bind widget - Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND); - intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mPendingBindWidgetId); - intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER, - mPendingWidgetInfo.componentName); - intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER_PROFILE, - mRequest.getAppWidgetProviderInfo(this).getProfile()); - startActivityForResult(intent, REQUEST_BIND_APPWIDGET); + mAppWidgetHost.startBindFlow(this, mPendingBindWidgetId, + mRequest.getAppWidgetProviderInfo(this), REQUEST_BIND_APPWIDGET); } private void acceptWidget(int widgetId) { @@ -280,7 +274,7 @@ public class AddItemActivity extends BaseActivity implements OnLongClickListener } @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { + public void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == REQUEST_BIND_APPWIDGET) { int widgetId = data != null ? data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mPendingBindWidgetId) diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java index 09cfc1eac..b6e38bb15 100644 --- a/src/com/android/launcher3/dragndrop/DragView.java +++ b/src/com/android/launcher3/dragndrop/DragView.java @@ -16,23 +16,18 @@ package com.android.launcher3.dragndrop; -import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; - import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.FloatArrayEvaluator; import android.animation.ValueAnimator; import android.animation.ValueAnimator.AnimatorUpdateListener; -import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.content.pm.LauncherActivityInfo; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; -import android.graphics.ColorFilter; import android.graphics.ColorMatrix; import android.graphics.ColorMatrixColorFilter; -import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Path; import android.graphics.Point; @@ -44,13 +39,11 @@ import android.graphics.drawable.InsetDrawable; import android.os.Build; import android.os.Handler; import android.os.Looper; -import android.support.animation.DynamicAnimation; +import android.support.animation.FloatPropertyCompat; import android.support.animation.SpringAnimation; import android.support.animation.SpringForce; import android.view.View; import android.view.animation.DecelerateInterpolator; -import android.widget.FrameLayout; -import android.widget.ImageView; import com.android.launcher3.FastBitmapDrawable; import com.android.launcher3.ItemInfo; @@ -76,12 +69,16 @@ import com.android.launcher3.widget.PendingAddShortcutInfo; import java.util.Arrays; import java.util.List; -public class DragView extends FrameLayout { +public class DragView extends View { + private static final ColorMatrix sTempMatrix1 = new ColorMatrix(); + private static final ColorMatrix sTempMatrix2 = new ColorMatrix(); + public static final int COLOR_CHANGE_DURATION = 120; public static final int VIEW_ZOOM_DURATION = 150; @Thunk static float sDragAlpha = 1f; + private boolean mDrawBitmap = true; private Bitmap mBitmap; private Bitmap mCrossFadeBitmap; @Thunk Paint mPaint; @@ -114,16 +111,11 @@ public class DragView extends FrameLayout { private int mAnimatedShiftY; // Below variable only needed IF FeatureFlags.LAUNCHER3_SPRING_ICONS is {@code true} - private SpringAnimation mSpringX, mSpringY; - private ImageView mFgImageView, mBgImageView; + private Drawable mBgSpringDrawable, mFgSpringDrawable; + private SpringFloatValue mTranslateX, mTranslateY; private Path mScaledMaskPath; private Drawable mBadge; - - // Following three values are fine tuned with motion ux designer - private final static int STIFFNESS = 4000; - private final static float DAMPENING_RATIO = 1f; - private final static int PARALLAX_MAX_IN_DP = 8; - private final int mDelta; + private ColorMatrixColorFilter mBaseFilter; /** * Construct the drag view. @@ -193,12 +185,11 @@ public class DragView extends FrameLayout { mBlurSizeOutline = getResources().getDimensionPixelSize(R.dimen.blur_size_medium_outline); setElevation(getResources().getDimension(R.dimen.drag_elevation)); - setWillNotDraw(false); - mDelta = (int)(getResources().getDisplayMetrics().density * PARALLAX_MAX_IN_DP); } /** - * Initialize {@code #mIconDrawable} only if the icon type is app icon (not shortcut or folder). + * Initialize {@code #mIconDrawable} if the item can be represented using + * an {@link AdaptiveIconDrawable} or {@link FolderAdaptiveIcon}. */ @TargetApi(Build.VERSION_CODES.O) public void setItemInfo(final ItemInfo info) { @@ -206,7 +197,8 @@ public class DragView extends FrameLayout { return; } if (info.itemType != LauncherSettings.Favorites.ITEM_TYPE_APPLICATION && - info.itemType != LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) { + info.itemType != LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT && + info.itemType != LauncherSettings.Favorites.ITEM_TYPE_FOLDER) { return; } // Load the adaptive icon on a background thread and add the view in ui thread. @@ -216,51 +208,103 @@ public class DragView extends FrameLayout { public void run() { LauncherAppState appState = LauncherAppState.getInstance(mLauncher); Object[] outObj = new Object[1]; - Drawable dr = getFullDrawable(info, appState, outObj); + final Drawable dr = getFullDrawable(info, appState, outObj); if (dr instanceof AdaptiveIconDrawable) { int w = mBitmap.getWidth(); int h = mBitmap.getHeight(); + int blurMargin = (int) mLauncher.getResources() + .getDimension(R.dimen.blur_size_medium_outline) / 2; + + Rect bounds = new Rect(0, 0, w, h); + bounds.inset(blurMargin, blurMargin); + Utilities.scaleRectAboutCenter(bounds, + IconNormalizer.getInstance(mLauncher).getScale(dr, null, null, null)); AdaptiveIconDrawable adaptiveIcon = (AdaptiveIconDrawable) dr; - float blurSizeOutline = mLauncher.getResources() - .getDimension(R.dimen.blur_size_medium_outline); - float normalizationScale = IconNormalizer.getInstance(mLauncher) - .getScale(adaptiveIcon, null, null, null) * ((w - blurSizeOutline) / w); - adaptiveIcon.setBounds(0, 0, w, h); - - final Path mask = getMaskPath(adaptiveIcon, normalizationScale); - mFgImageView = setupImageView(adaptiveIcon.getForeground(), normalizationScale); - mBgImageView = setupImageView(adaptiveIcon.getBackground(), normalizationScale); - mSpringX = setupSpringAnimation(-w/4, w/4, DynamicAnimation.TRANSLATION_X); - mSpringY = setupSpringAnimation(-h/4, h/4, DynamicAnimation.TRANSLATION_Y); + + // Shrink very tiny bit so that the clip path is smaller than the original bitmap + // that has anti aliased edges and shadows. + Rect shrunkBounds = new Rect(bounds); + Utilities.scaleRectAboutCenter(shrunkBounds, 0.98f); + adaptiveIcon.setBounds(shrunkBounds); + final Path mask = adaptiveIcon.getIconMask(); + + mTranslateX = new SpringFloatValue(DragView.this, + w * AdaptiveIconDrawable.getExtraInsetFraction()); + mTranslateY = new SpringFloatValue(DragView.this, + h * AdaptiveIconDrawable.getExtraInsetFraction()); mBadge = getBadge(info, appState, outObj[0]); - int blurMargin = (int) blurSizeOutline / 2; - mBadge.setBounds(blurMargin, blurMargin, w - blurMargin, h - blurMargin); + mBadge.setBounds(bounds); + + bounds.inset( + (int) (-bounds.width() * AdaptiveIconDrawable.getExtraInsetFraction()), + (int) (-bounds.height() * AdaptiveIconDrawable.getExtraInsetFraction()) + ); + mBgSpringDrawable = adaptiveIcon.getBackground(); + if (mBgSpringDrawable == null) { + mBgSpringDrawable = new ColorDrawable(Color.TRANSPARENT); + } + mBgSpringDrawable.setBounds(bounds); + mFgSpringDrawable = adaptiveIcon.getForeground(); + if (mFgSpringDrawable == null) { + mFgSpringDrawable = new ColorDrawable(Color.TRANSPARENT); + } + mFgSpringDrawable.setBounds(bounds); new Handler(Looper.getMainLooper()).post(new Runnable() { @Override public void run() { // Assign the variable on the UI thread to avoid race conditions. mScaledMaskPath = mask; - addView(mBgImageView); - addView(mFgImageView); - setWillNotDraw(true); + + // Do not draw the background in case of folder as its translucent + mDrawBitmap = !(dr instanceof FolderAdaptiveIcon); if (info.isDisabled()) { FastBitmapDrawable d = new FastBitmapDrawable(null); d.setIsDisabled(true); - ColorFilter cf = d.getColorFilter(); - mBgImageView.setColorFilter(cf); - mFgImageView.setColorFilter(cf); - mBadge.setColorFilter(cf); + mBaseFilter = (ColorMatrixColorFilter) d.getColorFilter(); } + updateColorFilter(); } }); } }}); } + @TargetApi(Build.VERSION_CODES.O) + private void updateColorFilter() { + if (mCurrentFilter == null) { + mPaint.setColorFilter(null); + + if (mScaledMaskPath != null) { + mBgSpringDrawable.setColorFilter(mBaseFilter); + mBgSpringDrawable.setColorFilter(mBaseFilter); + mBadge.setColorFilter(mBaseFilter); + } + } else { + ColorMatrixColorFilter currentFilter = new ColorMatrixColorFilter(mCurrentFilter); + mPaint.setColorFilter(currentFilter); + + if (mScaledMaskPath != null) { + if (mBaseFilter != null) { + mBaseFilter.getColorMatrix(sTempMatrix1); + sTempMatrix2.set(mCurrentFilter); + sTempMatrix1.postConcat(sTempMatrix2); + + currentFilter = new ColorMatrixColorFilter(sTempMatrix1); + } + + mBgSpringDrawable.setColorFilter(currentFilter); + mFgSpringDrawable.setColorFilter(currentFilter); + mBadge.setColorFilter(currentFilter); + } + } + + invalidate(); + } + /** * Returns the full drawable for {@param info}. * @param outObj this is set to the internal data associated with {@param info}, @@ -291,6 +335,14 @@ public class DragView extends FrameLayout { return sm.getShortcutIconDrawable(si.get(0), appState.getInvariantDeviceProfile().fillResIconDpi); } + } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) { + FolderAdaptiveIcon icon = FolderAdaptiveIcon.createFolderAdaptiveIcon( + mLauncher, info.id, new Point(mBitmap.getWidth(), mBitmap.getHeight())); + if (icon == null) { + return null; + } + outObj[0] = icon; + return icon; } else { return null; } @@ -318,84 +370,17 @@ public class DragView extends FrameLayout { float insetFraction = (iconSize - badgeSize) / iconSize; return new InsetDrawable(new FastBitmapDrawable(badge), insetFraction, insetFraction, 0, 0); + } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) { + return ((FolderAdaptiveIcon) obj).getBadge(); } else { return mLauncher.getPackageManager() .getUserBadgedIcon(new FixedSizeEmptyDrawable(iconSize), info.user); } } - private ImageView setupImageView(Drawable drawable, float normalizationScale) { - FrameLayout.LayoutParams params = new LayoutParams(MATCH_PARENT, MATCH_PARENT); - ImageView imageViewOut = new ImageView(getContext()); - imageViewOut.setLayoutParams(params); - imageViewOut.setScaleType(ImageView.ScaleType.FIT_XY); - imageViewOut.setScaleX(normalizationScale); - imageViewOut.setScaleY(normalizationScale); - imageViewOut.setImageDrawable(drawable); - return imageViewOut; - } - - private SpringAnimation setupSpringAnimation(int minValue, int maxValue, - DynamicAnimation.ViewProperty property) { - SpringAnimation s = new SpringAnimation(mFgImageView, property, 0); - s.setMinValue(minValue).setMaxValue(maxValue); - s.setSpring(new SpringForce(0) - .setDampingRatio(DAMPENING_RATIO) - .setStiffness(STIFFNESS)); - return s; - } - - @TargetApi(Build.VERSION_CODES.O) - private Path getMaskPath(AdaptiveIconDrawable dr, float normalizationScale) { - Matrix m = new Matrix(); - // Shrink very tiny bit so that the clip path is smaller than the original bitmap - // that has anti aliased edges and shadows. - float s = normalizationScale * .97f; - m.setScale(s, s, dr.getBounds().centerX(), dr.getBounds().centerY()); - Path p = new Path(); - dr.getIconMask().transform(m, p); - return p; - } - - private void applySpring(int x, int y) { - if (mSpringX == null || mSpringY == null) { - return; - } - mSpringX.animateToFinalPosition(Utilities.boundToRange(x, -mDelta, mDelta)); - mSpringY.animateToFinalPosition(Utilities.boundToRange(y, -mDelta, mDelta)); - } - - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - int w = right - left; - int h = bottom - top; - for (int i = 0; i < getChildCount(); i++) { - getChildAt(i).layout(-w / 4, -h / 4, w + w / 4, h + h / 4); - } - } - @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - int w = mBitmap.getWidth(); - int h = mBitmap.getHeight(); - setMeasuredDimension(w, h); - for (int i = 0; i < getChildCount(); i++) { - getChildAt(i).measure(w, h); - } - } - - @Override - protected void dispatchDraw(Canvas canvas) { - if (mScaledMaskPath != null) { - int cnt = canvas.save(); - canvas.drawBitmap(mBitmap, 0.0f, 0.0f, mPaint); - canvas.clipPath(mScaledMaskPath); - super.dispatchDraw(canvas); - canvas.restoreToCount(cnt); - mBadge.draw(canvas); - } else { - super.dispatchDraw(canvas); - } + setMeasuredDimension(mBitmap.getWidth(), mBitmap.getHeight()); } /** Sets the scale of the view over the normal workspace icon size. */ @@ -439,43 +424,37 @@ public class DragView extends FrameLayout { return mDragRegion; } - // Draws drag shadow for system DND. - @SuppressLint("WrongCall") - public void drawDragShadow(Canvas canvas) { - final int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG); - canvas.scale(getScaleX(), getScaleY()); - onDraw(canvas); - canvas.restoreToCount(saveCount); - } - - // Provides drag shadow metrics for system DND. - public void provideDragShadowMetrics(Point size, Point touch) { - size.set((int)(mBitmap.getWidth() * getScaleX()), (int)(mBitmap.getHeight() * getScaleY())); - - final float xGrowth = mBitmap.getWidth() * (getScaleX() - 1); - final float yGrowth = mBitmap.getHeight() * (getScaleY() - 1); - touch.set( - mRegistrationX + (int)Math.round(xGrowth / 2), - mRegistrationY + (int)Math.round(yGrowth / 2)); - } - @Override protected void onDraw(Canvas canvas) { mHasDrawn = true; - boolean crossFade = mCrossFadeProgress > 0 && mCrossFadeBitmap != null; - if (crossFade) { - int alpha = crossFade ? (int) (255 * (1 - mCrossFadeProgress)) : 255; - mPaint.setAlpha(alpha); + + if (mDrawBitmap) { + // Always draw the bitmap to mask anti aliasing due to clipPath + boolean crossFade = mCrossFadeProgress > 0 && mCrossFadeBitmap != null; + if (crossFade) { + int alpha = crossFade ? (int) (255 * (1 - mCrossFadeProgress)) : 255; + mPaint.setAlpha(alpha); + } + canvas.drawBitmap(mBitmap, 0.0f, 0.0f, mPaint); + if (crossFade) { + mPaint.setAlpha((int) (255 * mCrossFadeProgress)); + final int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG); + float sX = (mBitmap.getWidth() * 1.0f) / mCrossFadeBitmap.getWidth(); + float sY = (mBitmap.getHeight() * 1.0f) / mCrossFadeBitmap.getHeight(); + canvas.scale(sX, sY); + canvas.drawBitmap(mCrossFadeBitmap, 0.0f, 0.0f, mPaint); + canvas.restoreToCount(saveCount); + } } - canvas.drawBitmap(mBitmap, 0.0f, 0.0f, mPaint); - if (crossFade) { - mPaint.setAlpha((int) (255 * mCrossFadeProgress)); - final int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG); - float sX = (mBitmap.getWidth() * 1.0f) / mCrossFadeBitmap.getWidth(); - float sY = (mBitmap.getHeight() * 1.0f) / mCrossFadeBitmap.getHeight(); - canvas.scale(sX, sY); - canvas.drawBitmap(mCrossFadeBitmap, 0.0f, 0.0f, mPaint); - canvas.restoreToCount(saveCount); + + if (mScaledMaskPath != null) { + int cnt = canvas.save(); + canvas.clipPath(mScaledMaskPath); + mBgSpringDrawable.draw(canvas); + canvas.translate(mTranslateX.mValue, mTranslateY.mValue); + mFgSpringDrawable.draw(canvas); + canvas.restoreToCount(cnt); + mBadge.draw(canvas); } } @@ -512,8 +491,7 @@ public class DragView extends FrameLayout { animateFilterTo(m1.getArray()); } else { if (mCurrentFilter == null) { - mPaint.setColorFilter(null); - invalidate(); + updateColorFilter(); } else { animateFilterTo(new ColorMatrix().getArray()); } @@ -534,8 +512,7 @@ public class DragView extends FrameLayout { @Override public void onAnimationUpdate(ValueAnimator animation) { - mPaint.setColorFilter(new ColorMatrixColorFilter(mCurrentFilter)); - invalidate(); + updateColorFilter(); } }); mFilterAnimator.start(); @@ -590,8 +567,10 @@ public class DragView extends FrameLayout { * @param touchY the y coordinate the user touched in DragLayer coordinates */ public void move(int touchX, int touchY) { - if (touchX > 0 && touchY > 0 && mLastTouchX > 0 && mLastTouchY > 0) { - applySpring(mLastTouchX - touchX, mLastTouchY - touchY); + if (touchX > 0 && touchY > 0 && mLastTouchX > 0 && mLastTouchY > 0 + && mScaledMaskPath != null) { + mTranslateX.animateToPos(mLastTouchX - touchX); + mTranslateY.animateToPos(mLastTouchY - touchY); } mLastTouchX = touchX; mLastTouchY = touchY; @@ -642,6 +621,48 @@ public class DragView extends FrameLayout { return mInitialScale; } + private static class SpringFloatValue { + + private static final FloatPropertyCompat<SpringFloatValue> VALUE = + new FloatPropertyCompat<SpringFloatValue>("value") { + @Override + public float getValue(SpringFloatValue object) { + return object.mValue; + } + + @Override + public void setValue(SpringFloatValue object, float value) { + object.mValue = value; + object.mView.invalidate(); + } + }; + + // Following three values are fine tuned with motion ux designer + private final static int STIFFNESS = 4000; + private final static float DAMPENING_RATIO = 1f; + private final static int PARALLAX_MAX_IN_DP = 8; + + private final View mView; + private final SpringAnimation mSpring; + private final float mDelta; + + private float mValue; + + public SpringFloatValue(View view, float range) { + mView = view; + mSpring = new SpringAnimation(this, VALUE, 0) + .setMinValue(-range).setMaxValue(range) + .setSpring(new SpringForce(0) + .setDampingRatio(DAMPENING_RATIO) + .setStiffness(STIFFNESS)); + mDelta = view.getResources().getDisplayMetrics().density * PARALLAX_MAX_IN_DP; + } + + public void animateToPos(float value) { + mSpring.animateToFinalPosition(Utilities.boundToRange(value, -mDelta, mDelta)); + } + } + private static class FixedSizeEmptyDrawable extends ColorDrawable { private final int mSize; diff --git a/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java b/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java new file mode 100644 index 000000000..c90546088 --- /dev/null +++ b/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java @@ -0,0 +1,175 @@ +/* + * 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.dragndrop; + +import android.annotation.TargetApi; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.PixelFormat; +import android.graphics.Point; +import android.graphics.drawable.AdaptiveIconDrawable; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.util.Log; + +import com.android.launcher3.Launcher; +import com.android.launcher3.MainThreadExecutor; +import com.android.launcher3.R; +import com.android.launcher3.folder.FolderIcon; +import com.android.launcher3.folder.PreviewBackground; +import com.android.launcher3.util.Preconditions; + +import java.util.concurrent.Callable; + +/** + * {@link AdaptiveIconDrawable} representation of a {@link FolderIcon} + */ +@TargetApi(Build.VERSION_CODES.O) +public class FolderAdaptiveIcon extends AdaptiveIconDrawable { + private static final String TAG = "FolderAdaptiveIcon"; + + private final Drawable mBadge; + private final Path mMask; + + private FolderAdaptiveIcon(Drawable bg, Drawable fg, Drawable badge, Path mask) { + super(bg, fg); + mBadge = badge; + mMask = mask; + } + + @Override + public Path getIconMask() { + return mMask; + } + + public Drawable getBadge() { + return mBadge; + } + + public static FolderAdaptiveIcon createFolderAdaptiveIcon( + final Launcher launcher, final long folderId, Point dragViewSize) { + Preconditions.assertNonUiThread(); + int margin = launcher.getResources() + .getDimensionPixelSize(R.dimen.blur_size_medium_outline); + + // Allocate various bitmaps on the background thread, because why not! + final Bitmap badge = Bitmap.createBitmap( + dragViewSize.x - margin, dragViewSize.y - margin, Bitmap.Config.ARGB_8888); + + // The bitmap for the preview is generated larger than needed to allow for the spring effect + float sizeScaleFactor = 1 + 2 * AdaptiveIconDrawable.getExtraInsetFraction(); + final Bitmap preview = Bitmap.createBitmap( + (int) (dragViewSize.x * sizeScaleFactor), (int) (dragViewSize.y * sizeScaleFactor), + Bitmap.Config.ARGB_8888); + + // Create the actual drawable on the UI thread to avoid race conditions with + // FolderIcon draw pass + try { + return new MainThreadExecutor().submit(new Callable<FolderAdaptiveIcon>() { + @Override + public FolderAdaptiveIcon call() throws Exception { + FolderIcon icon = launcher.findFolderIcon(folderId); + return icon == null ? null : createDrawableOnUiThread(icon, badge, preview); + } + }).get(); + } catch (Exception e) { + Log.e(TAG, "Unable to create folder icon", e); + return null; + } + } + + /** + * Initializes various bitmaps on the UI thread and returns the final drawable. + */ + private static FolderAdaptiveIcon createDrawableOnUiThread(FolderIcon icon, + Bitmap badgeBitmap, Bitmap previewBitmap) { + Preconditions.assertUIThread(); + float margin = icon.getResources().getDimension(R.dimen.blur_size_medium_outline) / 2; + + Canvas c = new Canvas(); + PreviewBackground bg = icon.getFolderBackground(); + + // Initialize badge + c.setBitmap(badgeBitmap); + bg.drawShadow(c); + bg.drawBackgroundStroke(c); + icon.drawBadge(c); + + // Initialize preview + float shiftFactor = AdaptiveIconDrawable.getExtraInsetFraction() / + (1 + 2 * AdaptiveIconDrawable.getExtraInsetFraction()); + float previewShiftX = shiftFactor * previewBitmap.getWidth(); + float previewShiftY = shiftFactor * previewBitmap.getHeight(); + + c.setBitmap(previewBitmap); + c.translate(previewShiftX, previewShiftY); + icon.getPreviewItemManager().draw(c); + c.setBitmap(null); + + // Initialize mask + Path mask = new Path(); + Matrix m = new Matrix(); + m.setTranslate(margin, margin); + bg.getClipPath().transform(m, mask); + + ShiftedBitmapDrawable badge = new ShiftedBitmapDrawable(badgeBitmap, margin, margin); + ShiftedBitmapDrawable foreground = new ShiftedBitmapDrawable(previewBitmap, + margin - previewShiftX, margin - previewShiftY); + + return new FolderAdaptiveIcon(new ColorDrawable(bg.getBgColor()), foreground, badge, mask); + } + + /** + * A simple drawable which draws a bitmap at a fixed position irrespective of the bounds + */ + private static class ShiftedBitmapDrawable extends Drawable { + + private final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG); + private final Bitmap mBitmap; + private final float mShiftX; + private final float mShiftY; + + ShiftedBitmapDrawable(Bitmap bitmap, float shiftX, float shiftY) { + mBitmap = bitmap; + mShiftX = shiftX; + mShiftY = shiftY; + } + + @Override + public void draw(Canvas canvas) { + canvas.drawBitmap(mBitmap, mShiftX, mShiftY, mPaint); + } + + @Override + public void setAlpha(int i) { } + + @Override + public void setColorFilter(ColorFilter colorFilter) { + mPaint.setColorFilter(colorFilter); + } + + @Override + public int getOpacity() { + return PixelFormat.TRANSLUCENT; + } + } +} diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java index 84ec18410..6533b0463 100644 --- a/src/com/android/launcher3/folder/FolderIcon.java +++ b/src/com/android/launcher3/folder/FolderIcon.java @@ -440,6 +440,14 @@ public class FolderIcon extends FrameLayout implements FolderListener { invalidate(); } + public PreviewBackground getFolderBackground() { + return mBackground; + } + + public PreviewItemManager getPreviewItemManager() { + return mPreviewItemManager; + } + @Override protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); @@ -463,14 +471,11 @@ public class FolderIcon extends FrameLayout implements FolderListener { } else { saveCount = canvas.save(Canvas.CLIP_SAVE_FLAG); if (mPreviewLayoutRule.clipToBackground()) { - mBackground.clipCanvasSoftware(canvas, Region.Op.INTERSECT); + canvas.clipPath(mBackground.getClipPath(), Region.Op.INTERSECT); } } - // The items are drawn in coordinates relative to the preview offset - canvas.translate(mBackground.basePreviewOffsetX, mBackground.basePreviewOffsetY); mPreviewItemManager.draw(canvas); - canvas.translate(-mBackground.basePreviewOffsetX, -mBackground.basePreviewOffsetY); if (mPreviewLayoutRule.clipToBackground() && canvas.isHardwareAccelerated()) { mBackground.clipCanvasHardware(canvas); @@ -481,6 +486,10 @@ public class FolderIcon extends FrameLayout implements FolderListener { mBackground.drawBackgroundStroke(canvas); } + drawBadge(canvas); + } + + public void drawBadge(Canvas canvas) { if ((mBadgeInfo != null && mBadgeInfo.hasBadge()) || mBadgeScale > 0) { int offsetX = mBackground.getOffsetX(); int offsetY = mBackground.getOffsetY(); diff --git a/src/com/android/launcher3/folder/PreviewBackground.java b/src/com/android/launcher3/folder/PreviewBackground.java index 61490eee8..eba5d984d 100644 --- a/src/com/android/launcher3/folder/PreviewBackground.java +++ b/src/com/android/launcher3/folder/PreviewBackground.java @@ -195,19 +195,28 @@ public class PreviewBackground { invalidate(); } + public int getBgColor() { + int alpha = (int) Math.min(MAX_BG_OPACITY, BG_OPACITY * mColorMultiplier); + return ColorUtils.setAlphaComponent(mBgColor, alpha); + } + public void drawBackground(Canvas canvas) { mPaint.setStyle(Paint.Style.FILL); - int alpha = (int) Math.min(MAX_BG_OPACITY, BG_OPACITY * mColorMultiplier); - mPaint.setColor(ColorUtils.setAlphaComponent(mBgColor, alpha)); + mPaint.setColor(getBgColor()); drawCircle(canvas, 0 /* deltaRadius */); - // Draw shadow. + drawShadow(canvas); + } + + public void drawShadow(Canvas canvas) { if (mShadowShader == null) { return; } + float radius = getScaledRadius(); float shadowRadius = radius + mStrokeWidth; + mPaint.setStyle(Paint.Style.FILL); mPaint.setColor(Color.BLACK); int offsetX = getOffsetX(); int offsetY = getOffsetY(); @@ -219,7 +228,7 @@ public class PreviewBackground { } else { saveCount = canvas.save(Canvas.CLIP_SAVE_FLAG); - clipCanvasSoftware(canvas, Region.Op.DIFFERENCE); + canvas.clipPath(getClipPath(), Region.Op.DIFFERENCE); } mShaderMatrix.setScale(shadowRadius, shadowRadius); @@ -295,12 +304,11 @@ public class PreviewBackground { radius - deltaRadius, mPaint); } - // It is the callers responsibility to save and restore the canvas layers. - void clipCanvasSoftware(Canvas canvas, Region.Op op) { + public Path getClipPath() { mPath.reset(); float r = getScaledRadius(); mPath.addCircle(r + getOffsetX(), r + getOffsetY(), r, Path.Direction.CW); - canvas.clipPath(mPath, op); + return mPath; } // It is the callers responsibility to save and restore the canvas layers. diff --git a/src/com/android/launcher3/folder/PreviewItemManager.java b/src/com/android/launcher3/folder/PreviewItemManager.java index 2ecb54ca8..2d979a661 100644 --- a/src/com/android/launcher3/folder/PreviewItemManager.java +++ b/src/com/android/launcher3/folder/PreviewItemManager.java @@ -146,6 +146,10 @@ public class PreviewItemManager { } public void draw(Canvas canvas) { + // The items are drawn in coordinates relative to the preview offset + PreviewBackground bg = mIcon.getFolderBackground(); + canvas.translate(bg.basePreviewOffsetX, bg.basePreviewOffsetY); + float firstPageItemsTransX = 0; if (mShouldSlideInFirstPage) { drawParams(canvas, mCurrentPageParams, mCurrentPageItemsTransX); @@ -154,6 +158,7 @@ public class PreviewItemManager { } drawParams(canvas, mFirstPageParams, firstPageItemsTransX); + canvas.translate(-bg.basePreviewOffsetX, -bg.basePreviewOffsetY); } public void onParamsChanged() { diff --git a/src/com/android/launcher3/graphics/GradientView.java b/src/com/android/launcher3/graphics/GradientView.java index 678396df1..5455b43ec 100644 --- a/src/com/android/launcher3/graphics/GradientView.java +++ b/src/com/android/launcher3/graphics/GradientView.java @@ -27,6 +27,7 @@ import android.graphics.RectF; import android.graphics.Shader; import android.support.v4.graphics.ColorUtils; import android.util.AttributeSet; +import android.util.DisplayMetrics; import android.view.View; import android.view.animation.AccelerateInterpolator; import android.view.animation.Interpolator; @@ -46,7 +47,6 @@ public class GradientView extends View implements WallpaperColorInfo.OnChangeLis private static final int DEFAULT_COLOR = Color.WHITE; private static final int ALPHA_MASK_HEIGHT_DP = 500; private static final int ALPHA_MASK_WIDTH_DP = 2; - private static final int ALPHA_COLORS = 0xBF; private static final boolean DEBUG = false; private final Bitmap mAlphaGradientMask; @@ -62,7 +62,7 @@ public class GradientView extends View implements WallpaperColorInfo.OnChangeLis private final Paint mPaintNoScrim = new Paint(); private float mProgress; private final int mMaskHeight, mMaskWidth; - private final Context mAppContext; + private final int mAlphaColors; private final Paint mDebugPaint = DEBUG ? new Paint() : null; private final Interpolator mAccelerator = new AccelerateInterpolator(); private final float mAlphaStart; @@ -71,15 +71,14 @@ public class GradientView extends View implements WallpaperColorInfo.OnChangeLis public GradientView(Context context, AttributeSet attrs) { super(context, attrs); - this.mAppContext = context.getApplicationContext(); - this.mMaskHeight = Utilities.pxFromDp(ALPHA_MASK_HEIGHT_DP, - mAppContext.getResources().getDisplayMetrics()); - this.mMaskWidth = Utilities.pxFromDp(ALPHA_MASK_WIDTH_DP, - mAppContext.getResources().getDisplayMetrics()); + DisplayMetrics dm = getResources().getDisplayMetrics(); + this.mMaskHeight = Utilities.pxFromDp(ALPHA_MASK_HEIGHT_DP, dm); + this.mMaskWidth = Utilities.pxFromDp(ALPHA_MASK_WIDTH_DP, dm); Launcher launcher = Launcher.getLauncher(context); this.mAlphaStart = launcher.getDeviceProfile().isVerticalBarLayout() ? 0 : 100; this.mScrimColor = Themes.getAttrColor(context, R.attr.allAppsScrimColor); this.mWallpaperColorInfo = WallpaperColorInfo.getInstance(launcher); + mAlphaColors = getResources().getInteger(R.integer.extracted_color_gradient_alpha); updateColors(); mAlphaGradientMask = createDitheredAlphaMask(); } @@ -104,9 +103,9 @@ public class GradientView extends View implements WallpaperColorInfo.OnChangeLis private void updateColors() { this.mColor1 = ColorUtils.setAlphaComponent(mWallpaperColorInfo.getMainColor(), - ALPHA_COLORS); + mAlphaColors); this.mColor2 = ColorUtils.setAlphaComponent(mWallpaperColorInfo.getSecondaryColor(), - ALPHA_COLORS); + mAlphaColors); if (mWidth + mHeight > 0) { createRadialShader(); } diff --git a/src/com/android/launcher3/logging/FileLog.java b/src/com/android/launcher3/logging/FileLog.java index 4c83e9ac2..f7f8ef18f 100644 --- a/src/com/android/launcher3/logging/FileLog.java +++ b/src/com/android/launcher3/logging/FileLog.java @@ -29,6 +29,8 @@ import java.util.concurrent.TimeUnit; */ public final class FileLog { + protected static final boolean ENABLED = + FeatureFlags.IS_DOGFOOD_BUILD || Utilities.IS_DEBUG_DEVICE; private static final String FILE_NAME_PREFIX = "log-"; private static final DateFormat DATE_FORMAT = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT); @@ -39,7 +41,7 @@ public final class FileLog { private static File sLogsDirectory = null; public static void setDir(File logsDir) { - if (FeatureFlags.IS_DOGFOOD_BUILD || Utilities.IS_DEBUG_DEVICE) { + if (ENABLED) { synchronized (DATE_FORMAT) { // If the target directory changes, stop any active thread. if (sHandler != null && !logsDir.equals(sLogsDirectory)) { @@ -76,7 +78,7 @@ public final class FileLog { } public static void print(String tag, String msg, Exception e) { - if (!FeatureFlags.IS_DOGFOOD_BUILD) { + if (!ENABLED) { return; } String out = String.format("%s %s %s", DATE_FORMAT.format(new Date()), tag, msg); @@ -102,7 +104,7 @@ public final class FileLog { * @param out if not null, all the persisted logs are copied to the writer. */ public static void flushAll(PrintWriter out) throws InterruptedException { - if (!FeatureFlags.IS_DOGFOOD_BUILD) { + if (!ENABLED) { return; } CountDownLatch latch = new CountDownLatch(1); @@ -135,7 +137,7 @@ public final class FileLog { @Override public boolean handleMessage(Message msg) { - if (sLogsDirectory == null || !FeatureFlags.IS_DOGFOOD_BUILD) { + if (sLogsDirectory == null || !ENABLED) { return true; } switch (msg.what) { diff --git a/src/com/android/launcher3/logging/LoggerUtils.java b/src/com/android/launcher3/logging/LoggerUtils.java index 72a083b9e..ebb69c43b 100644 --- a/src/com/android/launcher3/logging/LoggerUtils.java +++ b/src/com/android/launcher3/logging/LoggerUtils.java @@ -25,6 +25,7 @@ import com.android.launcher3.InfoDropTarget; import com.android.launcher3.ItemInfo; import com.android.launcher3.LauncherSettings; import com.android.launcher3.UninstallDropTarget; +import com.android.launcher3.userevent.nano.LauncherLogExtensions.TargetExtension; import com.android.launcher3.userevent.nano.LauncherLogProto.Action; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType; @@ -159,6 +160,13 @@ public class LoggerUtils { return t; } + public static Target newTarget(int targetType, TargetExtension extension) { + Target t = new Target(); + t.type = targetType; + t.extension = extension; + return t; + } + public static Target newTarget(int targetType) { Target t = new Target(); t.type = targetType; diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java index 5bad436f8..c56325ad5 100644 --- a/src/com/android/launcher3/model/LoaderTask.java +++ b/src/com/android/launcher3/model/LoaderTask.java @@ -548,6 +548,11 @@ public class LoaderTask implements Runnable { break; case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: + if (FeatureFlags.GO_DISABLE_WIDGETS) { + c.markDeleted("Only legacy shortcuts can have null package"); + continue; + } + // Follow through case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET: // Read all Launcher-specific widget details boolean customWidget = c.itemType == diff --git a/src/com/android/launcher3/notification/FlingAnimationUtils.java b/src/com/android/launcher3/notification/FlingAnimationUtils.java deleted file mode 100644 index a1f7e49c0..000000000 --- a/src/com/android/launcher3/notification/FlingAnimationUtils.java +++ /dev/null @@ -1,356 +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.notification; - -import android.animation.Animator; -import android.content.Context; -import android.view.ViewPropertyAnimator; -import android.view.animation.Interpolator; -import android.view.animation.PathInterpolator; - -/** - * Utility class to calculate general fling animation when the finger is released. - * - * This class was copied from com.android.systemui.statusbar. - */ -public class FlingAnimationUtils { - - private static final float LINEAR_OUT_SLOW_IN_X2 = 0.35f; - private static final float LINEAR_OUT_SLOW_IN_X2_MAX = 0.68f; - private static final float LINEAR_OUT_FASTER_IN_X2 = 0.5f; - private static final float LINEAR_OUT_FASTER_IN_Y2_MIN = 0.4f; - private static final float LINEAR_OUT_FASTER_IN_Y2_MAX = 0.5f; - private static final float MIN_VELOCITY_DP_PER_SECOND = 250; - private static final float HIGH_VELOCITY_DP_PER_SECOND = 3000; - - private static final float LINEAR_OUT_SLOW_IN_START_GRADIENT = 0.75f; - private final float mSpeedUpFactor; - private final float mY2; - - private float mMinVelocityPxPerSecond; - private float mMaxLengthSeconds; - private float mHighVelocityPxPerSecond; - private float mLinearOutSlowInX2; - - private AnimatorProperties mAnimatorProperties = new AnimatorProperties(); - private PathInterpolator mInterpolator; - private float mCachedStartGradient = -1; - private float mCachedVelocityFactor = -1; - - public FlingAnimationUtils(Context ctx, float maxLengthSeconds) { - this(ctx, maxLengthSeconds, 0.0f); - } - - /** - * @param maxLengthSeconds the longest duration an animation can become in seconds - * @param speedUpFactor a factor from 0 to 1 how much the slow down should be shifted towards - * the end of the animation. 0 means it's at the beginning and no - * acceleration will take place. - */ - public FlingAnimationUtils(Context ctx, float maxLengthSeconds, float speedUpFactor) { - this(ctx, maxLengthSeconds, speedUpFactor, -1.0f, 1.0f); - } - - /** - * @param maxLengthSeconds the longest duration an animation can become in seconds - * @param speedUpFactor a factor from 0 to 1 how much the slow down should be shifted towards - * the end of the animation. 0 means it's at the beginning and no - * acceleration will take place. - * @param x2 the x value to take for the second point of the bezier spline. If a value below 0 - * is provided, the value is automatically calculated. - * @param y2 the y value to take for the second point of the bezier spline - */ - public FlingAnimationUtils(Context ctx, float maxLengthSeconds, float speedUpFactor, float x2, - float y2) { - mMaxLengthSeconds = maxLengthSeconds; - mSpeedUpFactor = speedUpFactor; - if (x2 < 0) { - mLinearOutSlowInX2 = interpolate(LINEAR_OUT_SLOW_IN_X2, - LINEAR_OUT_SLOW_IN_X2_MAX, - mSpeedUpFactor); - } else { - mLinearOutSlowInX2 = x2; - } - mY2 = y2; - - mMinVelocityPxPerSecond - = MIN_VELOCITY_DP_PER_SECOND * ctx.getResources().getDisplayMetrics().density; - mHighVelocityPxPerSecond - = HIGH_VELOCITY_DP_PER_SECOND * ctx.getResources().getDisplayMetrics().density; - } - - private static float interpolate(float start, float end, float amount) { - return start * (1.0f - amount) + end * amount; - } - - /** - * Applies the interpolator and length to the animator, such that the fling animation is - * consistent with the finger motion. - * - * @param animator the animator to apply - * @param currValue the current value - * @param endValue the end value of the animator - * @param velocity the current velocity of the motion - */ - public void apply(Animator animator, float currValue, float endValue, float velocity) { - apply(animator, currValue, endValue, velocity, Math.abs(endValue - currValue)); - } - - /** - * Applies the interpolator and length to the animator, such that the fling animation is - * consistent with the finger motion. - * - * @param animator the animator to apply - * @param currValue the current value - * @param endValue the end value of the animator - * @param velocity the current velocity of the motion - */ - public void apply(ViewPropertyAnimator animator, float currValue, float endValue, - float velocity) { - apply(animator, currValue, endValue, velocity, Math.abs(endValue - currValue)); - } - - /** - * Applies the interpolator and length to the animator, such that the fling animation is - * consistent with the finger motion. - * - * @param animator the animator to apply - * @param currValue the current value - * @param endValue the end value of the animator - * @param velocity the current velocity of the motion - * @param maxDistance the maximum distance for this interaction; the maximum animation length - * gets multiplied by the ratio between the actual distance and this value - */ - public void apply(Animator animator, float currValue, float endValue, float velocity, - float maxDistance) { - AnimatorProperties properties = getProperties(currValue, endValue, velocity, - maxDistance); - animator.setDuration(properties.duration); - animator.setInterpolator(properties.interpolator); - } - - /** - * Applies the interpolator and length to the animator, such that the fling animation is - * consistent with the finger motion. - * - * @param animator the animator to apply - * @param currValue the current value - * @param endValue the end value of the animator - * @param velocity the current velocity of the motion - * @param maxDistance the maximum distance for this interaction; the maximum animation length - * gets multiplied by the ratio between the actual distance and this value - */ - public void apply(ViewPropertyAnimator animator, float currValue, float endValue, - float velocity, float maxDistance) { - AnimatorProperties properties = getProperties(currValue, endValue, velocity, - maxDistance); - animator.setDuration(properties.duration); - animator.setInterpolator(properties.interpolator); - } - - private AnimatorProperties getProperties(float currValue, - float endValue, float velocity, float maxDistance) { - float maxLengthSeconds = (float) (mMaxLengthSeconds - * Math.sqrt(Math.abs(endValue - currValue) / maxDistance)); - float diff = Math.abs(endValue - currValue); - float velAbs = Math.abs(velocity); - float velocityFactor = mSpeedUpFactor == 0.0f - ? 1.0f : Math.min(velAbs / HIGH_VELOCITY_DP_PER_SECOND, 1.0f); - float startGradient = interpolate(LINEAR_OUT_SLOW_IN_START_GRADIENT, - mY2 / mLinearOutSlowInX2, velocityFactor); - float durationSeconds = startGradient * diff / velAbs; - Interpolator slowInInterpolator = getInterpolator(startGradient, velocityFactor); - if (durationSeconds <= maxLengthSeconds) { - mAnimatorProperties.interpolator = slowInInterpolator; - } else if (velAbs >= mMinVelocityPxPerSecond) { - - // Cross fade between fast-out-slow-in and linear interpolator with current velocity. - durationSeconds = maxLengthSeconds; - VelocityInterpolator velocityInterpolator - = new VelocityInterpolator(durationSeconds, velAbs, diff); - InterpolatorInterpolator superInterpolator = new InterpolatorInterpolator( - velocityInterpolator, slowInInterpolator, Interpolators.LINEAR_OUT_SLOW_IN); - mAnimatorProperties.interpolator = superInterpolator; - } else { - - // Just use a normal interpolator which doesn't take the velocity into account. - durationSeconds = maxLengthSeconds; - mAnimatorProperties.interpolator = Interpolators.FAST_OUT_SLOW_IN; - } - mAnimatorProperties.duration = (long) (durationSeconds * 1000); - return mAnimatorProperties; - } - - private Interpolator getInterpolator(float startGradient, float velocityFactor) { - if (startGradient != mCachedStartGradient - || velocityFactor != mCachedVelocityFactor) { - float speedup = mSpeedUpFactor * (1.0f - velocityFactor); - mInterpolator = new PathInterpolator(speedup, - speedup * startGradient, - mLinearOutSlowInX2, mY2); - mCachedStartGradient = startGradient; - mCachedVelocityFactor = velocityFactor; - } - return mInterpolator; - } - - /** - * Applies the interpolator and length to the animator, such that the fling animation is - * consistent with the finger motion for the case when the animation is making something - * disappear. - * - * @param animator the animator to apply - * @param currValue the current value - * @param endValue the end value of the animator - * @param velocity the current velocity of the motion - * @param maxDistance the maximum distance for this interaction; the maximum animation length - * gets multiplied by the ratio between the actual distance and this value - */ - public void applyDismissing(Animator animator, float currValue, float endValue, - float velocity, float maxDistance) { - AnimatorProperties properties = getDismissingProperties(currValue, endValue, velocity, - maxDistance); - animator.setDuration(properties.duration); - animator.setInterpolator(properties.interpolator); - } - - /** - * Applies the interpolator and length to the animator, such that the fling animation is - * consistent with the finger motion for the case when the animation is making something - * disappear. - * - * @param animator the animator to apply - * @param currValue the current value - * @param endValue the end value of the animator - * @param velocity the current velocity of the motion - * @param maxDistance the maximum distance for this interaction; the maximum animation length - * gets multiplied by the ratio between the actual distance and this value - */ - public void applyDismissing(ViewPropertyAnimator animator, float currValue, float endValue, - float velocity, float maxDistance) { - AnimatorProperties properties = getDismissingProperties(currValue, endValue, velocity, - maxDistance); - animator.setDuration(properties.duration); - animator.setInterpolator(properties.interpolator); - } - - private AnimatorProperties getDismissingProperties(float currValue, float endValue, - float velocity, float maxDistance) { - float maxLengthSeconds = (float) (mMaxLengthSeconds - * Math.pow(Math.abs(endValue - currValue) / maxDistance, 0.5f)); - float diff = Math.abs(endValue - currValue); - float velAbs = Math.abs(velocity); - float y2 = calculateLinearOutFasterInY2(velAbs); - - float startGradient = y2 / LINEAR_OUT_FASTER_IN_X2; - Interpolator mLinearOutFasterIn = new PathInterpolator(0, 0, LINEAR_OUT_FASTER_IN_X2, y2); - float durationSeconds = startGradient * diff / velAbs; - if (durationSeconds <= maxLengthSeconds) { - mAnimatorProperties.interpolator = mLinearOutFasterIn; - } else if (velAbs >= mMinVelocityPxPerSecond) { - - // Cross fade between linear-out-faster-in and linear interpolator with current - // velocity. - durationSeconds = maxLengthSeconds; - VelocityInterpolator velocityInterpolator - = new VelocityInterpolator(durationSeconds, velAbs, diff); - InterpolatorInterpolator superInterpolator = new InterpolatorInterpolator( - velocityInterpolator, mLinearOutFasterIn, Interpolators.LINEAR_OUT_SLOW_IN); - mAnimatorProperties.interpolator = superInterpolator; - } else { - - // Just use a normal interpolator which doesn't take the velocity into account. - durationSeconds = maxLengthSeconds; - mAnimatorProperties.interpolator = Interpolators.FAST_OUT_LINEAR_IN; - } - mAnimatorProperties.duration = (long) (durationSeconds * 1000); - return mAnimatorProperties; - } - - /** - * Calculates the y2 control point for a linear-out-faster-in path interpolator depending on the - * velocity. The faster the velocity, the more "linear" the interpolator gets. - * - * @param velocity the velocity of the gesture. - * @return the y2 control point for a cubic bezier path interpolator - */ - private float calculateLinearOutFasterInY2(float velocity) { - float t = (velocity - mMinVelocityPxPerSecond) - / (mHighVelocityPxPerSecond - mMinVelocityPxPerSecond); - t = Math.max(0, Math.min(1, t)); - return (1 - t) * LINEAR_OUT_FASTER_IN_Y2_MIN + t * LINEAR_OUT_FASTER_IN_Y2_MAX; - } - - /** - * @return the minimum velocity a gesture needs to have to be considered a fling - */ - public float getMinVelocityPxPerSecond() { - return mMinVelocityPxPerSecond; - } - - /** - * An interpolator which interpolates two interpolators with an interpolator. - */ - private static final class InterpolatorInterpolator implements Interpolator { - - private Interpolator mInterpolator1; - private Interpolator mInterpolator2; - private Interpolator mCrossfader; - - InterpolatorInterpolator(Interpolator interpolator1, Interpolator interpolator2, - Interpolator crossfader) { - mInterpolator1 = interpolator1; - mInterpolator2 = interpolator2; - mCrossfader = crossfader; - } - - @Override - public float getInterpolation(float input) { - float t = mCrossfader.getInterpolation(input); - return (1 - t) * mInterpolator1.getInterpolation(input) - + t * mInterpolator2.getInterpolation(input); - } - } - - /** - * An interpolator which interpolates with a fixed velocity. - */ - private static final class VelocityInterpolator implements Interpolator { - - private float mDurationSeconds; - private float mVelocity; - private float mDiff; - - private VelocityInterpolator(float durationSeconds, float velocity, float diff) { - mDurationSeconds = durationSeconds; - mVelocity = velocity; - mDiff = diff; - } - - @Override - public float getInterpolation(float input) { - float time = input * mDurationSeconds; - return time * mVelocity / mDiff; - } - } - - private static class AnimatorProperties { - Interpolator interpolator; - long duration; - } - -} diff --git a/src/com/android/launcher3/notification/NotificationItemView.java b/src/com/android/launcher3/notification/NotificationItemView.java index 11f6aa081..78c64d7da 100644 --- a/src/com/android/launcher3/notification/NotificationItemView.java +++ b/src/com/android/launcher3/notification/NotificationItemView.java @@ -37,6 +37,7 @@ import com.android.launcher3.anim.RoundedRectRevealOutlineProvider; import com.android.launcher3.graphics.IconPalette; import com.android.launcher3.logging.UserEventDispatcher.LogContainerProvider; import com.android.launcher3.popup.PopupItemView; +import com.android.launcher3.touch.SwipeDetector; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.util.Themes; @@ -56,7 +57,7 @@ public class NotificationItemView extends PopupItemView implements LogContainerP private TextView mHeaderCount; private NotificationMainView mMainView; private NotificationFooterLayout mFooter; - private SwipeHelper mSwipeHelper; + private SwipeDetector mSwipeDetector; private boolean mAnimatingNextIcon; private int mNotificationHeaderTextColor = Notification.COLOR_DEFAULT; @@ -75,12 +76,14 @@ public class NotificationItemView extends PopupItemView implements LogContainerP @Override protected void onFinishInflate() { super.onFinishInflate(); - mHeaderText = (TextView) findViewById(R.id.notification_text); - mHeaderCount = (TextView) findViewById(R.id.notification_count); - mMainView = (NotificationMainView) findViewById(R.id.main_view); - mFooter = (NotificationFooterLayout) findViewById(R.id.footer); - mSwipeHelper = new SwipeHelper(SwipeHelper.X, mMainView, getContext()); - mSwipeHelper.setDisableHardwareLayers(true); + mHeaderText = findViewById(R.id.notification_text); + mHeaderCount = findViewById(R.id.notification_count); + mMainView = findViewById(R.id.main_view); + mFooter = findViewById(R.id.footer); + + mSwipeDetector = new SwipeDetector(getContext(), mMainView, SwipeDetector.HORIZONTAL); + mSwipeDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_BOTH, false); + mMainView.setSwipeDetector(mSwipeDetector); } public NotificationMainView getMainView() { @@ -136,7 +139,8 @@ public class NotificationItemView extends PopupItemView implements LogContainerP return false; } getParent().requestDisallowInterceptTouchEvent(true); - return mSwipeHelper.onInterceptTouchEvent(ev); + mSwipeDetector.onTouchEvent(ev); + return mSwipeDetector.isDraggingOrSettling(); } @Override @@ -145,7 +149,7 @@ public class NotificationItemView extends PopupItemView implements LogContainerP // The notification hasn't been populated yet. return false; } - return mSwipeHelper.onTouchEvent(ev) || super.onTouchEvent(ev); + return mSwipeDetector.onTouchEvent(ev) || super.onTouchEvent(ev); } public void applyNotificationInfos(final List<NotificationInfo> notificationInfos) { diff --git a/src/com/android/launcher3/notification/NotificationMainView.java b/src/com/android/launcher3/notification/NotificationMainView.java index 9b8dd648f..5aff28db4 100644 --- a/src/com/android/launcher3/notification/NotificationMainView.java +++ b/src/com/android/launcher3/notification/NotificationMainView.java @@ -23,15 +23,17 @@ import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.RippleDrawable; import android.text.TextUtils; import android.util.AttributeSet; -import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; +import android.view.ViewPropertyAnimator; import android.widget.FrameLayout; import android.widget.TextView; import com.android.launcher3.ItemInfo; import com.android.launcher3.Launcher; import com.android.launcher3.R; +import com.android.launcher3.touch.OverScroll; +import com.android.launcher3.touch.SwipeDetector; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.util.Themes; @@ -39,7 +41,7 @@ import com.android.launcher3.util.Themes; * A {@link android.widget.FrameLayout} that contains a single notification, * e.g. icon + title + text. */ -public class NotificationMainView extends FrameLayout implements SwipeHelper.Callback { +public class NotificationMainView extends FrameLayout implements SwipeDetector.Listener { private NotificationInfo mNotificationInfo; private ViewGroup mTextAndBackground; @@ -47,6 +49,8 @@ public class NotificationMainView extends FrameLayout implements SwipeHelper.Cal private TextView mTitleView; private TextView mTextView; + private SwipeDetector mSwipeDetector; + public NotificationMainView(Context context) { this(context, null, 0); } @@ -78,6 +82,10 @@ public class NotificationMainView extends FrameLayout implements SwipeHelper.Cal applyNotificationInfo(mainNotification, iconView, false); } + public void setSwipeDetector(SwipeDetector swipeDetector) { + mSwipeDetector = swipeDetector; + } + /** * Sets the content of this view, animating it after a new icon shifts up if necessary. */ @@ -113,29 +121,11 @@ public class NotificationMainView extends FrameLayout implements SwipeHelper.Cal } - // SwipeHelper.Callback's - - @Override - public View getChildAtPosition(MotionEvent ev) { - return this; - } - - @Override - public boolean canChildBeDismissed(View v) { + public boolean canChildBeDismissed() { return mNotificationInfo != null && mNotificationInfo.dismissable; } - @Override - public boolean isAntiFalsingNeeded() { - return false; - } - - @Override - public void onBeginDrag(View v) { - } - - @Override - public void onChildDismissed(View v) { + public void onChildDismissed() { Launcher launcher = Launcher.getLauncher(getContext()); launcher.getPopupDataProvider().cancelNotification( mNotificationInfo.notificationKey); @@ -145,22 +135,55 @@ public class NotificationMainView extends FrameLayout implements SwipeHelper.Cal LauncherLogProto.ItemType.NOTIFICATION); } + // SwipeDetector.Listener's @Override - public void onDragCancelled(View v) { - } + public void onDragStart(boolean start) { } - @Override - public void onChildSnappedBack(View animView, float targetLeft) { - } @Override - public boolean updateSwipeProgress(View animView, boolean dismissable, float swipeProgress) { - // Don't fade out. + public boolean onDrag(float displacement, float velocity) { + setTranslationX(canChildBeDismissed() + ? displacement : OverScroll.dampedScroll(displacement, getWidth())); + animate().cancel(); return true; } @Override - public float getFalsingThresholdFactor() { - return 1; + public void onDragEnd(float velocity, boolean fling) { + final boolean willExit; + final float endTranslation; + + if (!canChildBeDismissed()) { + willExit = false; + endTranslation = 0; + } else if (fling) { + willExit = true; + endTranslation = velocity < 0 ? - getWidth() : getWidth(); + } else if (Math.abs(getTranslationX()) > getWidth() / 2) { + willExit = true; + endTranslation = (getTranslationX() < 0 ? -getWidth() : getWidth()); + } else { + willExit = false; + endTranslation = 0; + } + + SwipeDetector.ScrollInterpolator interpolator = new SwipeDetector.ScrollInterpolator(); + interpolator.setVelocityAtZero(velocity); + + long duration = SwipeDetector.calculateDuration(velocity, + (endTranslation - getTranslationX()) / getWidth()); + animate() + .setDuration(duration) + .setInterpolator(interpolator) + .translationX(endTranslation) + .withEndAction(new Runnable() { + @Override + public void run() { + mSwipeDetector.finishedScrolling(); + if (willExit) { + onChildDismissed(); + } + } + }).start(); } } diff --git a/src/com/android/launcher3/notification/SwipeHelper.java b/src/com/android/launcher3/notification/SwipeHelper.java deleted file mode 100644 index ebbe5fc6a..000000000 --- a/src/com/android/launcher3/notification/SwipeHelper.java +++ /dev/null @@ -1,687 +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.notification; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ObjectAnimator; -import android.animation.ValueAnimator; -import android.animation.ValueAnimator.AnimatorUpdateListener; -import android.content.Context; -import android.graphics.RectF; -import android.os.Handler; -import android.util.ArrayMap; -import android.util.Log; -import android.view.MotionEvent; -import android.view.VelocityTracker; -import android.view.View; -import android.view.ViewConfiguration; -import android.view.accessibility.AccessibilityEvent; -import com.android.launcher3.R; - -/** - * This class was copied from com.android.systemui. - */ -public class SwipeHelper { - private static final String TAG = "SwipeHelper"; - private static final boolean DEBUG_INVALIDATE = false; - private static final boolean SLOW_ANIMATIONS = false; // DEBUG; - private static final boolean CONSTRAIN_SWIPE = true; - private static final boolean FADE_OUT_DURING_SWIPE = true; - private static final boolean DISMISS_IF_SWIPED_FAR_ENOUGH = true; - - public static final int X = 0; - public static final int Y = 1; - - private static final float SWIPE_ESCAPE_VELOCITY = 100f; // dp/sec - private static final int DEFAULT_ESCAPE_ANIMATION_DURATION = 200; // ms - private static final int MAX_ESCAPE_ANIMATION_DURATION = 400; // ms - private static final int MAX_DISMISS_VELOCITY = 4000; // dp/sec - private static final int SNAP_ANIM_LEN = SLOW_ANIMATIONS ? 1000 : 150; // ms - - static final float SWIPE_PROGRESS_FADE_END = 0.5f; // fraction of thumbnail width - // beyond which swipe progress->0 - private float mMinSwipeProgress = 0f; - private float mMaxSwipeProgress = 1f; - - private final FlingAnimationUtils mFlingAnimationUtils; - private float mPagingTouchSlop; - private final Callback mCallback; - private final Handler mHandler; - private final int mSwipeDirection; - private final VelocityTracker mVelocityTracker; - - private float mInitialTouchPos; - private float mPerpendicularInitialTouchPos; - private boolean mDragging; - private boolean mSnappingChild; - private View mCurrView; - private boolean mCanCurrViewBeDimissed; - private float mDensityScale; - private float mTranslation = 0; - - private boolean mLongPressSent; - private LongPressListener mLongPressListener; - private Runnable mWatchLongPress; - private final long mLongPressTimeout; - - final private int[] mTmpPos = new int[2]; - private final int mFalsingThreshold; - private boolean mTouchAboveFalsingThreshold; - private boolean mDisableHwLayers; - - private final ArrayMap<View, Animator> mDismissPendingMap = new ArrayMap<>(); - - public SwipeHelper(int swipeDirection, Callback callback, Context context) { - mCallback = callback; - mHandler = new Handler(); - mSwipeDirection = swipeDirection; - mVelocityTracker = VelocityTracker.obtain(); - mDensityScale = context.getResources().getDisplayMetrics().density; - mPagingTouchSlop = ViewConfiguration.get(context).getScaledPagingTouchSlop(); - - mLongPressTimeout = (long) (ViewConfiguration.getLongPressTimeout() * 1.5f); // extra long-press! - mFalsingThreshold = context.getResources().getDimensionPixelSize( - R.dimen.swipe_helper_falsing_threshold); - mFlingAnimationUtils = new FlingAnimationUtils(context, getMaxEscapeAnimDuration() / 1000f); - } - - public void setLongPressListener(LongPressListener listener) { - mLongPressListener = listener; - } - - public void setDensityScale(float densityScale) { - mDensityScale = densityScale; - } - - public void setPagingTouchSlop(float pagingTouchSlop) { - mPagingTouchSlop = pagingTouchSlop; - } - - public void setDisableHardwareLayers(boolean disableHwLayers) { - mDisableHwLayers = disableHwLayers; - } - - private float getPos(MotionEvent ev) { - return mSwipeDirection == X ? ev.getX() : ev.getY(); - } - - private float getPerpendicularPos(MotionEvent ev) { - return mSwipeDirection == X ? ev.getY() : ev.getX(); - } - - protected float getTranslation(View v) { - return mSwipeDirection == X ? v.getTranslationX() : v.getTranslationY(); - } - - private float getVelocity(VelocityTracker vt) { - return mSwipeDirection == X ? vt.getXVelocity() : - vt.getYVelocity(); - } - - protected ObjectAnimator createTranslationAnimation(View v, float newPos) { - ObjectAnimator anim = ObjectAnimator.ofFloat(v, - mSwipeDirection == X ? View.TRANSLATION_X : View.TRANSLATION_Y, newPos); - return anim; - } - - private float getPerpendicularVelocity(VelocityTracker vt) { - return mSwipeDirection == X ? vt.getYVelocity() : - vt.getXVelocity(); - } - - protected Animator getViewTranslationAnimator(View v, float target, - AnimatorUpdateListener listener) { - ObjectAnimator anim = createTranslationAnimation(v, target); - if (listener != null) { - anim.addUpdateListener(listener); - } - return anim; - } - - protected void setTranslation(View v, float translate) { - if (v == null) { - return; - } - if (mSwipeDirection == X) { - v.setTranslationX(translate); - } else { - v.setTranslationY(translate); - } - } - - protected float getSize(View v) { - return mSwipeDirection == X ? v.getMeasuredWidth() : - v.getMeasuredHeight(); - } - - public void setMinSwipeProgress(float minSwipeProgress) { - mMinSwipeProgress = minSwipeProgress; - } - - public void setMaxSwipeProgress(float maxSwipeProgress) { - mMaxSwipeProgress = maxSwipeProgress; - } - - private float getSwipeProgressForOffset(View view, float translation) { - float viewSize = getSize(view); - float result = Math.abs(translation / viewSize); - return Math.min(Math.max(mMinSwipeProgress, result), mMaxSwipeProgress); - } - - private float getSwipeAlpha(float progress) { - return Math.min(0, Math.max(1, progress / SWIPE_PROGRESS_FADE_END)); - } - - private void updateSwipeProgressFromOffset(View animView, boolean dismissable) { - updateSwipeProgressFromOffset(animView, dismissable, getTranslation(animView)); - } - - private void updateSwipeProgressFromOffset(View animView, boolean dismissable, - float translation) { - float swipeProgress = getSwipeProgressForOffset(animView, translation); - if (!mCallback.updateSwipeProgress(animView, dismissable, swipeProgress)) { - if (FADE_OUT_DURING_SWIPE && dismissable) { - float alpha = swipeProgress; - if (!mDisableHwLayers) { - if (alpha != 0f && alpha != 1f) { - animView.setLayerType(View.LAYER_TYPE_HARDWARE, null); - } else { - animView.setLayerType(View.LAYER_TYPE_NONE, null); - } - } - animView.setAlpha(getSwipeAlpha(swipeProgress)); - } - } - invalidateGlobalRegion(animView); - } - - // invalidate the view's own bounds all the way up the view hierarchy - public static void invalidateGlobalRegion(View view) { - invalidateGlobalRegion( - view, - new RectF(view.getLeft(), view.getTop(), view.getRight(), view.getBottom())); - } - - // invalidate a rectangle relative to the view's coordinate system all the way up the view - // hierarchy - public static void invalidateGlobalRegion(View view, RectF childBounds) { - //childBounds.offset(view.getTranslationX(), view.getTranslationY()); - if (DEBUG_INVALIDATE) - Log.v(TAG, "-------------"); - while (view.getParent() != null && view.getParent() instanceof View) { - view = (View) view.getParent(); - view.getMatrix().mapRect(childBounds); - view.invalidate((int) Math.floor(childBounds.left), - (int) Math.floor(childBounds.top), - (int) Math.ceil(childBounds.right), - (int) Math.ceil(childBounds.bottom)); - if (DEBUG_INVALIDATE) { - Log.v(TAG, "INVALIDATE(" + (int) Math.floor(childBounds.left) - + "," + (int) Math.floor(childBounds.top) - + "," + (int) Math.ceil(childBounds.right) - + "," + (int) Math.ceil(childBounds.bottom)); - } - } - } - - public void removeLongPressCallback() { - if (mWatchLongPress != null) { - mHandler.removeCallbacks(mWatchLongPress); - mWatchLongPress = null; - } - } - - public boolean onInterceptTouchEvent(final MotionEvent ev) { - final int action = ev.getAction(); - - switch (action) { - case MotionEvent.ACTION_DOWN: - mTouchAboveFalsingThreshold = false; - mDragging = false; - mSnappingChild = false; - mLongPressSent = false; - mVelocityTracker.clear(); - mCurrView = mCallback.getChildAtPosition(ev); - - if (mCurrView != null) { - onDownUpdate(mCurrView); - mCanCurrViewBeDimissed = mCallback.canChildBeDismissed(mCurrView); - mVelocityTracker.addMovement(ev); - mInitialTouchPos = getPos(ev); - mPerpendicularInitialTouchPos = getPerpendicularPos(ev); - mTranslation = getTranslation(mCurrView); - if (mLongPressListener != null) { - if (mWatchLongPress == null) { - mWatchLongPress = new Runnable() { - @Override - public void run() { - if (mCurrView != null && !mLongPressSent) { - mLongPressSent = true; - mCurrView.sendAccessibilityEvent( - AccessibilityEvent.TYPE_VIEW_LONG_CLICKED); - mCurrView.getLocationOnScreen(mTmpPos); - final int x = (int) ev.getRawX() - mTmpPos[0]; - final int y = (int) ev.getRawY() - mTmpPos[1]; - mLongPressListener.onLongPress(mCurrView, x, y); - } - } - }; - } - mHandler.postDelayed(mWatchLongPress, mLongPressTimeout); - } - } - break; - - case MotionEvent.ACTION_MOVE: - if (mCurrView != null && !mLongPressSent) { - mVelocityTracker.addMovement(ev); - float pos = getPos(ev); - float perpendicularPos = getPerpendicularPos(ev); - float delta = pos - mInitialTouchPos; - float deltaPerpendicular = perpendicularPos - mPerpendicularInitialTouchPos; - if (Math.abs(delta) > mPagingTouchSlop - && Math.abs(delta) > Math.abs(deltaPerpendicular)) { - mCallback.onBeginDrag(mCurrView); - mDragging = true; - mInitialTouchPos = getPos(ev); - mTranslation = getTranslation(mCurrView); - removeLongPressCallback(); - } - } - break; - - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: - final boolean captured = (mDragging || mLongPressSent); - mDragging = false; - mCurrView = null; - mLongPressSent = false; - removeLongPressCallback(); - if (captured) return true; - break; - } - return mDragging || mLongPressSent; - } - - /** - * @param view The view to be dismissed - * @param velocity The desired pixels/second speed at which the view should move - * @param useAccelerateInterpolator Should an accelerating Interpolator be used - */ - public void dismissChild(final View view, float velocity, boolean useAccelerateInterpolator) { - dismissChild(view, velocity, null /* endAction */, 0 /* delay */, - useAccelerateInterpolator, 0 /* fixedDuration */, false /* isDismissAll */); - } - - /** - * @param animView The view to be dismissed - * @param velocity The desired pixels/second speed at which the view should move - * @param endAction The action to perform at the end - * @param delay The delay after which we should start - * @param useAccelerateInterpolator Should an accelerating Interpolator be used - * @param fixedDuration If not 0, this exact duration will be taken - */ - public void dismissChild(final View animView, float velocity, final Runnable endAction, - long delay, boolean useAccelerateInterpolator, long fixedDuration, - boolean isDismissAll) { - final boolean canBeDismissed = mCallback.canChildBeDismissed(animView); - float newPos; - boolean isLayoutRtl = animView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; - - // if we use the Menu to dismiss an item in landscape, animate up - boolean animateUpForMenu = velocity == 0 && (getTranslation(animView) == 0 || isDismissAll) - && mSwipeDirection == Y; - // if the language is rtl we prefer swiping to the left - boolean animateLeftForRtl = velocity == 0 && (getTranslation(animView) == 0 || isDismissAll) - && isLayoutRtl; - boolean animateLeft = velocity < 0 - || (velocity == 0 && getTranslation(animView) < 0 && !isDismissAll); - - if (animateLeft || animateLeftForRtl || animateUpForMenu) { - newPos = -getSize(animView); - } else { - newPos = getSize(animView); - } - long duration; - if (fixedDuration == 0) { - duration = MAX_ESCAPE_ANIMATION_DURATION; - if (velocity != 0) { - duration = Math.min(duration, - (int) (Math.abs(newPos - getTranslation(animView)) * 1000f / Math - .abs(velocity)) - ); - } else { - duration = DEFAULT_ESCAPE_ANIMATION_DURATION; - } - } else { - duration = fixedDuration; - } - - if (!mDisableHwLayers) { - animView.setLayerType(View.LAYER_TYPE_HARDWARE, null); - } - AnimatorUpdateListener updateListener = new AnimatorUpdateListener() { - public void onAnimationUpdate(ValueAnimator animation) { - onTranslationUpdate(animView, (float) animation.getAnimatedValue(), canBeDismissed); - } - }; - - Animator anim = getViewTranslationAnimator(animView, newPos, updateListener); - if (anim == null) { - return; - } - if (useAccelerateInterpolator) { - anim.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN); - anim.setDuration(duration); - } else { - mFlingAnimationUtils.applyDismissing(anim, getTranslation(animView), - newPos, velocity, getSize(animView)); - } - if (delay > 0) { - anim.setStartDelay(delay); - } - anim.addListener(new AnimatorListenerAdapter() { - private boolean mCancelled; - - public void onAnimationCancel(Animator animation) { - mCancelled = true; - } - - public void onAnimationEnd(Animator animation) { - updateSwipeProgressFromOffset(animView, canBeDismissed); - mDismissPendingMap.remove(animView); - if (!mCancelled) { - mCallback.onChildDismissed(animView); - } - if (endAction != null) { - endAction.run(); - } - if (!mDisableHwLayers) { - animView.setLayerType(View.LAYER_TYPE_NONE, null); - } - } - }); - - prepareDismissAnimation(animView, anim); - mDismissPendingMap.put(animView, anim); - anim.start(); - } - - /** - * Called to update the dismiss animation. - */ - protected void prepareDismissAnimation(View view, Animator anim) { - // Do nothing - } - - public void snapChild(final View animView, final float targetLeft, float velocity) { - final boolean canBeDismissed = mCallback.canChildBeDismissed(animView); - AnimatorUpdateListener updateListener = new AnimatorUpdateListener() { - public void onAnimationUpdate(ValueAnimator animation) { - onTranslationUpdate(animView, (float) animation.getAnimatedValue(), canBeDismissed); - } - }; - - Animator anim = getViewTranslationAnimator(animView, targetLeft, updateListener); - if (anim == null) { - return; - } - int duration = SNAP_ANIM_LEN; - anim.setDuration(duration); - anim.addListener(new AnimatorListenerAdapter() { - public void onAnimationEnd(Animator animator) { - mSnappingChild = false; - updateSwipeProgressFromOffset(animView, canBeDismissed); - mCallback.onChildSnappedBack(animView, targetLeft); - } - }); - prepareSnapBackAnimation(animView, anim); - mSnappingChild = true; - anim.start(); - } - - /** - * Called to update the snap back animation. - */ - protected void prepareSnapBackAnimation(View view, Animator anim) { - // Do nothing - } - - /** - * Called when there's a down event. - */ - public void onDownUpdate(View currView) { - // Do nothing - } - - /** - * Called on a move event. - */ - protected void onMoveUpdate(View view, float totalTranslation, float delta) { - // Do nothing - } - - /** - * Called in {@link AnimatorUpdateListener#onAnimationUpdate(ValueAnimator)} when the current - * view is being animated to dismiss or snap. - */ - public void onTranslationUpdate(View animView, float value, boolean canBeDismissed) { - updateSwipeProgressFromOffset(animView, canBeDismissed, value); - } - - private void snapChildInstantly(final View view) { - final boolean canAnimViewBeDismissed = mCallback.canChildBeDismissed(view); - setTranslation(view, 0); - updateSwipeProgressFromOffset(view, canAnimViewBeDismissed); - } - - /** - * Called when a view is updated to be non-dismissable, if the view was being dismissed before - * the update this will handle snapping it back into place. - * - * @param view the view to snap if necessary. - * @param animate whether to animate the snap or not. - * @param targetLeft the target to snap to. - */ - public void snapChildIfNeeded(final View view, boolean animate, float targetLeft) { - if ((mDragging && mCurrView == view) || mSnappingChild) { - return; - } - boolean needToSnap = false; - Animator dismissPendingAnim = mDismissPendingMap.get(view); - if (dismissPendingAnim != null) { - needToSnap = true; - dismissPendingAnim.cancel(); - } else if (getTranslation(view) != 0) { - needToSnap = true; - } - if (needToSnap) { - if (animate) { - snapChild(view, targetLeft, 0.0f /* velocity */); - } else { - snapChildInstantly(view); - } - } - } - - public boolean onTouchEvent(MotionEvent ev) { - if (mLongPressSent) { - return true; - } - - if (!mDragging) { - if (mCallback.getChildAtPosition(ev) != null) { - - // We are dragging directly over a card, make sure that we also catch the gesture - // even if nobody else wants the touch event. - onInterceptTouchEvent(ev); - return true; - } else { - - // We are not doing anything, make sure the long press callback - // is not still ticking like a bomb waiting to go off. - removeLongPressCallback(); - return false; - } - } - - mVelocityTracker.addMovement(ev); - final int action = ev.getAction(); - switch (action) { - case MotionEvent.ACTION_OUTSIDE: - case MotionEvent.ACTION_MOVE: - if (mCurrView != null) { - float delta = getPos(ev) - mInitialTouchPos; - float absDelta = Math.abs(delta); - if (absDelta >= getFalsingThreshold()) { - mTouchAboveFalsingThreshold = true; - } - // don't let items that can't be dismissed be dragged more than - // maxScrollDistance - if (CONSTRAIN_SWIPE && !mCallback.canChildBeDismissed(mCurrView)) { - float size = getSize(mCurrView); - float maxScrollDistance = 0.25f * size; - if (absDelta >= size) { - delta = delta > 0 ? maxScrollDistance : -maxScrollDistance; - } else { - delta = maxScrollDistance * (float) Math.sin((delta/size)*(Math.PI/2)); - } - } - - setTranslation(mCurrView, mTranslation + delta); - updateSwipeProgressFromOffset(mCurrView, mCanCurrViewBeDimissed); - onMoveUpdate(mCurrView, mTranslation + delta, delta); - } - break; - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: - if (mCurrView == null) { - break; - } - mVelocityTracker.computeCurrentVelocity(1000 /* px/sec */, getMaxVelocity()); - float velocity = getVelocity(mVelocityTracker); - - if (!handleUpEvent(ev, mCurrView, velocity, getTranslation(mCurrView))) { - if (isDismissGesture(ev)) { - // flingadingy - dismissChild(mCurrView, velocity, - !swipedFastEnough() /* useAccelerateInterpolator */); - } else { - // snappity - mCallback.onDragCancelled(mCurrView); - snapChild(mCurrView, 0 /* leftTarget */, velocity); - } - mCurrView = null; - } - mDragging = false; - break; - } - return true; - } - - private int getFalsingThreshold() { - float factor = mCallback.getFalsingThresholdFactor(); - return (int) (mFalsingThreshold * factor); - } - - private float getMaxVelocity() { - return MAX_DISMISS_VELOCITY * mDensityScale; - } - - protected float getEscapeVelocity() { - return getUnscaledEscapeVelocity() * mDensityScale; - } - - protected float getUnscaledEscapeVelocity() { - return SWIPE_ESCAPE_VELOCITY; - } - - protected long getMaxEscapeAnimDuration() { - return MAX_ESCAPE_ANIMATION_DURATION; - } - - protected boolean swipedFarEnough() { - float translation = getTranslation(mCurrView); - return DISMISS_IF_SWIPED_FAR_ENOUGH && Math.abs(translation) > 0.4 * getSize(mCurrView); - } - - protected boolean isDismissGesture(MotionEvent ev) { - boolean falsingDetected = mCallback.isAntiFalsingNeeded() && !mTouchAboveFalsingThreshold; - return !falsingDetected && (swipedFastEnough() || swipedFarEnough()) - && ev.getActionMasked() == MotionEvent.ACTION_UP - && mCallback.canChildBeDismissed(mCurrView); - } - - protected boolean swipedFastEnough() { - float velocity = getVelocity(mVelocityTracker); - float translation = getTranslation(mCurrView); - boolean ret = (Math.abs(velocity) > getEscapeVelocity()) - && (velocity > 0) == (translation > 0); - return ret; - } - - protected boolean handleUpEvent(MotionEvent ev, View animView, float velocity, - float translation) { - return false; - } - - public interface Callback { - View getChildAtPosition(MotionEvent ev); - - boolean canChildBeDismissed(View v); - - boolean isAntiFalsingNeeded(); - - void onBeginDrag(View v); - - void onChildDismissed(View v); - - void onDragCancelled(View v); - - /** - * Called when the child is snapped to a position. - * - * @param animView the view that was snapped. - * @param targetLeft the left position the view was snapped to. - */ - void onChildSnappedBack(View animView, float targetLeft); - - /** - * Updates the swipe progress on a child. - * - * @return if true, prevents the default alpha fading. - */ - boolean updateSwipeProgress(View animView, boolean dismissable, float swipeProgress); - - /** - * @return The factor the falsing threshold should be multiplied with - */ - float getFalsingThresholdFactor(); - } - - /** - * Equivalent to View.OnLongClickListener with coordinates - */ - public interface LongPressListener { - /** - * Equivalent to {@link View.OnLongClickListener#onLongClick(View)} with coordinates - * @return whether the longpress was handled - */ - boolean onLongPress(View v, int x, int y); - } -}
\ No newline at end of file diff --git a/src/com/android/launcher3/qsb/QsbContainerView.java b/src/com/android/launcher3/qsb/QsbContainerView.java index 4dc3c1c0d..d26f9f646 100644 --- a/src/com/android/launcher3/qsb/QsbContainerView.java +++ b/src/com/android/launcher3/qsb/QsbContainerView.java @@ -39,12 +39,14 @@ import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.LauncherAppState; import com.android.launcher3.R; import com.android.launcher3.Utilities; -import com.android.launcher3.compat.AppWidgetManagerCompat; import com.android.launcher3.config.FeatureFlags; /** * A frame layout which contains a QSB. This internally uses fragment to bind the view, which * allows it to contain the logic for {@link Fragment#startActivityForResult(Intent, int)}. + * + * Note: AppWidgetManagerCompat can be disabled using FeatureFlags. In QSB, we should use + * AppWidgetManager directly, so that it keeps working in that case. */ public class QsbContainerView extends FrameLayout { @@ -106,7 +108,7 @@ public class QsbContainerView extends FrameLayout { return QsbWidgetHostView.getDefaultView(container); } - AppWidgetManagerCompat widgetManager = AppWidgetManagerCompat.getInstance(activity); + AppWidgetManager widgetManager = AppWidgetManager.getInstance(activity); InvariantDeviceProfile idp = LauncherAppState.getIDP(activity); Bundle opts = new Bundle(); @@ -129,7 +131,8 @@ public class QsbContainerView extends FrameLayout { } widgetId = mQsbWidgetHost.allocateAppWidgetId(); - isWidgetBound = widgetManager.bindAppWidgetIdIfAllowed(widgetId, mWidgetInfo, opts); + isWidgetBound = widgetManager.bindAppWidgetIdIfAllowed( + widgetId, mWidgetInfo.getProfile(), mWidgetInfo.provider, opts); if (!isWidgetBound) { mQsbWidgetHost.deleteAppWidgetId(widgetId); widgetId = -1; diff --git a/src/com/android/launcher3/touch/OverScroll.java b/src/com/android/launcher3/touch/OverScroll.java new file mode 100644 index 000000000..dc801ec4c --- /dev/null +++ b/src/com/android/launcher3/touch/OverScroll.java @@ -0,0 +1,55 @@ +/* + * 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.touch; + +/** + * Utility methods for overscroll damping and related effect. + */ +public class OverScroll { + + private static final float OVERSCROLL_DAMP_FACTOR = 0.07f; + + /** + * This curve determines how the effect of scrolling over the limits of the page diminishes + * as the user pulls further and further from the bounds + * + * @param f The percentage of how much the user has overscrolled. + * @return A transformed percentage based on the influence curve. + */ + private static float overScrollInfluenceCurve(float f) { + f -= 1.0f; + return f * f * f + 1.0f; + } + + /** + * @param amount The original amount overscrolled. + * @param max The maximum amount that the View can overscroll. + * @return The dampened overscroll amount. + */ + public static int dampedScroll(float amount, int max) { + if (Float.compare(amount, 0) == 0) return 0; + + float f = amount / max; + f = f / (Math.abs(f)) * (overScrollInfluenceCurve(Math.abs(f))); + + // Clamp this factor, f, to -1 < f < 1 + if (Math.abs(f) >= 1) { + f /= Math.abs(f); + } + + return Math.round(OVERSCROLL_DAMP_FACTOR * f * max); + } +} diff --git a/src/com/android/launcher3/allapps/VerticalPullDetector.java b/src/com/android/launcher3/touch/SwipeDetector.java index 13c4f63f0..be4648eef 100644 --- a/src/com/android/launcher3/allapps/VerticalPullDetector.java +++ b/src/com/android/launcher3/touch/SwipeDetector.java @@ -1,32 +1,52 @@ -package com.android.launcher3.allapps; +/* + * 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.touch; +import static android.view.MotionEvent.INVALID_POINTER_ID; import android.content.Context; +import android.graphics.PointF; +import android.support.annotation.NonNull; +import android.support.annotation.VisibleForTesting; import android.util.Log; import android.view.MotionEvent; import android.view.ViewConfiguration; import android.view.animation.Interpolator; /** - * One dimensional scroll gesture detector for all apps container pull up interaction. - * Client (e.g., AllAppsTransitionController) of this class can register a listener. - * <p/> - * Features that this gesture detector can support. + * One dimensional scroll/drag/swipe gesture detector. + * + * Definition of swipe is different from android system in that this detector handles + * 'swipe to dismiss', 'swiping up/down a container' but also keeps scrolling state before + * swipe action happens */ -public class VerticalPullDetector { +public class SwipeDetector { private static final boolean DBG = false; - private static final String TAG = "VerticalPullDetector"; - - private final float mTouchSlop; + private static final String TAG = "SwipeDetector"; private int mScrollConditions; - public static final int DIRECTION_UP = 1 << 0; - public static final int DIRECTION_DOWN = 1 << 1; - public static final int DIRECTION_BOTH = DIRECTION_DOWN | DIRECTION_UP; + public static final int DIRECTION_POSITIVE = 1 << 0; + public static final int DIRECTION_NEGATIVE = 1 << 1; + public static final int DIRECTION_BOTH = DIRECTION_NEGATIVE | DIRECTION_POSITIVE; private static final float ANIMATION_DURATION = 1200; private static final float FAST_FLING_PX_MS = 10; + protected int mActivePointerId = INVALID_POINTER_ID; + /** * The minimum release velocity in pixels per millisecond that triggers fling.. */ @@ -47,6 +67,42 @@ public class VerticalPullDetector { SETTLING // onDragEnd } + public static abstract class Direction { + + abstract float getDisplacement(MotionEvent ev, int pointerIndex, PointF refPoint); + + /** + * Distance in pixels a touch can wander before we think the user is scrolling. + */ + abstract float getActiveTouchSlop(MotionEvent ev, int pointerIndex, PointF downPos); + } + + public static final Direction VERTICAL = new Direction() { + + @Override + float getDisplacement(MotionEvent ev, int pointerIndex, PointF refPoint) { + return ev.getY(pointerIndex) - refPoint.y; + } + + @Override + float getActiveTouchSlop(MotionEvent ev, int pointerIndex, PointF downPos) { + return Math.abs(ev.getX(pointerIndex) - downPos.x); + } + }; + + public static final Direction HORIZONTAL = new Direction() { + + @Override + float getDisplacement(MotionEvent ev, int pointerIndex, PointF refPoint) { + return ev.getX(pointerIndex) - refPoint.x; + } + + @Override + float getActiveTouchSlop(MotionEvent ev, int pointerIndex, PointF downPos) { + return Math.abs(ev.getY(pointerIndex) - downPos.y); + } + }; + //------------------- ScrollState transition diagram ----------------------------------- // // IDLE -> (mDisplacement > mTouchSlop) -> DRAGGING @@ -93,27 +149,24 @@ public class VerticalPullDetector { return mState == ScrollState.DRAGGING; } - private float mDownX; - private float mDownY; + private final PointF mDownPos = new PointF(); + private final PointF mLastPos = new PointF(); + private final Direction mDir; + + private final float mTouchSlop; + + /* Client of this gesture detector can register a callback. */ + private final Listener mListener; - private float mLastY; private long mCurrentMillis; private float mVelocity; private float mLastDisplacement; - private float mDisplacementY; - private float mDisplacementX; + private float mDisplacement; private float mSubtractDisplacement; private boolean mIgnoreSlopWhenSettling; - /* Client of this gesture detector can register a callback. */ - private Listener mListener; - - public void setListener(Listener l) { - mListener = l; - } - public interface Listener { void onDragStart(boolean start); @@ -122,8 +175,15 @@ public class VerticalPullDetector { void onDragEnd(float velocity, boolean fling); } - public VerticalPullDetector(Context context) { - mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); + public SwipeDetector(@NonNull Context context, @NonNull Listener l, @NonNull Direction dir) { + this(ViewConfiguration.get(context).getScaledTouchSlop(), l, dir); + } + + @VisibleForTesting + protected SwipeDetector(float touchSlope, @NonNull Listener l, @NonNull Direction dir) { + mTouchSlop = touchSlope; + mListener = l; + mDir = dir; } public void setDetectableScrollConditions(int scrollDirectionFlags, boolean ignoreSlop) { @@ -131,51 +191,65 @@ public class VerticalPullDetector { mIgnoreSlopWhenSettling = ignoreSlop; } - private boolean shouldScrollStart() { - // reject cases where the slop condition is not met. - if (Math.abs(mDisplacementY) < mTouchSlop) { + private boolean shouldScrollStart(MotionEvent ev, int pointerIndex) { + // reject cases where the angle or slop condition is not met. + if (Math.max(mDir.getActiveTouchSlop(ev, pointerIndex, mDownPos), mTouchSlop) + > Math.abs(mDisplacement)) { return false; } - // reject cases where the angle condition is not met. - float deltaY = Math.abs(mDisplacementY); - float deltaX = Math.max(Math.abs(mDisplacementX), 1); - if (deltaX > deltaY) { - return false; - } // Check if the client is interested in scroll in current direction. - if (((mScrollConditions & DIRECTION_DOWN) > 0 && mDisplacementY > 0) || - ((mScrollConditions & DIRECTION_UP) > 0 && mDisplacementY < 0)) { + if (((mScrollConditions & DIRECTION_NEGATIVE) > 0 && mDisplacement > 0) || + ((mScrollConditions & DIRECTION_POSITIVE) > 0 && mDisplacement < 0)) { return true; } return false; } public boolean onTouchEvent(MotionEvent ev) { - switch (ev.getAction()) { + switch (ev.getActionMasked()) { case MotionEvent.ACTION_DOWN: - mDownX = ev.getX(); - mDownY = ev.getY(); + mActivePointerId = ev.getPointerId(0); + mDownPos.set(ev.getX(), ev.getY()); + mLastPos.set(mDownPos); mLastDisplacement = 0; - mDisplacementY = 0; + mDisplacement = 0; mVelocity = 0; if (mState == ScrollState.SETTLING && mIgnoreSlopWhenSettling) { setState(ScrollState.DRAGGING); } break; + //case MotionEvent.ACTION_POINTER_DOWN: + case MotionEvent.ACTION_POINTER_UP: + int ptrIdx = ev.getActionIndex(); + int ptrId = ev.getPointerId(ptrIdx); + if (ptrId == mActivePointerId) { + final int newPointerIdx = ptrIdx == 0 ? 1 : 0; + mDownPos.set( + ev.getX(newPointerIdx) - (mLastPos.x - mDownPos.x), + ev.getY(newPointerIdx) - (mLastPos.y - mDownPos.y)); + mLastPos.set(ev.getX(newPointerIdx), ev.getY(newPointerIdx)); + mActivePointerId = ev.getPointerId(newPointerIdx); + } + break; case MotionEvent.ACTION_MOVE: - mDisplacementX = ev.getX() - mDownX; - mDisplacementY = ev.getY() - mDownY; - computeVelocity(ev); + int pointerIndex = ev.findPointerIndex(mActivePointerId); + if (pointerIndex == INVALID_POINTER_ID) { + break; + } + mDisplacement = mDir.getDisplacement(ev, pointerIndex, mDownPos); + computeVelocity(mDir.getDisplacement(ev, pointerIndex, mLastPos), + ev.getEventTime()); // handle state and listener calls. - if (mState != ScrollState.DRAGGING && shouldScrollStart()) { + if (mState != ScrollState.DRAGGING && shouldScrollStart(ev, pointerIndex)) { setState(ScrollState.DRAGGING); } if (mState == ScrollState.DRAGGING) { reportDragging(); } + mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex)); break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: @@ -185,12 +259,8 @@ public class VerticalPullDetector { } break; default: - //TODO: add multi finger tracking by tracking active pointer. break; } - // Do house keeping. - mLastDisplacement = mDisplacementY; - mLastY = ev.getY(); return true; } @@ -210,7 +280,7 @@ public class VerticalPullDetector { if (mState == ScrollState.SETTLING && mIgnoreSlopWhenSettling) { mSubtractDisplacement = 0; } - if (mDisplacementY > 0) { + if (mDisplacement > 0) { mSubtractDisplacement = mTouchSlop; } else { mSubtractDisplacement = -mTouchSlop; @@ -218,14 +288,14 @@ public class VerticalPullDetector { } private boolean reportDragging() { - float delta = mDisplacementY - mLastDisplacement; - if (delta != 0) { + if (mDisplacement != mLastDisplacement) { if (DBG) { Log.d(TAG, String.format("onDrag disp=%.1f, velocity=%.1f", - mDisplacementY, mVelocity)); + mDisplacement, mVelocity)); } - return mListener.onDrag(mDisplacementY - mSubtractDisplacement, mVelocity); + mLastDisplacement = mDisplacement; + return mListener.onDrag(mDisplacement - mSubtractDisplacement, mVelocity); } return true; } @@ -233,19 +303,15 @@ public class VerticalPullDetector { private void reportDragEnd() { if (DBG) { Log.d(TAG, String.format("onScrollEnd disp=%.1f, velocity=%.1f", - mDisplacementY, mVelocity)); + mDisplacement, mVelocity)); } mListener.onDragEnd(mVelocity, Math.abs(mVelocity) > RELEASE_VELOCITY_PX_MS); } /** - * Computes the damped velocity using the two motion events and the previous velocity. + * Computes the damped velocity. */ - private float computeVelocity(MotionEvent to) { - return computeVelocity(to.getY() - mLastY, to.getEventTime()); - } - public float computeVelocity(float delta, long currentMillis) { long previousMillis = mCurrentMillis; mCurrentMillis = currentMillis; @@ -275,7 +341,7 @@ public class VerticalPullDetector { return (1.0f - alpha) * from + alpha * to; } - public long calculateDuration(float velocity, float progressNeeded) { + public static long calculateDuration(float velocity, float progressNeeded) { // TODO: make these values constants after tuning. float velocityDivisor = Math.max(2f, Math.abs(0.5f * velocity)); float travelDistance = Math.max(0.2f, progressNeeded); diff --git a/src/com/android/launcher3/widget/WidgetAddFlowHandler.java b/src/com/android/launcher3/widget/WidgetAddFlowHandler.java index 629f30c26..5387be839 100644 --- a/src/com/android/launcher3/widget/WidgetAddFlowHandler.java +++ b/src/com/android/launcher3/widget/WidgetAddFlowHandler.java @@ -15,10 +15,8 @@ */ package com.android.launcher3.widget; -import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProviderInfo; import android.content.Context; -import android.content.Intent; import android.os.Parcel; import android.os.Parcelable; @@ -26,7 +24,6 @@ import com.android.launcher3.ItemInfo; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppWidgetInfo; import com.android.launcher3.LauncherAppWidgetProviderInfo; -import com.android.launcher3.compat.AppWidgetManagerCompat; import com.android.launcher3.util.PendingRequestArgs; /** @@ -56,15 +53,8 @@ public class WidgetAddFlowHandler implements Parcelable { public void startBindFlow(Launcher launcher, int appWidgetId, ItemInfo info, int requestCode) { launcher.setWaitingForResult(PendingRequestArgs.forWidgetInfo(appWidgetId, this, info)); - - Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND); - intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); - intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER, mProviderInfo.provider); - intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER_PROFILE, - mProviderInfo.getProfile()); - // TODO: we need to make sure that this accounts for the options bundle. - // intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, options); - launcher.startActivityForResult(intent, requestCode); + launcher.getAppWidgetHost() + .startBindFlow(launcher, appWidgetId, mProviderInfo, requestCode); } /** @@ -85,9 +75,7 @@ public class WidgetAddFlowHandler implements Parcelable { return false; } launcher.setWaitingForResult(PendingRequestArgs.forWidgetInfo(appWidgetId, this, info)); - - AppWidgetManagerCompat.getInstance(launcher).startConfigActivity( - mProviderInfo, appWidgetId, launcher, launcher.getAppWidgetHost(), requestCode); + launcher.getAppWidgetHost().startConfigActivity(launcher, appWidgetId, requestCode); return true; } diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java index b2fb09157..0b4bf628e 100644 --- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java +++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java @@ -40,7 +40,7 @@ import com.android.launcher3.LauncherAnimUtils; import com.android.launcher3.LauncherAppState; import com.android.launcher3.R; import com.android.launcher3.Utilities; -import com.android.launcher3.allapps.VerticalPullDetector; +import com.android.launcher3.touch.SwipeDetector; import com.android.launcher3.anim.PropertyListBuilder; import com.android.launcher3.dragndrop.DragController; import com.android.launcher3.dragndrop.DragOptions; @@ -58,7 +58,7 @@ import java.util.List; * Bottom sheet for the "Widgets" system shortcut in the long-press popup. */ public class WidgetsBottomSheet extends AbstractFloatingView implements Insettable, TouchController, - VerticalPullDetector.Listener, View.OnClickListener, View.OnLongClickListener, + SwipeDetector.Listener, View.OnClickListener, View.OnLongClickListener, DragController.DragListener { private int mTranslationYOpen; @@ -69,9 +69,9 @@ public class WidgetsBottomSheet extends AbstractFloatingView implements Insettab private ItemInfo mOriginalItemInfo; private ObjectAnimator mOpenCloseAnimator; private Interpolator mFastOutSlowInInterpolator; - private VerticalPullDetector.ScrollInterpolator mScrollInterpolator; + private SwipeDetector.ScrollInterpolator mScrollInterpolator; private Rect mInsets; - private VerticalPullDetector mVerticalPullDetector; + private SwipeDetector mSwipeDetector; private GradientView mGradientBackground; public WidgetsBottomSheet(Context context, AttributeSet attrs) { @@ -85,10 +85,9 @@ public class WidgetsBottomSheet extends AbstractFloatingView implements Insettab mOpenCloseAnimator = LauncherAnimUtils.ofPropertyValuesHolder(this); mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, android.R.interpolator.fast_out_slow_in); - mScrollInterpolator = new VerticalPullDetector.ScrollInterpolator(); + mScrollInterpolator = new SwipeDetector.ScrollInterpolator(); mInsets = new Rect(); - mVerticalPullDetector = new VerticalPullDetector(context); - mVerticalPullDetector.setListener(this); + mSwipeDetector = new SwipeDetector(context, this, SwipeDetector.VERTICAL); mGradientBackground = (GradientView) mLauncher.findViewById(R.id.gradient_bg); } @@ -192,7 +191,7 @@ public class WidgetsBottomSheet extends AbstractFloatingView implements Insettab mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - mVerticalPullDetector.finishedScrolling(); + mSwipeDetector.finishedScrolling(); } }); mOpenCloseAnimator.setInterpolator(mFastOutSlowInInterpolator); @@ -214,13 +213,13 @@ public class WidgetsBottomSheet extends AbstractFloatingView implements Insettab @Override public void onAnimationEnd(Animator animation) { mIsOpen = false; - mVerticalPullDetector.finishedScrolling(); + mSwipeDetector.finishedScrolling(); ((ViewGroup) getParent()).removeView(WidgetsBottomSheet.this); mLauncher.getSystemUiController().updateUiState( SystemUiController.UI_STATE_WIDGET_BOTTOM_SHEET, 0); } }); - mOpenCloseAnimator.setInterpolator(mVerticalPullDetector.isIdleState() + mOpenCloseAnimator.setInterpolator(mSwipeDetector.isIdleState() ? mFastOutSlowInInterpolator : mScrollInterpolator); mOpenCloseAnimator.start(); } else { @@ -259,7 +258,7 @@ public class WidgetsBottomSheet extends AbstractFloatingView implements Insettab getPaddingRight() + rightInset, getPaddingBottom() + bottomInset); } - /* VerticalPullDetector.Listener */ + /* SwipeDetector.Listener */ @Override public void onDragStart(boolean start) { @@ -285,12 +284,12 @@ public class WidgetsBottomSheet extends AbstractFloatingView implements Insettab public void onDragEnd(float velocity, boolean fling) { if ((fling && velocity > 0) || getTranslationY() > (mTranslationYRange) / 2) { mScrollInterpolator.setVelocityAtZero(velocity); - mOpenCloseAnimator.setDuration(mVerticalPullDetector.calculateDuration(velocity, + mOpenCloseAnimator.setDuration(SwipeDetector.calculateDuration(velocity, (mTranslationYClosed - getTranslationY()) / mTranslationYRange)); close(true); } else { mIsOpen = false; - mOpenCloseAnimator.setDuration(mVerticalPullDetector.calculateDuration(velocity, + mOpenCloseAnimator.setDuration(SwipeDetector.calculateDuration(velocity, (getTranslationY() - mTranslationYOpen) / mTranslationYRange)); open(true); } @@ -298,17 +297,17 @@ public class WidgetsBottomSheet extends AbstractFloatingView implements Insettab @Override public boolean onControllerTouchEvent(MotionEvent ev) { - return mVerticalPullDetector.onTouchEvent(ev); + return mSwipeDetector.onTouchEvent(ev); } @Override public boolean onControllerInterceptTouchEvent(MotionEvent ev) { - int directionsToDetectScroll = mVerticalPullDetector.isIdleState() ? - VerticalPullDetector.DIRECTION_DOWN : 0; - mVerticalPullDetector.setDetectableScrollConditions( + int directionsToDetectScroll = mSwipeDetector.isIdleState() ? + SwipeDetector.DIRECTION_NEGATIVE : 0; + mSwipeDetector.setDetectableScrollConditions( directionsToDetectScroll, false); - mVerticalPullDetector.onTouchEvent(ev); - return mVerticalPullDetector.isDraggingOrSettling(); + mSwipeDetector.onTouchEvent(ev); + return mSwipeDetector.isDraggingOrSettling(); } /* DragListener */ |