diff options
author | Sunny Goyal <sunnygoyal@google.com> | 2017-10-10 15:21:15 -0700 |
---|---|---|
committer | Sunny Goyal <sunnygoyal@google.com> | 2017-10-13 18:47:54 -0700 |
commit | f1fbc3fbe78997f141e2770221fe5ab1b1e68014 (patch) | |
tree | 466510cf18141f0a7a841c349096f6dfc23d5e8f /src/com/android/launcher3/widget | |
parent | 10a1bd0e652ec7ea3e3ee861fc0d72261a33a3fd (diff) | |
download | android_packages_apps_Trebuchet-f1fbc3fbe78997f141e2770221fe5ab1b1e68014.tar.gz android_packages_apps_Trebuchet-f1fbc3fbe78997f141e2770221fe5ab1b1e68014.tar.bz2 android_packages_apps_Trebuchet-f1fbc3fbe78997f141e2770221fe5ab1b1e68014.zip |
Converting widget panel into a floating view
> The widget panel is only inflated when needed
> Using the swipe up/down interaction for widgets tray
> Removing additional view wrappers from all-apps
> Widget tray is preserved across activity recreation
> Launcher no longer has WIDGET state, the actual code around
the states will be removed in a follow-up cl
Bug: 67678570
Bug: 67585158
Change-Id: Ia29a7c33ec81e6c53cc24e2906b7022b6f41755b
Diffstat (limited to 'src/com/android/launcher3/widget')
8 files changed, 615 insertions, 510 deletions
diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java new file mode 100644 index 000000000..ee5dd66bd --- /dev/null +++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java @@ -0,0 +1,288 @@ +/* + * 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.widget; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ObjectAnimator; +import android.animation.PropertyValuesHolder; +import android.content.Context; +import android.graphics.Point; +import android.util.AttributeSet; +import android.util.Property; +import android.view.MotionEvent; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.View.OnLongClickListener; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.DecelerateInterpolator; +import android.widget.Toast; + +import com.android.launcher3.AbstractFloatingView; +import com.android.launcher3.DeleteDropTarget; +import com.android.launcher3.DragSource; +import com.android.launcher3.DropTarget.DragObject; +import com.android.launcher3.ItemInfo; +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherAnimUtils; +import com.android.launcher3.R; +import com.android.launcher3.Utilities; +import com.android.launcher3.dragndrop.DragOptions; +import com.android.launcher3.folder.Folder; +import com.android.launcher3.graphics.GradientView; +import com.android.launcher3.touch.SwipeDetector; +import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; +import com.android.launcher3.userevent.nano.LauncherLogProto.Target; +import com.android.launcher3.util.SystemUiController; +import com.android.launcher3.util.Themes; + +/** + * Base class for various widgets popup + */ +abstract class BaseWidgetSheet extends AbstractFloatingView + implements OnClickListener, OnLongClickListener, DragSource, SwipeDetector.Listener { + + + protected static Property<BaseWidgetSheet, Float> TRANSLATION_SHIFT = + new Property<BaseWidgetSheet, Float>(Float.class, "translationShift") { + + @Override + public Float get(BaseWidgetSheet view) { + return view.mTranslationShift; + } + + @Override + public void set(BaseWidgetSheet view, Float value) { + view.setTranslationShift(value); + } + }; + protected static final float TRANSLATION_SHIFT_CLOSED = 1f; + protected static final float TRANSLATION_SHIFT_OPENED = 0f; + + /* Touch handling related member variables. */ + private Toast mWidgetInstructionToast; + + protected final Launcher mLauncher; + protected final SwipeDetector.ScrollInterpolator mScrollInterpolator; + protected final SwipeDetector mSwipeDetector; + protected final ObjectAnimator mOpenCloseAnimator; + + protected View mContent; + protected GradientView mGradientView; + + // range [0, 1], 0=> completely open, 1=> completely closed + protected float mTranslationShift = TRANSLATION_SHIFT_CLOSED; + + protected boolean mNoIntercept; + + public BaseWidgetSheet(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + mLauncher = Launcher.getLauncher(context); + + mScrollInterpolator = new SwipeDetector.ScrollInterpolator(); + mSwipeDetector = new SwipeDetector(context, this, SwipeDetector.VERTICAL); + + mOpenCloseAnimator = LauncherAnimUtils.ofPropertyValuesHolder(this); + mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mSwipeDetector.finishedScrolling(); + } + }); + } + + @Override + public final void onClick(View v) { + // Let the user know that they have to long press to add a widget + if (mWidgetInstructionToast != null) { + mWidgetInstructionToast.cancel(); + } + + CharSequence msg = Utilities.wrapForTts( + getContext().getText(R.string.long_press_widget_to_add), + getContext().getString(R.string.long_accessible_way_to_add)); + mWidgetInstructionToast = Toast.makeText(getContext(), msg, Toast.LENGTH_SHORT); + mWidgetInstructionToast.show(); + } + + @Override + public final boolean onLongClick(View v) { + if (!mLauncher.isDraggingEnabled()) return false; + + if (v instanceof WidgetCell) { + return beginDraggingWidget((WidgetCell) v); + } + return true; + } + + protected void setTranslationShift(float translationShift) { + mTranslationShift = translationShift; + mGradientView.setAlpha(1 - mTranslationShift); + mContent.setTranslationY(mTranslationShift * mContent.getHeight()); + } + + private boolean beginDraggingWidget(WidgetCell v) { + // Get the widget preview as the drag representation + WidgetImageView image = v.getWidgetView(); + + // If the ImageView doesn't have a drawable yet, the widget preview hasn't been loaded and + // we abort the drag. + if (image.getBitmap() == null) { + return false; + } + + int[] loc = new int[2]; + mLauncher.getDragLayer().getLocationInDragLayer(image, loc); + + new PendingItemDragHelper(v).startDrag( + image.getBitmapBounds(), image.getBitmap().getWidth(), image.getWidth(), + new Point(loc[0], loc[1]), this, new DragOptions()); + close(true); + return true; + } + + // + // Drag related handling methods that implement {@link DragSource} interface. + // + + @Override + public void onDropCompleted(View target, DragObject d, boolean isFlingToDelete, + boolean success) { + if (isFlingToDelete || !success || (target != mLauncher.getWorkspace() && + !(target instanceof DeleteDropTarget) && !(target instanceof Folder))) { + // Exit spring loaded mode if we have not successfully dropped or have not handled the + // drop in Workspace + mLauncher.exitSpringLoadedDragModeDelayed(true, + Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null); + } + mLauncher.unlockScreenOrientation(false); + + if (!success) { + d.deferDragViewCleanupPostAnimation = false; + } + } + + + @Override + public boolean onControllerInterceptTouchEvent(MotionEvent ev) { + if (ev.getAction() == MotionEvent.ACTION_UP && !mNoIntercept) { + // If we got ACTION_UP without ever returning true on intercept, + // the user never started dragging the bottom sheet. + if (!mLauncher.getDragLayer().isEventOverView(mContent, ev)) { + close(true); + return false; + } + } + + if (mNoIntercept) { + return false; + } + + int directionsToDetectScroll = mSwipeDetector.isIdleState() ? + SwipeDetector.DIRECTION_NEGATIVE : 0; + mSwipeDetector.setDetectableScrollConditions( + directionsToDetectScroll, false); + mSwipeDetector.onTouchEvent(ev); + return mSwipeDetector.isDraggingOrSettling(); + } + + @Override + public boolean onControllerTouchEvent(MotionEvent ev) { + return mSwipeDetector.onTouchEvent(ev); + } + + /* SwipeDetector.Listener */ + + @Override + public void onDragStart(boolean start) { } + + @Override + public boolean onDrag(float displacement, float velocity) { + float range = mContent.getHeight(); + displacement = Utilities.boundToRange(displacement, 0, range); + setTranslationShift(displacement / range); + return true; + } + + @Override + public void onDragEnd(float velocity, boolean fling) { + if ((fling && velocity > 0) || mTranslationShift > 0.5f) { + mScrollInterpolator.setVelocityAtZero(velocity); + mOpenCloseAnimator.setDuration(SwipeDetector.calculateDuration( + velocity, TRANSLATION_SHIFT_CLOSED - mTranslationShift)); + close(true); + } else { + mOpenCloseAnimator.setValues(PropertyValuesHolder.ofFloat( + TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED)); + mOpenCloseAnimator.setDuration( + SwipeDetector.calculateDuration(velocity, mTranslationShift)) + .setInterpolator(new DecelerateInterpolator()); + mOpenCloseAnimator.start(); + } + } + + protected void handleClose(boolean animate, long defaultDuration) { + if (!mIsOpen || mOpenCloseAnimator.isRunning()) { + return; + } + if (animate) { + mOpenCloseAnimator.setValues( + PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_CLOSED)); + mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + onCloseComplete(); + } + }); + if (mSwipeDetector.isIdleState()) { + mOpenCloseAnimator + .setDuration(defaultDuration) + .setInterpolator(new AccelerateInterpolator()); + } else { + mOpenCloseAnimator.setInterpolator(mScrollInterpolator); + } + mOpenCloseAnimator.start(); + } else { + setTranslationShift(TRANSLATION_SHIFT_CLOSED); + onCloseComplete(); + } + } + + protected void onCloseComplete() { + mIsOpen = false; + mLauncher.getDragLayer().removeView(this); + mLauncher.getSystemUiController().updateUiState( + SystemUiController.UI_STATE_WIDGET_BOTTOM_SHEET, 0); + } + + protected void setupNavBarColor() { + boolean isSheetDark = Themes.getAttrBoolean(mLauncher, R.attr.isMainColorDark); + mLauncher.getSystemUiController().updateUiState( + SystemUiController.UI_STATE_WIDGET_BOTTOM_SHEET, + isSheetDark ? SystemUiController.FLAG_DARK_NAV : SystemUiController.FLAG_LIGHT_NAV); + } + + @Override + public void fillInLogContainerData(View v, ItemInfo info, Target target, Target targetParent) { + targetParent.containerType = ContainerType.WIDGETS; + } + + @Override + public final void logActionCommand(int command) { + // TODO: be more specific + mLauncher.getUserEventDispatcher().logActionCommand(command, ContainerType.WIDGETS); + } +} diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java index 40dbd523c..2ba55ab97 100644 --- a/src/com/android/launcher3/widget/WidgetCell.java +++ b/src/com/android/launcher3/widget/WidgetCell.java @@ -75,6 +75,9 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { protected CancellationSignal mActiveRequest; private boolean mAnimatePreview = true; + private boolean mApplyBitmapDeferred = false; + private Bitmap mDeferredBitmap; + protected final BaseActivity mActivity; public WidgetCell(Context context) { @@ -150,15 +153,31 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { return mWidgetImage; } + /** + * Sets if applying bitmap preview should be deferred. The UI will still load the bitmap, but + * will not cause invalidate, so that when deferring is disabled later, all the bitmaps are + * ready. + * This prevents invalidates while the animation is running. + */ + public void setApplyBitmapDeferred(boolean isDeferred) { + if (mApplyBitmapDeferred != isDeferred) { + mApplyBitmapDeferred = isDeferred; + if (!mApplyBitmapDeferred && mDeferredBitmap != null) { + applyPreview(mDeferredBitmap); + mDeferredBitmap = null; + } + } + } + public void setAnimatePreview(boolean shouldAnimate) { mAnimatePreview = shouldAnimate; } public void applyPreview(Bitmap bitmap) { - applyPreview(bitmap, true); - } - - public void applyPreview(Bitmap bitmap, boolean animate) { + if (mApplyBitmapDeferred) { + mDeferredBitmap = bitmap; + return; + } if (bitmap != null) { mWidgetImage.setBitmap(bitmap, DrawableFactory.get(getContext()).getBadgeForUser(mItem.user, getContext())); @@ -173,15 +192,11 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { } public void ensurePreview() { - ensurePreview(true); - } - - public void ensurePreview(boolean animate) { if (mActiveRequest != null) { return; } mActiveRequest = mWidgetPreviewLoader.getPreview( - mItem, mPresetPreviewSize, mPresetPreviewSize, this, animate); + mItem, mPresetPreviewSize, mPresetPreviewSize, this); } @Override diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java index 7aa50a445..201bd1c9c 100644 --- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java +++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java @@ -16,62 +16,38 @@ package com.android.launcher3.widget; -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ObjectAnimator; +import android.animation.PropertyValuesHolder; import android.content.Context; import android.graphics.Rect; import android.util.AttributeSet; import android.view.Gravity; import android.view.LayoutInflater; -import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; import android.widget.TextView; -import com.android.launcher3.AbstractFloatingView; -import com.android.launcher3.DropTarget; import com.android.launcher3.Insettable; import com.android.launcher3.ItemInfo; -import com.android.launcher3.Launcher; -import com.android.launcher3.LauncherAnimUtils; import com.android.launcher3.LauncherAppState; import com.android.launcher3.R; import com.android.launcher3.Utilities; -import com.android.launcher3.anim.PropertyListBuilder; -import com.android.launcher3.dragndrop.DragController; -import com.android.launcher3.dragndrop.DragOptions; import com.android.launcher3.graphics.GradientView; import com.android.launcher3.model.WidgetItem; -import com.android.launcher3.touch.SwipeDetector; -import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; import com.android.launcher3.util.PackageUserKey; -import com.android.launcher3.util.SystemUiController; -import com.android.launcher3.util.Themes; import java.util.List; /** * Bottom sheet for the "Widgets" system shortcut in the long-press popup. */ -public class WidgetsBottomSheet extends AbstractFloatingView implements Insettable, - SwipeDetector.Listener, View.OnClickListener, View.OnLongClickListener, - DragController.DragListener { +public class WidgetsBottomSheet extends BaseWidgetSheet implements Insettable { - private int mTranslationYOpen; - private int mTranslationYClosed; - private float mTranslationYRange; - - private Launcher mLauncher; + private static final int DEFAULT_CLOSE_DURATION = 200; private ItemInfo mOriginalItemInfo; - private ObjectAnimator mOpenCloseAnimator; private Interpolator mFastOutSlowInInterpolator; - private SwipeDetector.ScrollInterpolator mScrollInterpolator; private Rect mInsets; - private SwipeDetector mSwipeDetector; - private GradientView mGradientBackground; public WidgetsBottomSheet(Context context, AttributeSet attrs) { this(context, attrs, 0); @@ -80,23 +56,20 @@ public class WidgetsBottomSheet extends AbstractFloatingView implements Insettab public WidgetsBottomSheet(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); setWillNotDraw(false); - mLauncher = Launcher.getLauncher(context); - mOpenCloseAnimator = LauncherAnimUtils.ofPropertyValuesHolder(this); mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, android.R.interpolator.fast_out_slow_in); - mScrollInterpolator = new SwipeDetector.ScrollInterpolator(); mInsets = new Rect(); - mSwipeDetector = new SwipeDetector(context, this, SwipeDetector.VERTICAL); - mGradientBackground = (GradientView) mLauncher.getLayoutInflater().inflate( + + mGradientView = (GradientView) mLauncher.getLayoutInflater().inflate( R.layout.gradient_bg, mLauncher.getDragLayer(), false); + mGradientView.setProgress(1, false); + mContent = this; } @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - mTranslationYOpen = 0; - mTranslationYClosed = getMeasuredHeight(); - mTranslationYRange = mTranslationYClosed - mTranslationYOpen; + protected void onLayout(boolean changed, int l, int t, int r, int b) { + super.onLayout(changed, l, t, r, b); + setTranslationShift(mTranslationShift); } public void populateAndShow(ItemInfo itemInfo) { @@ -106,21 +79,21 @@ public class WidgetsBottomSheet extends AbstractFloatingView implements Insettab onWidgetsBound(); - mLauncher.getDragLayer().addView(mGradientBackground); + mLauncher.getDragLayer().addView(mGradientView); mLauncher.getDragLayer().addView(this); - measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); - setTranslationY(mTranslationYClosed); mIsOpen = false; open(true); } @Override protected void onWidgetsBound() { - List<WidgetItem> widgets = mLauncher.getWidgetsForPackageUser(new PackageUserKey( - mOriginalItemInfo.getTargetComponent().getPackageName(), mOriginalItemInfo.user)); + List<WidgetItem> widgets = mLauncher.getPopupDataProvider().getWidgetsForPackageUser( + new PackageUserKey( + mOriginalItemInfo.getTargetComponent().getPackageName(), + mOriginalItemInfo.user)); - ViewGroup widgetRow = (ViewGroup) findViewById(R.id.widgets); - ViewGroup widgetCells = (ViewGroup) widgetRow.findViewById(R.id.widgets_cell_list); + ViewGroup widgetRow = findViewById(R.id.widgets); + ViewGroup widgetCells = widgetRow.findViewById(R.id.widgets_cell_list); widgetCells.removeAllViews(); @@ -166,72 +139,31 @@ public class WidgetsBottomSheet extends AbstractFloatingView implements Insettab return widget; } - @Override - public void onClick(View view) { - mLauncher.getWidgetsView().handleClick(); - } - - @Override - public boolean onLongClick(View view) { - mLauncher.getDragController().addDragListener(this); - return mLauncher.getWidgetsView().handleLongClick(view); - } - private void open(boolean animate) { if (mIsOpen || mOpenCloseAnimator.isRunning()) { return; } mIsOpen = true; - boolean isSheetDark = Themes.getAttrBoolean(mLauncher, R.attr.isMainColorDark); - mLauncher.getSystemUiController().updateUiState( - SystemUiController.UI_STATE_WIDGET_BOTTOM_SHEET, - isSheetDark ? SystemUiController.FLAG_DARK_NAV : SystemUiController.FLAG_LIGHT_NAV); + setupNavBarColor(); if (animate) { - mOpenCloseAnimator.setValues(new PropertyListBuilder() - .translationY(mTranslationYOpen).build()); - mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mSwipeDetector.finishedScrolling(); - } - }); + mOpenCloseAnimator.setValues( + PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED)); mOpenCloseAnimator.setInterpolator(mFastOutSlowInInterpolator); mOpenCloseAnimator.start(); } else { - setTranslationY(mTranslationYOpen); + setTranslationShift(TRANSLATION_SHIFT_OPENED); } } @Override protected void handleClose(boolean animate) { - if (!mIsOpen || mOpenCloseAnimator.isRunning()) { - return; - } - if (animate) { - mOpenCloseAnimator.setValues(new PropertyListBuilder() - .translationY(mTranslationYClosed).build()); - mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mSwipeDetector.finishedScrolling(); - onCloseComplete(); - } - }); - mOpenCloseAnimator.setInterpolator(mSwipeDetector.isIdleState() - ? mFastOutSlowInInterpolator : mScrollInterpolator); - mOpenCloseAnimator.start(); - } else { - setTranslationY(mTranslationYClosed); - onCloseComplete(); - } + handleClose(animate, DEFAULT_CLOSE_DURATION); } - private void onCloseComplete() { - mIsOpen = false; - mLauncher.getDragLayer().removeView(mGradientBackground); - mLauncher.getDragLayer().removeView(WidgetsBottomSheet.this); - mLauncher.getSystemUiController().updateUiState( - SystemUiController.UI_STATE_WIDGET_BOTTOM_SHEET, 0); + @Override + protected void onCloseComplete() { + super.onCloseComplete(); + mLauncher.getDragLayer().removeView(mGradientView); } @Override @@ -249,83 +181,4 @@ public class WidgetsBottomSheet extends AbstractFloatingView implements Insettab setPadding(getPaddingLeft() + leftInset, getPaddingTop(), getPaddingRight() + rightInset, getPaddingBottom() + bottomInset); } - - /* SwipeDetector.Listener */ - - @Override - public void onDragStart(boolean start) { - } - - @Override - public boolean onDrag(float displacement, float velocity) { - setTranslationY(Utilities.boundToRange(displacement, mTranslationYOpen, - mTranslationYClosed)); - return true; - } - - @Override - public void setTranslationY(float translationY) { - super.setTranslationY(translationY); - if (mGradientBackground == null) return; - float p = (mTranslationYClosed - translationY) / mTranslationYRange; - boolean showScrim = p <= 0; - mGradientBackground.setProgress(p, showScrim); - } - - @Override - public void onDragEnd(float velocity, boolean fling) { - if ((fling && velocity > 0) || getTranslationY() > (mTranslationYRange) / 2) { - mScrollInterpolator.setVelocityAtZero(velocity); - mOpenCloseAnimator.setDuration(SwipeDetector.calculateDuration(velocity, - (mTranslationYClosed - getTranslationY()) / mTranslationYRange)); - close(true); - } else { - mIsOpen = false; - mOpenCloseAnimator.setDuration(SwipeDetector.calculateDuration(velocity, - (getTranslationY() - mTranslationYOpen) / mTranslationYRange)); - open(true); - } - } - - @Override - public void logActionCommand(int command) { - // TODO: be more specific - mLauncher.getUserEventDispatcher().logActionCommand(command, ContainerType.WIDGETS); - } - - @Override - public boolean onControllerTouchEvent(MotionEvent ev) { - return mSwipeDetector.onTouchEvent(ev); - } - - @Override - public boolean onControllerInterceptTouchEvent(MotionEvent ev) { - if (ev.getAction() == MotionEvent.ACTION_UP) { - // If we got ACTION_UP without ever returning true on intercept, - // the user never started dragging the bottom sheet. - if (!mLauncher.getDragLayer().isEventOverView(this, ev)) { - close(true); - return false; - } - } - - int directionsToDetectScroll = mSwipeDetector.isIdleState() ? - SwipeDetector.DIRECTION_NEGATIVE : 0; - mSwipeDetector.setDetectableScrollConditions( - directionsToDetectScroll, false); - mSwipeDetector.onTouchEvent(ev); - return mSwipeDetector.isDraggingOrSettling(); - } - - /* DragListener */ - - @Override - public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) { - // A widget or custom shortcut was dragged. - close(true); - } - - @Override - public void onDragEnd() { - } } diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java deleted file mode 100644 index 39dd0d498..000000000 --- a/src/com/android/launcher3/widget/WidgetsContainerView.java +++ /dev/null @@ -1,243 +0,0 @@ -/* - * Copyright (C) 2015 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.widget; - -import android.content.Context; -import android.content.pm.LauncherApps; -import android.graphics.Point; -import android.support.v7.widget.LinearLayoutManager; -import android.util.AttributeSet; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Toast; - -import com.android.launcher3.BaseContainerView; -import com.android.launcher3.DeleteDropTarget; -import com.android.launcher3.DragSource; -import com.android.launcher3.DropTarget.DragObject; -import com.android.launcher3.ItemInfo; -import com.android.launcher3.Launcher; -import com.android.launcher3.LauncherAppState; -import com.android.launcher3.R; -import com.android.launcher3.Utilities; -import com.android.launcher3.compat.AlphabeticIndexCompat; -import com.android.launcher3.dragndrop.DragOptions; -import com.android.launcher3.folder.Folder; -import com.android.launcher3.model.PackageItemInfo; -import com.android.launcher3.model.WidgetItem; -import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; -import com.android.launcher3.userevent.nano.LauncherLogProto.Target; -import com.android.launcher3.util.MultiHashMap; -import com.android.launcher3.util.PackageUserKey; -import com.android.launcher3.util.Thunk; - -import java.util.List; - -/** - * The widgets list view container. - */ -public class WidgetsContainerView extends BaseContainerView - implements View.OnLongClickListener, View.OnClickListener, DragSource { - private static final String TAG = "WidgetsContainerView"; - private static final boolean LOGD = false; - - /* Global instances that are used inside this container. */ - @Thunk Launcher mLauncher; - - /* Recycler view related member variables */ - private WidgetsRecyclerView mRecyclerView; - private WidgetsListAdapter mAdapter; - - /* Touch handling related member variables. */ - private Toast mWidgetInstructionToast; - - public WidgetsContainerView(Context context) { - this(context, null); - } - - public WidgetsContainerView(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public WidgetsContainerView(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - mLauncher = Launcher.getLauncher(context); - LauncherAppState apps = LauncherAppState.getInstance(context); - mAdapter = new WidgetsListAdapter(context, LayoutInflater.from(context), - apps.getWidgetCache(), new AlphabeticIndexCompat(context), this, this, - new WidgetsDiffReporter(apps.getIconCache())); - mAdapter.setNotifyListener(); - if (LOGD) { - Log.d(TAG, "WidgetsContainerView constructor"); - } - } - - @Override - public View getTouchDelegateTargetView() { - return mRecyclerView; - } - - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - mRecyclerView = (WidgetsRecyclerView) getContentView().findViewById(R.id.widgets_list_view); - mRecyclerView.setAdapter(mAdapter); - mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext())); - } - - // - // Returns views used for launcher transitions. - // - - public void scrollToTop() { - mRecyclerView.scrollToPosition(0); - } - - // - // Touch related handling. - // - - @Override - public void onClick(View v) { - // When we have exited widget tray or are in transition, disregard clicks - if (!mLauncher.isWidgetsViewVisible() - || mLauncher.getWorkspace().isSwitchingState() - || !(v instanceof WidgetCell)) return; - - handleClick(); - } - - public void handleClick() { - // Let the user know that they have to long press to add a widget - if (mWidgetInstructionToast != null) { - mWidgetInstructionToast.cancel(); - } - - CharSequence msg = Utilities.wrapForTts( - getContext().getText(R.string.long_press_widget_to_add), - getContext().getString(R.string.long_accessible_way_to_add)); - mWidgetInstructionToast = Toast.makeText(getContext(), msg, Toast.LENGTH_SHORT); - mWidgetInstructionToast.show(); - } - - @Override - public boolean onLongClick(View v) { - // When we have exited the widget tray, disregard long clicks - if (!mLauncher.isWidgetsViewVisible()) return false; - return handleLongClick(v); - } - - public boolean handleLongClick(View v) { - if (LOGD) { - Log.d(TAG, String.format("onLongClick [v=%s]", v)); - } - // When we are in transition, disregard long clicks - if (mLauncher.getWorkspace().isSwitchingState()) return false; - // Return if global dragging is not enabled - if (!mLauncher.isDraggingEnabled()) return false; - - return beginDragging(v); - } - - private boolean beginDragging(View v) { - if (v instanceof WidgetCell) { - if (!beginDraggingWidget((WidgetCell) v)) { - return false; - } - } else { - Log.e(TAG, "Unexpected dragging view: " + v); - } - - // We don't enter spring-loaded mode if the drag has been cancelled - if (mLauncher.getDragController().isDragging()) { - // Go into spring loaded mode (must happen before we startDrag()) - mLauncher.enterSpringLoadedDragMode(); - } - - return true; - } - - private boolean beginDraggingWidget(WidgetCell v) { - // Get the widget preview as the drag representation - WidgetImageView image = (WidgetImageView) v.findViewById(R.id.widget_preview); - - // If the ImageView doesn't have a drawable yet, the widget preview hasn't been loaded and - // we abort the drag. - if (image.getBitmap() == null) { - return false; - } - - int[] loc = new int[2]; - mLauncher.getDragLayer().getLocationInDragLayer(image, loc); - - new PendingItemDragHelper(v).startDrag( - image.getBitmapBounds(), image.getBitmap().getWidth(), image.getWidth(), - new Point(loc[0], loc[1]), this, new DragOptions()); - return true; - } - - // - // Drag related handling methods that implement {@link DragSource} interface. - // - - @Override - public void onDropCompleted(View target, DragObject d, boolean isFlingToDelete, - boolean success) { - if (LOGD) { - Log.d(TAG, "onDropCompleted"); - } - if (isFlingToDelete || !success || (target != mLauncher.getWorkspace() && - !(target instanceof DeleteDropTarget) && !(target instanceof Folder))) { - // Exit spring loaded mode if we have not successfully dropped or have not handled the - // drop in Workspace - mLauncher.exitSpringLoadedDragModeDelayed(true, - Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null); - } - mLauncher.unlockScreenOrientation(false); - - if (!success) { - d.deferDragViewCleanupPostAnimation = false; - } - } - - /** - * Initialize the widget data model. - */ - public void setWidgets(MultiHashMap<PackageItemInfo, WidgetItem> model) { - mAdapter.setWidgets(model); - - View loader = getContentView().findViewById(R.id.loader); - if (loader != null) { - ((ViewGroup) getContentView()).removeView(loader); - } - } - - public boolean isEmpty() { - return mAdapter.getItemCount() == 0; - } - - public List<WidgetItem> getWidgetsForPackageUser(PackageUserKey packageUserKey) { - return mAdapter.copyWidgetsForPackageUser(packageUserKey); - } - - @Override - public void fillInLogContainerData(View v, ItemInfo info, Target target, Target targetParent) { - targetParent.containerType = ContainerType.WIDGETS; - } -}
\ No newline at end of file diff --git a/src/com/android/launcher3/widget/WidgetsDiffReporter.java b/src/com/android/launcher3/widget/WidgetsDiffReporter.java index 52deec32b..d67f40365 100644 --- a/src/com/android/launcher3/widget/WidgetsDiffReporter.java +++ b/src/com/android/launcher3/widget/WidgetsDiffReporter.java @@ -16,6 +16,7 @@ package com.android.launcher3.widget; +import android.support.v7.widget.RecyclerView; import android.util.Log; import com.android.launcher3.IconCache; @@ -26,26 +27,18 @@ import java.util.ArrayList; import java.util.Iterator; /** - * Do diff on widget's tray list items and call the {@link NotifyListener} methods accordingly. + * Do diff on widget's tray list items and call the {@link RecyclerView.Adapter} + * methods accordingly. */ public class WidgetsDiffReporter { - private final boolean DEBUG = false; - private final String TAG = "WidgetsDiffReporter"; - private final IconCache mIconCache; - private NotifyListener mListener; + private static final boolean DEBUG = false; + private static final String TAG = "WidgetsDiffReporter"; - public interface NotifyListener { - void notifyDataSetChanged(); - void notifyItemChanged(int index); - void notifyItemInserted(int index); - void notifyItemRemoved(int index); - } + private final IconCache mIconCache; + private final RecyclerView.Adapter mListener; - public WidgetsDiffReporter(IconCache iconCache) { + public WidgetsDiffReporter(IconCache iconCache, RecyclerView.Adapter listener) { mIconCache = iconCache; - } - - public void setListener(NotifyListener listener) { mListener = listener; } @@ -55,9 +48,17 @@ public class WidgetsDiffReporter { Log.d(TAG, "process oldEntries#=" + currentEntries.size() + " newEntries#=" + newEntries.size()); } - if (currentEntries.size() == 0 && newEntries.size() > 0) { - currentEntries.addAll(newEntries); - mListener.notifyDataSetChanged(); + // Early exit if either of the list is empty + if (currentEntries.isEmpty() || newEntries.isEmpty()) { + // Skip if both list are empty. + // On rotation, we open the widget tray with empty. Then try to fetch the list again + // when the animation completes (which still gives empty). And we get the final result + // when the bind actually completes. + if (currentEntries.size() != newEntries.size()) { + currentEntries.clear(); + currentEntries.addAll(newEntries); + mListener.notifyDataSetChanged(); + } return; } ArrayList<WidgetListRowEntry> orgEntries = diff --git a/src/com/android/launcher3/widget/WidgetsFullSheet.java b/src/com/android/launcher3/widget/WidgetsFullSheet.java new file mode 100644 index 000000000..72277a253 --- /dev/null +++ b/src/com/android/launcher3/widget/WidgetsFullSheet.java @@ -0,0 +1,222 @@ +/* + * 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.widget; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.PropertyValuesHolder; +import android.content.Context; +import android.graphics.Rect; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.animation.AnimationUtils; + +import com.android.launcher3.Insettable; +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherAppState; +import com.android.launcher3.LauncherAppWidgetHost.ProviderChangedListener; +import com.android.launcher3.R; + +/** + * Popup for showing the full list of available widgets + */ +public class WidgetsFullSheet extends BaseWidgetSheet + implements Insettable, ProviderChangedListener { + + private static final long DEFAULT_OPEN_DURATION = 267; + private static final long FADE_IN_DURATION = 150; + private static final float VERTICAL_START_POSITION = 0.3f; + + private static final Rect sTempRect = new Rect(); + + private final Rect mInsets = new Rect(); + + private final WidgetsListAdapter mAdapter; + + private View mNavBarScrim; + private WidgetsRecyclerView mRecyclerView; + + public WidgetsFullSheet(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + LauncherAppState apps = LauncherAppState.getInstance(context); + mAdapter = new WidgetsListAdapter(context, + LayoutInflater.from(context), apps.getWidgetCache(), apps.getIconCache(), + this, this); + } + + public WidgetsFullSheet(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mContent = findViewById(R.id.container); + mNavBarScrim = findViewById(R.id.nav_bar_bg); + + mRecyclerView = findViewById(R.id.widgets_list_view); + mRecyclerView.setAdapter(mAdapter); + mAdapter.setApplyBitmapDeferred(true, mRecyclerView); + + mGradientView = findViewById(R.id.gradient_bg); + mGradientView.setProgress(1, false); + + onWidgetsBound(); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + mLauncher.getAppWidgetHost().addProviderChangeListener(this); + notifyWidgetProvidersChanged(); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + mLauncher.getAppWidgetHost().removeProviderChangeListener(this); + } + + @Override + public void setInsets(Rect insets) { + mInsets.set(insets); + + mNavBarScrim.getLayoutParams().height = insets.bottom; + mRecyclerView.setPadding( + mRecyclerView.getPaddingLeft(), mRecyclerView.getPaddingTop(), + mRecyclerView.getPaddingRight(), insets.bottom); + if (insets.bottom > 0) { + setupNavBarColor(); + } + requestLayout(); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int widthUsed; + if (mInsets.bottom > 0) { + // If we have bottom insets, we do not show the scrim as it would overlap + // with the navbar scrim + mGradientView.setVisibility(View.INVISIBLE); + widthUsed = 0; + } else { + mLauncher.getDeviceProfile().getWorkspacePadding(sTempRect); + widthUsed = Math.max(sTempRect.left + sTempRect.right, + 2 * (mInsets.left + mInsets.right)); + } + + int heightUsed = mInsets.top + mLauncher.getDeviceProfile().edgeMarginPx; + measureChildWithMargins(mContent, widthMeasureSpec, + widthUsed, heightMeasureSpec, heightUsed); + measureChild(mGradientView, widthMeasureSpec, heightMeasureSpec); + setMeasuredDimension(mGradientView.getMeasuredWidth(), mGradientView.getMeasuredHeight()); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + int width = r - l; + int height = b - t; + mGradientView.layout(0, 0, width, height); + + // Content is laid out as center bottom aligned + int contentWidth = mContent.getMeasuredWidth(); + int contentLeft = (width - contentWidth) / 2; + mContent.layout(contentLeft, height - mContent.getMeasuredHeight(), + contentLeft + contentWidth, height); + + setTranslationShift(mTranslationShift); + } + + @Override + public void notifyWidgetProvidersChanged() { + mLauncher.refreshAndBindWidgetsForPackageUser(null); + } + + @Override + protected void onWidgetsBound() { + mAdapter.setWidgets(mLauncher.getPopupDataProvider().getAllWidgets()); + } + + private void open(boolean animate) { + if (mIsOpen) { + return; + } + mIsOpen = true; + if (animate) { + if (mLauncher.getDragLayer().getInsets().bottom > 0) { + mContent.setAlpha(0); + setTranslationShift(VERTICAL_START_POSITION); + } + mOpenCloseAnimator.setValues( + PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED)); + mOpenCloseAnimator + .setDuration(DEFAULT_OPEN_DURATION) + .setInterpolator(AnimationUtils.loadInterpolator( + getContext(), android.R.interpolator.linear_out_slow_in)); + mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mRecyclerView.setLayoutFrozen(false); + mAdapter.setApplyBitmapDeferred(false, mRecyclerView); + mOpenCloseAnimator.removeListener(this); + } + }); + post(new Runnable() { + @Override + public void run() { + mRecyclerView.setLayoutFrozen(true); + mOpenCloseAnimator.start(); + mContent.animate().alpha(1).setDuration(FADE_IN_DURATION); + } + }); + } else { + setTranslationShift(TRANSLATION_SHIFT_OPENED); + mAdapter.setApplyBitmapDeferred(false, mRecyclerView); + } + } + + @Override + protected void handleClose(boolean animate) { + handleClose(animate, DEFAULT_OPEN_DURATION); + } + + @Override + protected boolean isOfType(int type) { + return (type & TYPE_WIDGETS_FULL_SHEET) != 0; + } + + @Override + public boolean onControllerInterceptTouchEvent(MotionEvent ev) { + // Disable swipe down when recycler view is scrolling + if (ev.getAction() == MotionEvent.ACTION_DOWN) { + mNoIntercept = false; + if (mLauncher.getDragLayer().isEventOverView(mContent, ev)) { + mNoIntercept = !mRecyclerView.shouldContainerScroll(ev, mLauncher.getDragLayer()); + } + } + return super.onControllerInterceptTouchEvent(ev); + } + + public static WidgetsFullSheet show(Launcher launcher, boolean animate) { + WidgetsFullSheet sheet = (WidgetsFullSheet) launcher.getLayoutInflater() + .inflate(R.layout.widgets_full_sheet, launcher.getDragLayer(), false); + launcher.getDragLayer().addView(sheet); + sheet.open(animate); + return sheet; + } +} diff --git a/src/com/android/launcher3/widget/WidgetsListAdapter.java b/src/com/android/launcher3/widget/WidgetsListAdapter.java index 6b1800c67..0147ea427 100644 --- a/src/com/android/launcher3/widget/WidgetsListAdapter.java +++ b/src/com/android/launcher3/widget/WidgetsListAdapter.java @@ -25,13 +25,11 @@ import android.view.View.OnClickListener; import android.view.View.OnLongClickListener; import android.view.ViewGroup; +import com.android.launcher3.IconCache; import com.android.launcher3.R; import com.android.launcher3.WidgetPreviewLoader; -import com.android.launcher3.compat.AlphabeticIndexCompat; -import com.android.launcher3.model.PackageItemInfo; import com.android.launcher3.model.WidgetItem; import com.android.launcher3.util.LabelComparator; -import com.android.launcher3.util.MultiHashMap; import com.android.launcher3.util.PackageUserKey; import java.util.ArrayList; @@ -39,7 +37,6 @@ import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; -import java.util.Map; /** * List view adapter for the widget tray. @@ -56,7 +53,6 @@ public class WidgetsListAdapter extends Adapter<WidgetsRowViewHolder> { private final WidgetPreviewLoader mWidgetPreviewLoader; private final LayoutInflater mLayoutInflater; - private final AlphabeticIndexCompat mIndexer; private final OnClickListener mIconClickListener; private final OnLongClickListener mIconLongClickListener; @@ -64,56 +60,43 @@ public class WidgetsListAdapter extends Adapter<WidgetsRowViewHolder> { private ArrayList<WidgetListRowEntry> mEntries = new ArrayList<>(); private final WidgetsDiffReporter mDiffReporter; + private boolean mApplyBitmapDeferred; + public WidgetsListAdapter(Context context, LayoutInflater layoutInflater, - WidgetPreviewLoader widgetPreviewLoader, AlphabeticIndexCompat indexCompat, - OnClickListener iconClickListener, OnLongClickListener iconLongClickListener, - WidgetsDiffReporter diffReporter) { + WidgetPreviewLoader widgetPreviewLoader, IconCache iconCache, + OnClickListener iconClickListener, OnLongClickListener iconLongClickListener) { mLayoutInflater = layoutInflater; mWidgetPreviewLoader = widgetPreviewLoader; - mIndexer = indexCompat; mIconClickListener = iconClickListener; mIconLongClickListener = iconLongClickListener; mIndent = context.getResources().getDimensionPixelSize(R.dimen.widget_section_indent); - mDiffReporter = diffReporter; + mDiffReporter = new WidgetsDiffReporter(iconCache, this); } - public void setNotifyListener() { - mDiffReporter.setListener(new WidgetsDiffReporter.NotifyListener() { - @Override - public void notifyDataSetChanged() { - WidgetsListAdapter.this.notifyDataSetChanged(); - } - - @Override - public void notifyItemChanged(int index) { - WidgetsListAdapter.this.notifyItemChanged(index); - } - - @Override - public void notifyItemInserted(int index) { - WidgetsListAdapter.this.notifyItemInserted(index); - } - - @Override - public void notifyItemRemoved(int index) { - WidgetsListAdapter.this.notifyItemRemoved(index); + /** + * Defers applying bitmap on all the {@link WidgetCell} in the {@param rv} + * + * @see WidgetCell#setApplyBitmapDeferred(boolean) + */ + public void setApplyBitmapDeferred(boolean isDeferred, RecyclerView rv) { + mApplyBitmapDeferred = isDeferred; + + for (int i = rv.getChildCount() - 1; i >= 0; i--) { + WidgetsRowViewHolder holder = (WidgetsRowViewHolder) + rv.getChildViewHolder(rv.getChildAt(i)); + for (int j = holder.cellContainer.getChildCount() - 1; j >= 0; j--) { + View v = holder.cellContainer.getChildAt(j); + if (v instanceof WidgetCell) { + ((WidgetCell) v).setApplyBitmapDeferred(mApplyBitmapDeferred); + } } - }); + } } /** * Update the widget list. */ - public void setWidgets(MultiHashMap<PackageItemInfo, WidgetItem> widgets) { - ArrayList<WidgetListRowEntry> tempEntries = new ArrayList<>(); - - WidgetItemComparator widgetComparator = new WidgetItemComparator(); - for (Map.Entry<PackageItemInfo, ArrayList<WidgetItem>> entry : widgets.entrySet()) { - WidgetListRowEntry row = new WidgetListRowEntry(entry.getKey(), entry.getValue()); - row.titleSectionName = mIndexer.computeSectionName(row.pkgItem.title); - Collections.sort(row.widgets, widgetComparator); - tempEntries.add(row); - } + public void setWidgets(ArrayList<WidgetListRowEntry> tempEntries) { WidgetListRowEntryComparator rowComparator = new WidgetListRowEntryComparator(); Collections.sort(tempEntries, rowComparator); mDiffReporter.process(mEntries, tempEntries, rowComparator); @@ -128,26 +111,6 @@ public class WidgetsListAdapter extends Adapter<WidgetsRowViewHolder> { return mEntries.get(pos).titleSectionName; } - /** - * Copies and returns the widgets associated with the package and user of the ComponentKey. - */ - public List<WidgetItem> copyWidgetsForPackageUser(PackageUserKey packageUserKey) { - for (WidgetListRowEntry entry : mEntries) { - if (entry.pkgItem.packageName.equals(packageUserKey.mPackageName)) { - ArrayList<WidgetItem> widgets = new ArrayList<>(entry.widgets); - // Remove widgets not associated with the correct user. - Iterator<WidgetItem> iterator = widgets.iterator(); - while (iterator.hasNext()) { - if (!iterator.next().user.equals(packageUserKey.mUser)) { - iterator.remove(); - } - } - return widgets.isEmpty() ? null : widgets; - } - } - return null; - } - @Override public void onBindViewHolder(WidgetsRowViewHolder holder, int pos) { WidgetListRowEntry entry = mEntries.get(pos); @@ -194,6 +157,7 @@ public class WidgetsListAdapter extends Adapter<WidgetsRowViewHolder> { for (int i=0; i < infoList.size(); i++) { WidgetCell widget = (WidgetCell) row.getChildAt(2*i); widget.applyFromCellItem(infoList.get(i), mWidgetPreviewLoader); + widget.setApplyBitmapDeferred(mApplyBitmapDeferred); widget.ensurePreview(); widget.setVisibility(View.VISIBLE); @@ -253,5 +217,4 @@ public class WidgetsListAdapter extends Adapter<WidgetsRowViewHolder> { return mComparator.compare(a.pkgItem.title.toString(), b.pkgItem.title.toString()); } } - } diff --git a/src/com/android/launcher3/widget/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/WidgetsRecyclerView.java index 9730a82aa..89c88a4e7 100644 --- a/src/com/android/launcher3/widget/WidgetsRecyclerView.java +++ b/src/com/android/launcher3/widget/WidgetsRecyclerView.java @@ -17,12 +17,12 @@ package com.android.launcher3.widget; import android.content.Context; -import android.graphics.Color; import android.support.v7.widget.LinearLayoutManager; import android.util.AttributeSet; import android.view.View; import com.android.launcher3.BaseRecyclerView; +import com.android.launcher3.R; /** * The widgets recycler view. @@ -32,6 +32,8 @@ public class WidgetsRecyclerView extends BaseRecyclerView { private static final String TAG = "WidgetsRecyclerView"; private WidgetsListAdapter mAdapter; + private final int mScrollbarTop; + public WidgetsRecyclerView(Context context) { this(context, null); } @@ -43,6 +45,7 @@ public class WidgetsRecyclerView extends BaseRecyclerView { public WidgetsRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) { // API 21 and below only support 3 parameter ctor. super(context, attrs, defStyleAttr); + mScrollbarTop = getResources().getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin); } public WidgetsRecyclerView(Context context, AttributeSet attrs, int defStyleAttr, @@ -130,13 +133,16 @@ public class WidgetsRecyclerView extends BaseRecyclerView { @Override protected int getAvailableScrollHeight() { View child = getChildAt(0); - int height = child.getMeasuredHeight() * mAdapter.getItemCount(); - int totalHeight = getPaddingTop() + height + getPaddingBottom(); - int availableScrollHeight = totalHeight - getScrollbarTrackHeight(); - return availableScrollHeight; + return child.getMeasuredHeight() * mAdapter.getItemCount() - getScrollbarTrackHeight() + - mScrollbarTop; } private boolean isModelNotReady() { return mAdapter.getItemCount() == 0; } + + @Override + public int getScrollBarTop() { + return mScrollbarTop; + } }
\ No newline at end of file |