summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--res/layout-land/launcher.xml6
-rw-r--r--res/layout-port/launcher.xml6
-rw-r--r--res/layout-sw720dp/launcher.xml6
-rw-r--r--res/layout/all_apps.xml65
-rw-r--r--res/layout/widgets_full_sheet.xml (renamed from res/layout/widgets_view.xml)51
-rw-r--r--res/values/attrs.xml7
-rw-r--r--res/values/config.xml2
-rw-r--r--src/com/android/launcher3/AbstractFloatingView.java23
-rw-r--r--src/com/android/launcher3/BaseContainerView.java218
-rw-r--r--src/com/android/launcher3/BaseRecyclerView.java36
-rw-r--r--src/com/android/launcher3/Launcher.java113
-rw-r--r--src/com/android/launcher3/LauncherModel.java3
-rw-r--r--src/com/android/launcher3/LauncherStateTransitionAnimation.java586
-rw-r--r--src/com/android/launcher3/WidgetPreviewLoader.java11
-rw-r--r--src/com/android/launcher3/allapps/AllAppsContainerView.java120
-rw-r--r--src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java73
-rw-r--r--src/com/android/launcher3/allapps/AllAppsTransitionController.java2
-rw-r--r--src/com/android/launcher3/anim/CircleRevealOutlineProvider.java53
-rw-r--r--src/com/android/launcher3/model/BaseModelUpdateTask.java6
-rw-r--r--src/com/android/launcher3/model/LoaderResults.java6
-rw-r--r--src/com/android/launcher3/model/WidgetsModel.java27
-rw-r--r--src/com/android/launcher3/popup/PopupDataProvider.java30
-rw-r--r--src/com/android/launcher3/popup/SystemShortcut.java5
-rw-r--r--src/com/android/launcher3/views/RecyclerViewFastScroller.java6
-rw-r--r--src/com/android/launcher3/widget/BaseWidgetSheet.java288
-rw-r--r--src/com/android/launcher3/widget/WidgetCell.java33
-rw-r--r--src/com/android/launcher3/widget/WidgetsBottomSheet.java199
-rw-r--r--src/com/android/launcher3/widget/WidgetsContainerView.java243
-rw-r--r--src/com/android/launcher3/widget/WidgetsDiffReporter.java37
-rw-r--r--src/com/android/launcher3/widget/WidgetsFullSheet.java222
-rw-r--r--src/com/android/launcher3/widget/WidgetsListAdapter.java87
-rw-r--r--src/com/android/launcher3/widget/WidgetsRecyclerView.java16
-rw-r--r--tests/src/com/android/launcher3/widget/WidgetsListAdapterTest.java47
33 files changed, 1024 insertions, 1609 deletions
diff --git a/res/layout-land/launcher.xml b/res/layout-land/launcher.xml
index ac440fc01..0c7999ec7 100644
--- a/res/layout-land/launcher.xml
+++ b/res/layout-land/launcher.xml
@@ -60,12 +60,6 @@
android:id="@+id/overview_panel"
android:visibility="gone" />
- <include layout="@layout/widgets_view"
- android:id="@+id/widgets_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:visibility="invisible" />
-
<include layout="@layout/all_apps"
android:id="@+id/apps_view"
android:layout_width="match_parent"
diff --git a/res/layout-port/launcher.xml b/res/layout-port/launcher.xml
index c41a6e380..f58a87ecd 100644
--- a/res/layout-port/launcher.xml
+++ b/res/layout-port/launcher.xml
@@ -66,12 +66,6 @@
android:id="@+id/drop_target_bar"
layout="@layout/drop_target_bar_horz" />
- <include layout="@layout/widgets_view"
- android:id="@+id/widgets_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:visibility="invisible" />
-
<include layout="@layout/all_apps"
android:id="@+id/apps_view"
android:layout_width="match_parent"
diff --git a/res/layout-sw720dp/launcher.xml b/res/layout-sw720dp/launcher.xml
index 03e42bc1d..660d0ed14 100644
--- a/res/layout-sw720dp/launcher.xml
+++ b/res/layout-sw720dp/launcher.xml
@@ -65,12 +65,6 @@
<include layout="@layout/page_indicator"
android:id="@+id/page_indicator" />
- <include layout="@layout/widgets_view"
- android:id="@+id/widgets_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:visibility="invisible" />
-
<include layout="@layout/all_apps"
android:id="@+id/apps_view"
android:layout_width="match_parent"
diff --git a/res/layout/all_apps.xml b/res/layout/all_apps.xml
index 39df2b193..6f837aeca 100644
--- a/res/layout/all_apps.xml
+++ b/res/layout/all_apps.xml
@@ -18,60 +18,39 @@
will bake the left/right padding into that view's background itself. -->
<com.android.launcher3.allapps.AllAppsContainerView
xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:launcher="http://schemas.android.com/apk/res-auto"
android:id="@+id/apps_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:orientation="vertical"
- launcher:revealBackground="@drawable/round_rect_primary">
-
- <View
- android:id="@+id/reveal_view"
+ android:clipChildren="true"
+ android:clipToPadding="false"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:saveEnabled="false" >
+
+ <!-- DO NOT CHANGE THE ID -->
+ <com.android.launcher3.allapps.AllAppsRecyclerView
+ android:id="@+id/apps_list_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_gravity="center"
- android:focusable="false"
- android:visibility="invisible" />
-
-
- <com.android.launcher3.allapps.AllAppsRecyclerViewContainerView
- android:id="@+id/main_content"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="center"
- android:focusable="true"
+ android:layout_below="@id/search_container_all_apps"
+ android:layout_gravity="center_horizontal|top"
android:clipToPadding="false"
- android:clipChildren="true"
- android:focusableInTouchMode="true"
- android:saveEnabled="false"
- android:visibility="gone">
-
- <!-- DO NOT CHANGE THE ID -->
- <com.android.launcher3.allapps.AllAppsRecyclerView
- android:id="@+id/apps_list_view"
- android:layout_below="@id/search_container_all_apps"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="center_horizontal|top"
- android:clipToPadding="false"
- android:overScrollMode="never"
- android:descendantFocusability="afterDescendants"
- android:focusable="true" />
+ android:descendantFocusability="afterDescendants"
+ android:focusable="true"
+ android:overScrollMode="never" />
- <!-- Note: we are reusing/repurposing a system attribute for search layout, because of a
- platform bug, which prevents using custom attributes in <include> tag -->
- <include
- layout="?android:attr/keyboardLayout"
- android:id="@id/search_container_all_apps" />
+ <!-- Note: we are reusing/repurposing a system attribute for search layout, because of a
+ platform bug, which prevents using custom attributes in <include> tag -->
+ <include
+ android:id="@id/search_container_all_apps"
+ layout="?android:attr/keyboardLayout" />
- <include layout="@layout/all_apps_fast_scroller" />
+ <include layout="@layout/all_apps_fast_scroller" />
- </com.android.launcher3.allapps.AllAppsRecyclerViewContainerView>
<View
android:id="@+id/nav_bar_bg"
- android:background="?attr/allAppsNavBarScrimColor"
android:layout_width="match_parent"
android:layout_height="0dp"
- android:layout_gravity="bottom"
- android:focusable="false" />
+ android:layout_alignParentBottom="true"
+ android:background="?attr/allAppsNavBarScrimColor" />
</com.android.launcher3.allapps.AllAppsContainerView> \ No newline at end of file
diff --git a/res/layout/widgets_view.xml b/res/layout/widgets_full_sheet.xml
index 4f3c7c8df..153529968 100644
--- a/res/layout/widgets_view.xml
+++ b/res/layout/widgets_full_sheet.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
+<!-- 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.
@@ -13,40 +13,31 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<!-- The top and bottom paddings are defined in this container, but since we want
- the list view to span the full width (for touch interception purposes), we
- will bake the left/right padding into that view's background itself. -->
-<com.android.launcher3.widget.WidgetsContainerView
+<com.android.launcher3.widget.WidgetsFullSheet
xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:launcher="http://schemas.android.com/apk/res-auto"
- android:id="@+id/widgets_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:descendantFocusability="afterDescendants"
- android:theme="?attr/widgetsTheme"
- launcher:revealBackground="@drawable/round_rect_primary">
+ android:orientation="vertical"
+ android:theme="?attr/widgetsTheme" >
- <View
- android:id="@+id/reveal_view"
+ <com.android.launcher3.graphics.GradientView
+ android:id="@+id/gradient_bg"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="center"
- android:elevation="2dp"
- android:focusable="false"
- android:visibility="invisible" />
+ android:layout_height="match_parent" />
<FrameLayout
- android:id="@+id/main_content"
+ android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_gravity="center"
- android:elevation="15dp"
- android:visibility="gone">
+ android:background="?android:attr/colorPrimary"
+ android:elevation="4dp">
<com.android.launcher3.widget.WidgetsRecyclerView
android:id="@+id/widgets_list_view"
android:layout_width="match_parent"
- android:layout_height="match_parent" />
+ android:layout_height="match_parent"
+ android:clipToPadding="false"
+ />
<!-- Fast scroller popup -->
<TextView
@@ -55,12 +46,6 @@
android:layout_gravity="top|end"
android:layout_marginEnd="@dimen/fastscroll_popup_margin" />
- <ProgressBar
- android:id="@+id/loader"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center" />
-
<com.android.launcher3.views.RecyclerViewFastScroller
android:id="@+id/fast_scroller"
android:layout_width="@dimen/fastscroll_width"
@@ -68,6 +53,12 @@
android:layout_gravity="end"
android:layout_marginEnd="@dimen/fastscroll_end_margin" />
+ <View
+ android:id="@+id/nav_bar_bg"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_gravity="bottom"
+ android:background="?attr/allAppsNavBarScrimColor"
+ android:focusable="false" />
</FrameLayout>
-
-</com.android.launcher3.widget.WidgetsContainerView> \ No newline at end of file
+</com.android.launcher3.widget.WidgetsFullSheet> \ No newline at end of file
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index e87397bb4..ad5f0b880 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -63,13 +63,6 @@
<attr name="pageIndicator" format="reference" />
</declare-styleable>
- <!-- BaseContainerView specific attributes. These attributes are used to customize
- AllApps view and WidgetsView in xml. -->
- <declare-styleable name="BaseContainerView">
- <!-- Drawable to use for the reveal animation -->
- <attr name="revealBackground" format="reference" />
- </declare-styleable>
-
<!-- XML attributes used by default_workspace.xml -->
<declare-styleable name="Favorite">
<attr name="className" format="string" />
diff --git a/res/values/config.xml b/res/values/config.xml
index f8faf988a..27d4655b9 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -57,10 +57,8 @@
<!-- Fade/zoom in/out duration & scale in a Launcher overlay transition.
Note: This should be less than the config_overlayTransitionTime as they happen together. -->
- <integer name="config_overlayRevealTime">220</integer>
<integer name="config_overlaySlideRevealTime">320</integer>
<integer name="config_overlayTransitionTime">300</integer>
- <integer name="config_overlayItemsAlphaStagger">60</integer>
<!-- This constant stores the ratio of the all apps button drawable which
is used for internal (baked-in) padding -->
diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java
index 0fbad522e..62e0fb1bc 100644
--- a/src/com/android/launcher3/AbstractFloatingView.java
+++ b/src/com/android/launcher3/AbstractFloatingView.java
@@ -20,6 +20,7 @@ import android.annotation.SuppressLint;
import android.content.Context;
import android.support.annotation.IntDef;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.LinearLayout;
@@ -40,7 +41,8 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch
TYPE_FOLDER,
TYPE_ACTION_POPUP,
TYPE_WIDGETS_BOTTOM_SHEET,
- TYPE_WIDGET_RESIZE_FRAME
+ TYPE_WIDGET_RESIZE_FRAME,
+ TYPE_WIDGETS_FULL_SHEET
})
@Retention(RetentionPolicy.SOURCE)
public @interface FloatingViewType {}
@@ -48,6 +50,10 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch
public static final int TYPE_ACTION_POPUP = 1 << 1;
public static final int TYPE_WIDGETS_BOTTOM_SHEET = 1 << 2;
public static final int TYPE_WIDGET_RESIZE_FRAME = 1 << 3;
+ public static final int TYPE_WIDGETS_FULL_SHEET = 1 << 4;
+
+ public static final int TYPE_ALL = TYPE_FOLDER | TYPE_ACTION_POPUP
+ | TYPE_WIDGETS_BOTTOM_SHEET | TYPE_WIDGET_RESIZE_FRAME | TYPE_WIDGETS_FULL_SHEET;
protected boolean mIsOpen;
@@ -121,24 +127,31 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch
}
}
- public static void closeAllOpenViews(Launcher launcher, boolean animate) {
+ public static void closeOpenViews(Launcher launcher, boolean animate,
+ @FloatingViewType int type) {
DragLayer dragLayer = launcher.getDragLayer();
// Iterate in reverse order. AbstractFloatingView is added later to the dragLayer,
// and will be one of the last views.
for (int i = dragLayer.getChildCount() - 1; i >= 0; i--) {
View child = dragLayer.getChildAt(i);
if (child instanceof AbstractFloatingView) {
- ((AbstractFloatingView) child).close(animate);
+ AbstractFloatingView abs = (AbstractFloatingView) child;
+ if (abs.isOfType(type)) {
+ abs.close(animate);
+ }
}
}
}
+ public static void closeAllOpenViews(Launcher launcher, boolean animate) {
+ closeOpenViews(launcher, animate, TYPE_ALL);
+ }
+
public static void closeAllOpenViews(Launcher launcher) {
closeAllOpenViews(launcher, true);
}
public static AbstractFloatingView getTopOpenView(Launcher launcher) {
- return getOpenView(launcher, TYPE_FOLDER | TYPE_ACTION_POPUP
- | TYPE_WIDGETS_BOTTOM_SHEET | TYPE_WIDGET_RESIZE_FRAME);
+ return getOpenView(launcher, TYPE_ALL);
}
}
diff --git a/src/com/android/launcher3/BaseContainerView.java b/src/com/android/launcher3/BaseContainerView.java
deleted file mode 100644
index 82175b721..000000000
--- a/src/com/android/launcher3/BaseContainerView.java
+++ /dev/null
@@ -1,218 +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;
-
-import android.annotation.SuppressLint;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.InsetDrawable;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.widget.FrameLayout;
-
-import com.android.launcher3.allapps.AllAppsContainerView;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.util.TransformingTouchDelegate;
-
-/**
- * A base container view, which supports resizing.
- */
-public abstract class BaseContainerView extends FrameLayout
- implements DeviceProfile.LauncherLayoutChangeListener {
-
- private static final Rect sBgPaddingRect = new Rect();
-
- protected final Drawable mBaseDrawable;
-
- private View mRevealView;
- private View mContent;
-
- private TransformingTouchDelegate mTouchDelegate;
-
- private final PointF mLastTouchDownPosPx = new PointF(-1.0f, -1.0f);
-
- public BaseContainerView(Context context) {
- this(context, null);
- }
-
- public BaseContainerView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public BaseContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
-
- if (this instanceof AllAppsContainerView) {
- mBaseDrawable = new ColorDrawable();
- } else {
- TypedArray a = context.obtainStyledAttributes(attrs,
- R.styleable.BaseContainerView, defStyleAttr, 0);
- mBaseDrawable = a.getDrawable(R.styleable.BaseContainerView_revealBackground);
- a.recycle();
- }
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
-
- DeviceProfile grid = Launcher.getLauncher(getContext()).getDeviceProfile();
- grid.addLauncherLayoutChangedListener(this);
-
- View touchDelegateTargetView = getTouchDelegateTargetView();
- if (touchDelegateTargetView != null) {
- mTouchDelegate = new TransformingTouchDelegate(touchDelegateTargetView);
- ((View) touchDelegateTargetView.getParent()).setTouchDelegate(mTouchDelegate);
- }
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
-
- DeviceProfile grid = Launcher.getLauncher(getContext()).getDeviceProfile();
- grid.removeLauncherLayoutChangedListener(this);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
-
- mContent = findViewById(R.id.main_content);
- mRevealView = findViewById(R.id.reveal_view);
-
- updatePaddings();
- }
-
- @Override
- public void onLauncherLayoutChanged() {
- updatePaddings();
- }
-
- /**
- * Calculate the background padding as it can change due to insets/content padding change.
- */
- private void updatePaddings() {
- DeviceProfile grid = Launcher.getLauncher(getContext()).getDeviceProfile();
- int[] padding = grid.getContainerPadding();
-
- int paddingLeft = padding[0];
- int paddingRight = padding[1];
- int paddingTop = 0;
- int paddingBottom = 0;
-
- if (!grid.isVerticalBarLayout()) {
- paddingLeft += grid.edgeMarginPx;
- paddingRight += grid.edgeMarginPx;
- paddingTop = paddingBottom = grid.edgeMarginPx;
- }
- updateBackground(paddingLeft, paddingTop, paddingRight, paddingBottom);
- }
-
- /**
- * Update the background for the reveal view and content view based on the background padding.
- */
- protected void updateBackground(int paddingLeft, int paddingTop,
- int paddingRight, int paddingBottom) {
- mRevealView.setBackground(new InsetDrawable(mBaseDrawable,
- paddingLeft, paddingTop, paddingRight, paddingBottom));
- mContent.setBackground(new InsetDrawable(mBaseDrawable,
- paddingLeft, paddingTop, paddingRight, paddingBottom));
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
-
- View touchDelegateTargetView = getTouchDelegateTargetView();
- if (touchDelegateTargetView != null) {
- getRevealView().getBackground().getPadding(sBgPaddingRect);
- mTouchDelegate.setBounds(
- touchDelegateTargetView.getLeft() - sBgPaddingRect.left,
- touchDelegateTargetView.getTop() - sBgPaddingRect.top,
- touchDelegateTargetView.getRight() + sBgPaddingRect.right,
- touchDelegateTargetView.getBottom() + sBgPaddingRect.bottom);
- }
- }
-
- @Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- return handleTouchEvent(ev);
- }
-
- @SuppressLint("ClickableViewAccessibility")
- @Override
- public boolean onTouchEvent(MotionEvent ev) {
- return handleTouchEvent(ev);
- }
-
- public void setRevealDrawableColor(int color) {
- ((ColorDrawable) mBaseDrawable).setColor(color);
- }
-
- public final View getContentView() {
- return mContent;
- }
-
- public final View getRevealView() {
- return mRevealView;
- }
-
-
- /**
- * Handles the touch events that shows the workspace when clicking outside the bounds of the
- * touch delegate target view.
- */
- private boolean handleTouchEvent(MotionEvent ev) {
- switch (ev.getAction()) {
- case MotionEvent.ACTION_DOWN:
- // Check if the touch is outside touch delegate target view
- View touchDelegateTargetView = getTouchDelegateTargetView();
- float leftBoundPx = touchDelegateTargetView.getLeft();
- if (ev.getX() < leftBoundPx ||
- ev.getX() > (touchDelegateTargetView.getWidth() + leftBoundPx)) {
- mLastTouchDownPosPx.set((int) ev.getX(), (int) ev.getY());
- }
- break;
- case MotionEvent.ACTION_UP:
- if (mLastTouchDownPosPx.x > -1) {
- ViewConfiguration viewConfig = ViewConfiguration.get(getContext());
- float dx = ev.getX() - mLastTouchDownPosPx.x;
- float dy = ev.getY() - mLastTouchDownPosPx.y;
- float distance = PointF.length(dx, dy);
- if (distance < viewConfig.getScaledTouchSlop()) {
- // The background was clicked, so just go home
- Launcher.getLauncher(getContext()).showWorkspace(true);
- return true;
- }
- }
- // Fall through
- case MotionEvent.ACTION_CANCEL:
- mLastTouchDownPosPx.set(-1, -1);
- break;
- }
- return false;
- }
-
- public abstract View getTouchDelegateTargetView();
-}
diff --git a/src/com/android/launcher3/BaseRecyclerView.java b/src/com/android/launcher3/BaseRecyclerView.java
index 3ee6e51b8..afb83be58 100644
--- a/src/com/android/launcher3/BaseRecyclerView.java
+++ b/src/com/android/launcher3/BaseRecyclerView.java
@@ -21,6 +21,7 @@ import android.graphics.Canvas;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.MotionEvent;
+import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
@@ -99,11 +100,15 @@ public abstract class BaseRecyclerView extends RecyclerView
// DO NOT REMOVE, NEEDED IMPLEMENTATION FOR M BUILDS
}
+ public int getScrollBarTop() {
+ return getPaddingTop();
+ }
+
/**
* Returns the height of the fast scroll bar
*/
public int getScrollbarTrackHeight() {
- return getHeight() - getPaddingTop() - getPaddingBottom();
+ return getHeight() - getScrollBarTop() - getPaddingBottom();
}
/**
@@ -121,13 +126,6 @@ public abstract class BaseRecyclerView extends RecyclerView
return availableScrollBarHeight;
}
- /**
- * Returns the scrollbar for this recycler view.
- */
- public RecyclerViewFastScroller getScrollBar() {
- return mScrollbar;
- }
-
@Override
protected void dispatchDraw(Canvas canvas) {
onUpdateScrollbar(0);
@@ -160,6 +158,28 @@ public abstract class BaseRecyclerView extends RecyclerView
}
/**
+ * Returns whether the view itself will handle the touch event or not.
+ * @param ev MotionEvent in {@param eventSource}
+ */
+ public boolean shouldContainerScroll(MotionEvent ev, View eventSource) {
+ int[] point = new int[2];
+ point[0] = (int) ev.getX();
+ point[1] = (int) ev.getY();
+ Utilities.mapCoordInSelfToDescendant(mScrollbar, eventSource, point);
+ // IF the MotionEvent is inside the thumb, container should not be pulled down.
+ if (mScrollbar.shouldBlockIntercept(point[0], point[1])) {
+ return false;
+ }
+
+ // IF scroller is at the very top OR there is no scroll bar because there is probably not
+ // enough items to scroll, THEN it's okay for the container to be pulled down.
+ if (getCurrentScrollY() == 0) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
* @return whether fast scrolling is supported in the current state.
*/
public boolean supportsFastScrolling() {
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 1bb4807b5..5c635ca51 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -54,6 +54,7 @@ import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
+import android.os.Parcelable;
import android.os.Process;
import android.os.StrictMode;
import android.os.UserHandle;
@@ -63,6 +64,7 @@ import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.text.method.TextKeyListener;
import android.util.Log;
+import android.util.SparseArray;
import android.view.Display;
import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
@@ -105,8 +107,6 @@ import com.android.launcher3.keyboard.ViewGroupFocusHelper;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.model.ModelWriter;
-import com.android.launcher3.model.PackageItemInfo;
-import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.notification.NotificationListener;
import com.android.launcher3.pageindicators.PageIndicator;
import com.android.launcher3.popup.BaseActionPopup;
@@ -136,7 +136,8 @@ import com.android.launcher3.widget.PendingAddShortcutInfo;
import com.android.launcher3.widget.PendingAddWidgetInfo;
import com.android.launcher3.widget.WidgetAddFlowHandler;
import com.android.launcher3.widget.WidgetHostViewLoader;
-import com.android.launcher3.widget.WidgetsContainerView;
+import com.android.launcher3.widget.WidgetListRowEntry;
+import com.android.launcher3.widget.WidgetsFullSheet;
import com.android.launcher3.widget.custom.CustomWidgetParser;
import java.io.FileDescriptor;
@@ -198,6 +199,8 @@ public class Launcher extends BaseActivity
private static final String RUNTIME_STATE_PENDING_REQUEST_ARGS = "launcher.request_args";
// Type: ActivityResultInfo
private static final String RUNTIME_STATE_PENDING_ACTIVITY_RESULT = "launcher.activity_result";
+ // Type: SparseArray<Parcelable>
+ private static final String RUNTIME_STATE_WIDGET_PANEL = "launcher.widget_panel";
static final String APPS_VIEW_SHOWN = "launcher.apps_view_shown";
@@ -234,7 +237,6 @@ public class Launcher extends BaseActivity
private ViewGroup mOverviewPanel;
private View mAllAppsButton;
- private View mWidgetsButton;
private DropTargetBar mDropTargetBar;
@@ -242,9 +244,6 @@ public class Launcher extends BaseActivity
@Thunk AllAppsContainerView mAppsView;
AllAppsTransitionController mAllAppsController;
- // Main container view and the model for the widget tray screen.
- @Thunk WidgetsContainerView mWidgetsView;
-
// We need to store the orientation Launcher was created with, due to a bug (b/64916689)
// that results in widgets being inflated in the wrong orientation.
private int mOrientation;
@@ -1043,8 +1042,6 @@ public class Launcher extends BaseActivity
? stateValues[stateOrdinal] : State.WORKSPACE;
if (state == State.APPS) {
showAppsView(false /* animated */);
- } else if (state == State.WIDGETS) {
- showWidgetsView(false, false);
}
PendingRequestArgs requestArgs = savedState.getParcelable(RUNTIME_STATE_PENDING_REQUEST_ARGS);
@@ -1053,6 +1050,12 @@ public class Launcher extends BaseActivity
}
mPendingActivityResult = savedState.getParcelable(RUNTIME_STATE_PENDING_ACTIVITY_RESULT);
+
+ SparseArray<Parcelable> widgetsState =
+ savedState.getSparseParcelableArray(RUNTIME_STATE_WIDGET_PANEL);
+ if (widgetsState != null) {
+ WidgetsFullSheet.show(this, false).restoreHierarchyState(widgetsState);
+ }
}
/**
@@ -1093,9 +1096,8 @@ public class Launcher extends BaseActivity
// Get the search/delete/uninstall bar
mDropTargetBar = mDragLayer.findViewById(R.id.drop_target_bar);
- // Setup Apps and Widgets
- mAppsView = (AllAppsContainerView) findViewById(R.id.apps_view);
- mWidgetsView = (WidgetsContainerView) findViewById(R.id.widgets_view);
+ // Setup Apps
+ mAppsView = findViewById(R.id.apps_view);
// Setup the drag controller (drop targets have to be added in reverse order in priority)
mDragController.setMoveTarget(mWorkspace);
@@ -1110,7 +1112,7 @@ public class Launcher extends BaseActivity
}
private void setupOverviewPanel() {
- mOverviewPanel = (ViewGroup) findViewById(R.id.overview_panel);
+ mOverviewPanel = findViewById(R.id.overview_panel);
// Bind wallpaper button actions
View wallpaperButton = findViewById(R.id.wallpaper_button);
@@ -1122,13 +1124,12 @@ public class Launcher extends BaseActivity
}.attachTo(wallpaperButton);
// Bind widget button actions
- mWidgetsButton = findViewById(R.id.widget_button);
new OverviewButtonClickListener(ControlType.WIDGETS_BUTTON) {
@Override
public void handleViewClick(View view) {
onClickAddWidgetButton(view);
}
- }.attachTo(mWidgetsButton);
+ }.attachTo(findViewById(R.id.widget_button));
// Bind settings actions
View settingsButton = findViewById(R.id.settings_button);
@@ -1155,14 +1156,6 @@ public class Launcher extends BaseActivity
mAllAppsButton = allAppsButton;
}
- public View getStartViewForAllAppsRevealAnimation() {
- return FeatureFlags.NO_ALL_APPS_ICON ? mWorkspace.getPageIndicator() : mAllAppsButton;
- }
-
- public View getWidgetsButton() {
- return mWidgetsButton;
- }
-
/**
* Creates a view representing a shortcut.
*
@@ -1325,7 +1318,7 @@ public class Launcher extends BaseActivity
if (Intent.ACTION_SCREEN_OFF.equals(action)) {
// Reset AllApps to its initial state only if we are not in the middle of
// processing a multi-step drop
- if (mAppsView != null && mWidgetsView != null && mPendingRequestArgs == null) {
+ if (mAppsView != null && mPendingRequestArgs == null) {
if (!showWorkspace(false)) {
// If we are already on the workspace, then manually reset all apps
mAppsView.reset();
@@ -1385,10 +1378,6 @@ public class Launcher extends BaseActivity
return mAppsView;
}
- public WidgetsContainerView getWidgetsView() {
- return mWidgetsView;
- }
-
public Workspace getWorkspace() {
return mWorkspace;
}
@@ -1468,11 +1457,6 @@ public class Launcher extends BaseActivity
mAppsView.reset();
}
- // Reset the widgets view
- if (!alreadyOnHome && mWidgetsView != null) {
- mWidgetsView.scrollToTop();
- }
-
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onHomeIntent();
}
@@ -1517,9 +1501,19 @@ public class Launcher extends BaseActivity
outState.putInt(RUNTIME_STATE_CURRENT_SCREEN, mWorkspace.getNextPage());
}
- super.onSaveInstanceState(outState);
-
outState.putInt(RUNTIME_STATE, mState.ordinal());
+
+
+ AbstractFloatingView widgets = AbstractFloatingView
+ .getOpenView(this, AbstractFloatingView.TYPE_WIDGETS_FULL_SHEET);
+ if (widgets != null) {
+ SparseArray<Parcelable> widgetsState = new SparseArray<>();
+ widgets.saveHierarchyState(widgetsState);
+ outState.putSparseParcelableArray(RUNTIME_STATE_WIDGET_PANEL, widgetsState);
+ } else {
+ outState.remove(RUNTIME_STATE_WIDGET_PANEL);
+ }
+
// We close any open folders and shortcut containers since they will not be re-opened,
// and we need to make sure this state is reflected.
AbstractFloatingView.closeAllOpenViews(this, false);
@@ -1531,6 +1525,8 @@ public class Launcher extends BaseActivity
outState.putParcelable(RUNTIME_STATE_PENDING_ACTIVITY_RESULT, mPendingActivityResult);
}
+ super.onSaveInstanceState(outState);
+
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onSaveInstanceState(outState);
}
@@ -2159,7 +2155,7 @@ public class Launcher extends BaseActivity
if (mIsSafeModeEnabled) {
Toast.makeText(this, R.string.safemode_widget_error, Toast.LENGTH_SHORT).show();
} else {
- showWidgetsView(true /* animated */, true /* resetPageToZero */);
+ WidgetsFullSheet.show(this, true /* animated */);
}
}
@@ -2557,24 +2553,6 @@ public class Launcher extends BaseActivity
}
/**
- * Shows the widgets view.
- */
- void showWidgetsView(boolean animated, boolean resetPageToZero) {
- if (LOGD) Log.d(TAG, "showWidgetsView:" + animated + " resetPageToZero:" + resetPageToZero);
- if (resetPageToZero) {
- mWidgetsView.scrollToTop();
- }
- showAppsOrWidgets(State.WIDGETS, animated);
-
- mWidgetsView.post(new Runnable() {
- @Override
- public void run() {
- mWidgetsView.requestFocus();
- }
- });
- }
-
- /**
* Sets up the transition to show the apps/widgets view.
*
* @return whether the current from and to state allowed this operation
@@ -2642,7 +2620,6 @@ public class Launcher extends BaseActivity
// Before we show workspace, hide all apps again because
// exitSpringLoadedDragMode made it visible. This is a bit hacky; we should
// clean up our state transition functions
- mWidgetsView.setVisibility(View.GONE);
showWorkspace(true, onCompleteRunnable);
} else {
exitSpringLoadedDragMode();
@@ -2661,8 +2638,6 @@ public class Launcher extends BaseActivity
public void exitSpringLoadedDragMode() {
if (mState == State.APPS_SPRING_LOADED) {
showAppsView(true /* animated */);
- } else if (mState == State.WIDGETS_SPRING_LOADED) {
- showWidgetsView(true, false);
} else if (mState == State.WORKSPACE_SPRING_LOADED) {
showWorkspace(true);
}
@@ -2676,8 +2651,6 @@ public class Launcher extends BaseActivity
// Populate event with a fake title based on the current state.
if (mState == State.APPS) {
text.add(getString(R.string.all_apps_button_label));
- } else if (mState == State.WIDGETS) {
- text.add(getString(R.string.widget_button_text));
} else if (mWorkspace != null) {
text.add(mWorkspace.getCurrentPageDescription());
} else {
@@ -2777,7 +2750,11 @@ public class Launcher extends BaseActivity
*/
public void startBinding() {
TraceHelper.beginSection("startBinding");
- AbstractFloatingView.closeAllOpenViews(this);
+ // Floating panels (except the full widget sheet) are associated with individual icons. If
+ // we are starting a fresh bind, close all such panels as all the icons are about
+ // to go away.
+ AbstractFloatingView.closeOpenViews(this, true,
+ AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_WIDGETS_FULL_SHEET);
setWorkspaceLoading(true);
@@ -3388,7 +3365,8 @@ public class Launcher extends BaseActivity
}
@Override
- public void bindAllWidgets(final MultiHashMap<PackageItemInfo, WidgetItem> allWidgets) {
+ public void bindAllWidgets(final ArrayList<WidgetListRowEntry> allWidgets) {
+ mPopupDataProvider.setAllWidgets(allWidgets);
Runnable r = new RunnableWithId(RUNNABLE_ID_BIND_WIDGETS) {
@Override
public void run() {
@@ -3399,25 +3377,12 @@ public class Launcher extends BaseActivity
return;
}
- if (mWidgetsView != null && allWidgets != null) {
- Executor pendingExecutor = getPendingExecutor();
- if (pendingExecutor != null && mState != State.WIDGETS) {
- pendingExecutor.execute(r);
- return;
- }
- mWidgetsView.setWidgets(allWidgets);
- }
-
AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(this);
if (topView != null) {
topView.onWidgetsBound();
}
}
- public List<WidgetItem> getWidgetsForPackageUser(PackageUserKey packageUserKey) {
- return mWidgetsView.getWidgetsForPackageUser(packageUserKey);
- }
-
@Override
public void notifyWidgetProvidersChanged() {
if (mWorkspace.getState().shouldUpdateWidget) {
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 3e2236682..618bd0f73 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -62,6 +62,7 @@ import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.Provider;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.util.ViewOnDrawExecutor;
+import com.android.launcher3.widget.WidgetListRowEntry;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -153,7 +154,7 @@ public class LauncherModel extends BroadcastReceiver
public void bindRestoreItemsChange(HashSet<ItemInfo> updates);
public void bindWorkspaceComponentsRemoved(ItemInfoMatcher matcher);
public void bindAppInfosRemoved(ArrayList<AppInfo> appInfos);
- public void bindAllWidgets(MultiHashMap<PackageItemInfo, WidgetItem> widgets);
+ public void bindAllWidgets(ArrayList<WidgetListRowEntry> widgets);
public void onPageBoundSynchronously(int page);
public void executeOnNextDraw(ViewOnDrawExecutor executor);
public void bindDeepShortcutMap(MultiHashMap<ComponentKey, String> deepShortcutMap);
diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
index be0ed0cef..5823734e4 100644
--- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java
+++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
@@ -19,22 +19,15 @@ package com.android.launcher3;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
-import android.animation.TimeInterpolator;
-import android.animation.ValueAnimator;
import android.content.res.Resources;
import android.util.Log;
import android.view.View;
-import android.view.animation.AccelerateInterpolator;
import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.anim.AnimationLayerSet;
-import com.android.launcher3.anim.CircleRevealOutlineProvider;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.Thunk;
-import com.android.launcher3.widget.WidgetsContainerView;
/**
* TODO: figure out what kind of tests we can write for this
@@ -79,41 +72,8 @@ import com.android.launcher3.widget.WidgetsContainerView;
*/
public class LauncherStateTransitionAnimation {
- /**
- * animation used for the widget tray
- */
- public static final int CIRCULAR_REVEAL = 0;
- /**
- * animation used for all apps tray
- */
- public static final int PULLUP = 1;
-
- private static final float FINAL_REVEAL_ALPHA_FOR_WIDGETS = 0.3f;
-
- /**
- * Private callbacks made during transition setup.
- */
- private static class PrivateTransitionCallbacks {
- private final float materialRevealViewFinalAlpha;
-
- PrivateTransitionCallbacks(float revealAlpha) {
- materialRevealViewFinalAlpha = revealAlpha;
- }
-
- float getMaterialRevealViewStartFinalRadius() {
- return 0;
- }
- AnimatorListenerAdapter getMaterialRevealViewAnimatorListener(View revealView,
- View buttonView) {
- return null;
- }
- void onTransitionComplete() {}
- }
-
public static final String TAG = "LSTAnimation";
- public static final int SINGLE_FRAME_DELAY = 16;
-
@Thunk Launcher mLauncher;
@Thunk AnimatorSet mCurrentAnimation;
AllAppsTransitionController mAllAppsController;
@@ -128,49 +88,63 @@ public class LauncherStateTransitionAnimation {
*/
public void startAnimationToAllApps(final boolean animated) {
final AllAppsContainerView toView = mLauncher.getAppsView();
- final View buttonView = mLauncher.getStartViewForAllAppsRevealAnimation();
- PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks(1f) {
- @Override
- public float getMaterialRevealViewStartFinalRadius() {
- int allAppsButtonSize = mLauncher.getDeviceProfile().allAppsButtonVisualSize;
- return allAppsButtonSize / 2;
- }
- @Override
- public AnimatorListenerAdapter getMaterialRevealViewAnimatorListener(
- final View revealView, final View allAppsButtonView) {
- return new AnimatorListenerAdapter() {
- public void onAnimationStart(Animator animation) {
- allAppsButtonView.setVisibility(View.INVISIBLE);
- }
- public void onAnimationEnd(Animator animation) {
- allAppsButtonView.setVisibility(View.VISIBLE);
- }
- };
- }
+
+ final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet();
+ final Resources res = mLauncher.getResources();
+ final int revealDurationSlide = res.getInteger(R.integer.config_overlaySlideRevealTime);
+
+ final AnimationLayerSet layerViews = new AnimationLayerSet();
+
+ // If for some reason our views aren't initialized, don't animate
+ boolean initialized = toView != null;
+
+ // Cancel the current animation
+ cancelAnimation();
+
+ playCommonTransitionAnimations(Workspace.State.NORMAL_HIDDEN,
+ animated, initialized, animation, layerViews);
+ if (!animated || !initialized) {
+ mAllAppsController.finishPullUp();
+ toView.setTranslationX(0.0f);
+ toView.setTranslationY(0.0f);
+ toView.setScaleX(1.0f);
+ toView.setScaleY(1.0f);
+ toView.setAlpha(1.0f);
+ toView.setVisibility(View.VISIBLE);
+
+ mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
+ return;
+ }
+ if (!FeatureFlags.LAUNCHER3_PHYSICS) {
+ // We are animating the content view alpha, so ensure we have a layer for it.
+ layerViews.addView(toView);
+ }
+
+ animation.addListener(new AnimatorListenerAdapter() {
@Override
- void onTransitionComplete() {
+ public void onAnimationEnd(Animator animation) {
+ cleanupAnimation();
mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
}
- };
- // Only animate the search bar if animating from spring loaded mode back to all apps
- startAnimationToOverlay(
- Workspace.State.NORMAL_HIDDEN, buttonView, toView, animated, PULLUP, cb);
+ });
+ boolean shouldPost = mAllAppsController.animateToAllApps(animation, revealDurationSlide);
+
+ Runnable startAnimRunnable = new StartAnimRunnable(animation, toView);
+ mCurrentAnimation = animation;
+ mCurrentAnimation.addListener(layerViews);
+ if (shouldPost) {
+ toView.post(startAnimRunnable);
+ } else {
+ startAnimRunnable.run();
+ }
}
/**
* Starts an animation to the widgets view.
*/
public void startAnimationToWidgets(final boolean animated) {
- final WidgetsContainerView toView = mLauncher.getWidgetsView();
- final View buttonView = mLauncher.getWidgetsButton();
- startAnimationToOverlay(
- Workspace.State.OVERVIEW_HIDDEN, buttonView, toView, animated, CIRCULAR_REVEAL,
- new PrivateTransitionCallbacks(FINAL_REVEAL_ALPHA_FOR_WIDGETS){
- @Override
- void onTransitionComplete() {
- mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
- }
- });
+ // TODO: Remove this
+ throw new RuntimeException("This cannot happen");
}
/**
@@ -188,10 +162,6 @@ public class LauncherStateTransitionAnimation {
if (fromState == Launcher.State.APPS || fromState == Launcher.State.APPS_SPRING_LOADED
|| mAllAppsController.isTransitioning()) {
startAnimationToWorkspaceFromAllApps(fromWorkspaceState, toWorkspaceState,
- animated, PULLUP, onCompleteRunnable);
- } else if (fromState == Launcher.State.WIDGETS ||
- fromState == Launcher.State.WIDGETS_SPRING_LOADED) {
- startAnimationToWorkspaceFromWidgets(fromWorkspaceState, toWorkspaceState,
animated, onCompleteRunnable);
} else {
startAnimationToNewWorkspaceState(fromWorkspaceState, toWorkspaceState,
@@ -200,159 +170,6 @@ public class LauncherStateTransitionAnimation {
}
/**
- * Creates and starts a new animation to a particular overlay view.
- */
- private void startAnimationToOverlay(
- final Workspace.State toWorkspaceState,
- final View buttonView, final BaseContainerView toView,
- final boolean animated, int animType, final PrivateTransitionCallbacks pCb) {
- final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet();
- final Resources res = mLauncher.getResources();
- final int revealDuration = res.getInteger(R.integer.config_overlayRevealTime);
- final int revealDurationSlide = res.getInteger(R.integer.config_overlaySlideRevealTime);
-
- final int itemsAlphaStagger = res.getInteger(R.integer.config_overlayItemsAlphaStagger);
-
- final AnimationLayerSet layerViews = new AnimationLayerSet();
-
- // If for some reason our views aren't initialized, don't animate
- boolean initialized = buttonView != null;
-
- // Cancel the current animation
- cancelAnimation();
-
- final View contentView = toView.getContentView();
- playCommonTransitionAnimations(toWorkspaceState,
- animated, initialized, animation, layerViews);
- if (!animated || !initialized) {
- if (toWorkspaceState == Workspace.State.NORMAL_HIDDEN) {
- mAllAppsController.finishPullUp();
- }
- toView.setTranslationX(0.0f);
- toView.setTranslationY(0.0f);
- toView.setScaleX(1.0f);
- toView.setScaleY(1.0f);
- toView.setAlpha(1.0f);
- toView.setVisibility(View.VISIBLE);
-
- // Show the content view
- contentView.setVisibility(View.VISIBLE);
- pCb.onTransitionComplete();
- return;
- }
- if (animType == CIRCULAR_REVEAL) {
- // Setup the reveal view animation
- final View revealView = toView.getRevealView();
-
- int width = revealView.getMeasuredWidth();
- int height = revealView.getMeasuredHeight();
- float revealRadius = (float) Math.hypot(width / 2, height / 2);
- revealView.setVisibility(View.VISIBLE);
- revealView.setAlpha(0f);
- revealView.setTranslationY(0f);
- revealView.setTranslationX(0f);
-
- // Calculate the final animation values
- int[] buttonViewToPanelDelta =
- Utilities.getCenterDeltaInScreenSpace(revealView, buttonView);
- final float revealViewToAlpha = pCb.materialRevealViewFinalAlpha;
- final float revealViewToXDrift = buttonViewToPanelDelta[0];
- final float revealViewToYDrift = buttonViewToPanelDelta[1];
-
- // Create the animators
- PropertyValuesHolder panelAlpha =
- PropertyValuesHolder.ofFloat(View.ALPHA, revealViewToAlpha, 1f);
- PropertyValuesHolder panelDriftY =
- PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, revealViewToYDrift, 0);
- PropertyValuesHolder panelDriftX =
- PropertyValuesHolder.ofFloat(View.TRANSLATION_X, revealViewToXDrift, 0);
- ObjectAnimator panelAlphaAndDrift = ObjectAnimator.ofPropertyValuesHolder(revealView,
- panelAlpha, panelDriftY, panelDriftX);
- panelAlphaAndDrift.setDuration(revealDuration);
- panelAlphaAndDrift.setInterpolator(new LogDecelerateInterpolator(100, 0));
-
- // Play the animation
- layerViews.addView(revealView);
- animation.play(panelAlphaAndDrift);
-
- // Setup the animation for the content view
- contentView.setVisibility(View.VISIBLE);
- contentView.setAlpha(0f);
- contentView.setTranslationY(revealViewToYDrift);
- layerViews.addView(contentView);
-
- // Create the individual animators
- ObjectAnimator pageDrift = ObjectAnimator.ofFloat(contentView, "translationY",
- revealViewToYDrift, 0);
- pageDrift.setDuration(revealDuration);
- pageDrift.setInterpolator(new LogDecelerateInterpolator(100, 0));
- pageDrift.setStartDelay(itemsAlphaStagger);
- animation.play(pageDrift);
-
- ObjectAnimator itemsAlpha = ObjectAnimator.ofFloat(contentView, "alpha", 0f, 1f);
- itemsAlpha.setDuration(revealDuration);
- itemsAlpha.setInterpolator(new AccelerateInterpolator(1.5f));
- itemsAlpha.setStartDelay(itemsAlphaStagger);
- animation.play(itemsAlpha);
-
- float startRadius = pCb.getMaterialRevealViewStartFinalRadius();
- AnimatorListenerAdapter listener = pCb.getMaterialRevealViewAnimatorListener(
- revealView, buttonView);
- Animator reveal = new CircleRevealOutlineProvider(width / 2, height / 2,
- startRadius, revealRadius).createRevealAnimator(revealView);
- reveal.setDuration(revealDuration);
- reveal.setInterpolator(new LogDecelerateInterpolator(100, 0));
- if (listener != null) {
- reveal.addListener(listener);
- }
- animation.play(reveal);
-
- animation.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- // Hide the reveal view
- revealView.setVisibility(View.INVISIBLE);
-
- // This can hold unnecessary references to views.
- cleanupAnimation();
- pCb.onTransitionComplete();
- }
-
- });
-
- toView.bringToFront();
- toView.setVisibility(View.VISIBLE);
-
- animation.addListener(layerViews);
- toView.post(new StartAnimRunnable(animation, toView));
- mCurrentAnimation = animation;
- } else if (animType == PULLUP) {
- if (!FeatureFlags.LAUNCHER3_PHYSICS) {
- // We are animating the content view alpha, so ensure we have a layer for it.
- layerViews.addView(contentView);
- }
-
- animation.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- cleanupAnimation();
- pCb.onTransitionComplete();
- }
- });
- boolean shouldPost = mAllAppsController.animateToAllApps(animation, revealDurationSlide);
-
- Runnable startAnimRunnable = new StartAnimRunnable(animation, toView);
- mCurrentAnimation = animation;
- mCurrentAnimation.addListener(layerViews);
- if (shouldPost) {
- toView.post(startAnimRunnable);
- } else {
- startAnimRunnable.run();
- }
- }
- }
-
- /**
* Plays animations used by various transitions.
*/
private void playCommonTransitionAnimations(
@@ -376,139 +193,20 @@ public class LauncherStateTransitionAnimation {
* Starts an animation to the workspace from the apps view.
*/
private void startAnimationToWorkspaceFromAllApps(final Workspace.State fromWorkspaceState,
- final Workspace.State toWorkspaceState, final boolean animated, int type,
- final Runnable onCompleteRunnable) {
- // No alpha anim from all apps
- PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks(1f) {
- @Override
- float getMaterialRevealViewStartFinalRadius() {
- int allAppsButtonSize = mLauncher.getDeviceProfile().allAppsButtonVisualSize;
- return allAppsButtonSize / 2;
- }
- @Override
- public AnimatorListenerAdapter getMaterialRevealViewAnimatorListener(
- final View revealView, final View allAppsButtonView) {
- return new AnimatorListenerAdapter() {
- public void onAnimationStart(Animator animation) {
- // We set the alpha instead of visibility to ensure that the focus does not
- // get taken from the all apps view
- allAppsButtonView.setVisibility(View.VISIBLE);
- allAppsButtonView.setAlpha(0f);
- }
- public void onAnimationEnd(Animator animation) {
- // Hide the reveal view
- revealView.setVisibility(View.INVISIBLE);
-
- // Show the all apps button, and focus it
- allAppsButtonView.setAlpha(1f);
- }
- };
- }
- @Override
- void onTransitionComplete() {
- mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
- }
- };
- // Only animate the search bar if animating to spring loaded mode from all apps
- startAnimationToWorkspaceFromOverlay(fromWorkspaceState, toWorkspaceState,
- mLauncher.getStartViewForAllAppsRevealAnimation(), mLauncher.getAppsView(),
- animated, type, onCompleteRunnable, cb);
- }
-
- /**
- * Starts an animation to the workspace from the widgets view.
- */
- private void startAnimationToWorkspaceFromWidgets(final Workspace.State fromWorkspaceState,
final Workspace.State toWorkspaceState, final boolean animated,
final Runnable onCompleteRunnable) {
- final WidgetsContainerView widgetsView = mLauncher.getWidgetsView();
- PrivateTransitionCallbacks cb =
- new PrivateTransitionCallbacks(FINAL_REVEAL_ALPHA_FOR_WIDGETS) {
- @Override
- public AnimatorListenerAdapter getMaterialRevealViewAnimatorListener(
- final View revealView, final View widgetsButtonView) {
- return new AnimatorListenerAdapter() {
- public void onAnimationEnd(Animator animation) {
- // Hide the reveal view
- revealView.setVisibility(View.INVISIBLE);
- }
- };
- }
- @Override
- void onTransitionComplete() {
- mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
- }
- };
- startAnimationToWorkspaceFromOverlay(
- fromWorkspaceState, toWorkspaceState,
- mLauncher.getWidgetsButton(), widgetsView,
- animated, CIRCULAR_REVEAL, onCompleteRunnable, cb);
- }
-
- /**
- * Starts an animation to the workspace from another workspace state, e.g. normal to overview.
- */
- private void startAnimationToNewWorkspaceState(final Workspace.State fromWorkspaceState,
- final Workspace.State toWorkspaceState, final boolean animated,
- final Runnable onCompleteRunnable) {
- final View fromWorkspace = mLauncher.getWorkspace();
- final AnimationLayerSet layerViews = new AnimationLayerSet();
- final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet();
-
- // Cancel the current animation
- cancelAnimation();
-
- playCommonTransitionAnimations(toWorkspaceState, animated, animated, animation, layerViews);
- mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
-
- if (animated) {
- animation.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- // Run any queued runnables
- if (onCompleteRunnable != null) {
- onCompleteRunnable.run();
- }
-
- // This can hold unnecessary references to views.
- cleanupAnimation();
- }
- });
- animation.addListener(layerViews);
- fromWorkspace.post(new StartAnimRunnable(animation, null));
- mCurrentAnimation = animation;
- } else /* if (!animated) */ {
- // Run any queued runnables
- if (onCompleteRunnable != null) {
- onCompleteRunnable.run();
- }
-
- mCurrentAnimation = null;
- }
- }
- /**
- * Creates and starts a new animation to the workspace.
- */
- private void startAnimationToWorkspaceFromOverlay(
- final Workspace.State fromWorkspaceState, final Workspace.State toWorkspaceState,
- final View buttonView, final BaseContainerView fromView,
- final boolean animated, int animType, final Runnable onCompleteRunnable,
- final PrivateTransitionCallbacks pCb) {
+ final AllAppsContainerView fromView = mLauncher.getAppsView();
final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet();
final Resources res = mLauncher.getResources();
- final int revealDuration = res.getInteger(R.integer.config_overlayRevealTime);
final int revealDurationSlide = res.getInteger(R.integer.config_overlaySlideRevealTime);
- final int itemsAlphaStagger = res.getInteger(R.integer.config_overlayItemsAlphaStagger);
final View toView = mLauncher.getWorkspace();
- final View revealView = fromView.getRevealView();
- final View contentView = fromView.getContentView();
final AnimationLayerSet layerViews = new AnimationLayerSet();
// If for some reason our views aren't initialized, don't animate
- boolean initialized = buttonView != null;
+ boolean initialized = fromView != null;
// Cancel the current animation
cancelAnimation();
@@ -520,7 +218,7 @@ public class LauncherStateTransitionAnimation {
mAllAppsController.finishPullDown();
}
fromView.setVisibility(View.GONE);
- pCb.onTransitionComplete();
+ mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
// Run any queued runnables
if (onCompleteRunnable != null) {
@@ -528,156 +226,82 @@ public class LauncherStateTransitionAnimation {
}
return;
}
- if (animType == CIRCULAR_REVEAL) {
- // hideAppsCustomizeHelper is called in some cases when it is already hidden
- // don't perform all these no-op animations. In particularly, this was causing
- // the all-apps button to pop in and out.
- if (fromView.getVisibility() == View.VISIBLE) {
- int width = revealView.getMeasuredWidth();
- int height = revealView.getMeasuredHeight();
- float revealRadius = (float) Math.hypot(width / 2, height / 2);
- revealView.setVisibility(View.VISIBLE);
- revealView.setAlpha(1f);
- revealView.setTranslationY(0);
- layerViews.addView(revealView);
-
- // Calculate the final animation values
- int[] buttonViewToPanelDelta = Utilities.getCenterDeltaInScreenSpace(revealView, buttonView);
- final float revealViewToXDrift = buttonViewToPanelDelta[0];
- final float revealViewToYDrift = buttonViewToPanelDelta[1];
-
- // The vertical motion of the apps panel should be delayed by one frame
- // from the conceal animation in order to give the right feel. We correspondingly
- // shorten the duration so that the slide and conceal end at the same time.
- TimeInterpolator decelerateInterpolator = new LogDecelerateInterpolator(100, 0);
- ObjectAnimator panelDriftY = ObjectAnimator.ofFloat(revealView, "translationY",
- 0, revealViewToYDrift);
- panelDriftY.setDuration(revealDuration - SINGLE_FRAME_DELAY);
- panelDriftY.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
- panelDriftY.setInterpolator(decelerateInterpolator);
- animation.play(panelDriftY);
-
- ObjectAnimator panelDriftX = ObjectAnimator.ofFloat(revealView, "translationX",
- 0, revealViewToXDrift);
- panelDriftX.setDuration(revealDuration - SINGLE_FRAME_DELAY);
- panelDriftX.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
- panelDriftX.setInterpolator(decelerateInterpolator);
- animation.play(panelDriftX);
-
- // Setup animation for the reveal panel alpha
- if (pCb.materialRevealViewFinalAlpha != 1f) {
- ObjectAnimator panelAlpha = ObjectAnimator.ofFloat(revealView, "alpha",
- 1f, pCb.materialRevealViewFinalAlpha);
- panelAlpha.setDuration(revealDuration);
- panelAlpha.setInterpolator(decelerateInterpolator);
- animation.play(panelAlpha);
- }
- // Setup the animation for the content view
- layerViews.addView(contentView);
-
- // Create the individual animators
- ObjectAnimator pageDrift = ObjectAnimator.ofFloat(contentView, "translationY",
- 0, revealViewToYDrift);
- contentView.setTranslationY(0);
- pageDrift.setDuration(revealDuration - SINGLE_FRAME_DELAY);
- pageDrift.setInterpolator(decelerateInterpolator);
- pageDrift.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
- animation.play(pageDrift);
-
- contentView.setAlpha(1f);
- ObjectAnimator itemsAlpha = ObjectAnimator.ofFloat(contentView, "alpha", 1f, 0f);
- itemsAlpha.setDuration(100);
- itemsAlpha.setInterpolator(decelerateInterpolator);
- animation.play(itemsAlpha);
-
- // Invalidate the scrim throughout the animation to ensure the highlight
- // cutout is correct throughout.
- ValueAnimator invalidateScrim = ValueAnimator.ofFloat(0f, 1f);
- invalidateScrim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- mLauncher.getDragLayer().invalidateScrim();
- }
- });
- animation.play(invalidateScrim);
-
- // Animate the all apps button
- float finalRadius = pCb.getMaterialRevealViewStartFinalRadius();
- AnimatorListenerAdapter listener =
- pCb.getMaterialRevealViewAnimatorListener(revealView, buttonView);
- Animator reveal = new CircleRevealOutlineProvider(width / 2, height / 2,
- revealRadius, finalRadius).createRevealAnimator(revealView);
- reveal.setInterpolator(new LogDecelerateInterpolator(100, 0));
- reveal.setDuration(revealDuration);
- reveal.setStartDelay(itemsAlphaStagger);
- if (listener != null) {
- reveal.addListener(listener);
+ // We are animating the content view alpha, so ensure we have a layer for it
+ layerViews.addView(toView);
+
+ animation.addListener(new AnimatorListenerAdapter() {
+ boolean canceled = false;
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ canceled = true;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (canceled) return;
+ // Run any queued runnables
+ if (onCompleteRunnable != null) {
+ onCompleteRunnable.run();
}
- animation.play(reveal);
+
+ cleanupAnimation();
+ mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
}
- animation.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- fromView.setVisibility(View.GONE);
- // Run any queued runnables
- if (onCompleteRunnable != null) {
- onCompleteRunnable.run();
- }
+ });
+ boolean shouldPost = mAllAppsController.animateToWorkspace(animation, revealDurationSlide);
- // Reset page transforms
- if (contentView != null) {
- contentView.setTranslationX(0);
- contentView.setTranslationY(0);
- contentView.setAlpha(1);
- }
+ Runnable startAnimRunnable = new StartAnimRunnable(animation, toView);
+ mCurrentAnimation = animation;
+ mCurrentAnimation.addListener(layerViews);
+ if (shouldPost) {
+ fromView.post(startAnimRunnable);
+ } else {
+ startAnimRunnable.run();
+ }
+ }
- // This can hold unnecessary references to views.
- cleanupAnimation();
- pCb.onTransitionComplete();
- }
- });
+ /**
+ * Starts an animation to the workspace from another workspace state, e.g. normal to overview.
+ */
+ private void startAnimationToNewWorkspaceState(final Workspace.State fromWorkspaceState,
+ final Workspace.State toWorkspaceState, final boolean animated,
+ final Runnable onCompleteRunnable) {
+ final View fromWorkspace = mLauncher.getWorkspace();
+ final AnimationLayerSet layerViews = new AnimationLayerSet();
+ final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet();
- mCurrentAnimation = animation;
- mCurrentAnimation.addListener(layerViews);
- fromView.post(new StartAnimRunnable(animation, null));
- } else if (animType == PULLUP) {
- // We are animating the content view alpha, so ensure we have a layer for it
- layerViews.addView(contentView);
+ // Cancel the current animation
+ cancelAnimation();
- animation.addListener(new AnimatorListenerAdapter() {
- boolean canceled = false;
- @Override
- public void onAnimationCancel(Animator animation) {
- canceled = true;
- }
+ playCommonTransitionAnimations(toWorkspaceState, animated, animated, animation, layerViews);
+ mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
+ if (animated) {
+ animation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- if (canceled) return;
// Run any queued runnables
if (onCompleteRunnable != null) {
onCompleteRunnable.run();
}
+ // This can hold unnecessary references to views.
cleanupAnimation();
- pCb.onTransitionComplete();
}
-
});
- boolean shouldPost = mAllAppsController.animateToWorkspace(animation, revealDurationSlide);
-
- Runnable startAnimRunnable = new StartAnimRunnable(animation, toView);
+ animation.addListener(layerViews);
+ fromWorkspace.post(new StartAnimRunnable(animation, null));
mCurrentAnimation = animation;
- mCurrentAnimation.addListener(layerViews);
- if (shouldPost) {
- fromView.post(startAnimRunnable);
- } else {
- startAnimRunnable.run();
+ } else /* if (!animated) */ {
+ // Run any queued runnables
+ if (onCompleteRunnable != null) {
+ onCompleteRunnable.run();
}
+
+ mCurrentAnimation = null;
}
- return;
}
/**
diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java
index f150c89c1..bdfeae162 100644
--- a/src/com/android/launcher3/WidgetPreviewLoader.java
+++ b/src/com/android/launcher3/WidgetPreviewLoader.java
@@ -92,12 +92,11 @@ public class WidgetPreviewLoader {
* @return a request id which can be used to cancel the request.
*/
public CancellationSignal getPreview(WidgetItem item, int previewWidth,
- int previewHeight, WidgetCell caller, boolean animate) {
+ int previewHeight, WidgetCell caller) {
String size = previewWidth + "x" + previewHeight;
WidgetCacheKey key = new WidgetCacheKey(item.componentName, item.user, size);
- PreviewLoadTask task = new PreviewLoadTask(key, item, previewWidth, previewHeight, caller,
- animate);
+ PreviewLoadTask task = new PreviewLoadTask(key, item, previewWidth, previewHeight, caller);
task.executeOnExecutor(Utilities.THREAD_POOL_EXECUTOR);
CancellationSignal signal = new CancellationSignal();
@@ -527,19 +526,17 @@ public class WidgetPreviewLoader {
private final int mPreviewHeight;
private final int mPreviewWidth;
private final WidgetCell mCaller;
- private final boolean mAnimatePreviewIn;
private final BaseActivity mActivity;
@Thunk long[] mVersions;
@Thunk Bitmap mBitmapToRecycle;
PreviewLoadTask(WidgetCacheKey key, WidgetItem info, int previewWidth,
- int previewHeight, WidgetCell caller, boolean animate) {
+ int previewHeight, WidgetCell caller) {
mKey = key;
mInfo = info;
mPreviewHeight = previewHeight;
mPreviewWidth = previewWidth;
mCaller = caller;
- mAnimatePreviewIn = animate;
mActivity = BaseActivity.fromContext(mCaller.getContext());
if (DEBUG) {
Log.d(TAG, String.format("%s, %s, %d, %d",
@@ -595,7 +592,7 @@ public class WidgetPreviewLoader {
@Override
protected void onPostExecute(final Bitmap preview) {
- mCaller.applyPreview(preview, mAnimatePreviewIn);
+ mCaller.applyPreview(preview);
// Write the generated preview to the DB in the worker thread
if (mVersions != null) {
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index d63ae4189..91b196eea 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -16,10 +16,8 @@
package com.android.launcher3.allapps;
import android.content.Context;
-import android.graphics.Color;
+import android.graphics.Bitmap;
import android.graphics.Rect;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.InsetDrawable;
import android.support.v7.widget.LinearLayoutManager;
import android.text.Selection;
import android.text.SpannableStringBuilder;
@@ -28,10 +26,11 @@ import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.RelativeLayout;
import com.android.launcher3.AppInfo;
-import com.android.launcher3.BaseContainerView;
import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.ClickShadowView;
import com.android.launcher3.DeleteDropTarget;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.DragSource;
@@ -41,7 +40,6 @@ import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.PromiseAppInfo;
import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
import com.android.launcher3.anim.SpringAnimationHandler;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DragController;
@@ -49,9 +47,8 @@ import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.keyboard.FocusedItemDecorator;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
-import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.ComponentKeyMapper;
import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.util.TransformingTouchDelegate;
import java.util.List;
import java.util.Set;
@@ -59,13 +56,17 @@ import java.util.Set;
/**
* The all apps view container.
*/
-public class AllAppsContainerView extends BaseContainerView implements DragSource,
- View.OnLongClickListener, Insettable {
+public class AllAppsContainerView extends RelativeLayout implements DragSource,
+ View.OnLongClickListener, Insettable, DeviceProfile.LauncherLayoutChangeListener,
+ BubbleTextView.BubbleTextShadowHandler {
+
+ protected final Rect mBasePadding = new Rect();
private final Launcher mLauncher;
private final AlphabeticalAppsList mApps;
private final AllAppsGridAdapter mAdapter;
private final LinearLayoutManager mLayoutManager;
+ private final ClickShadowView mTouchFeedbackView;
private AllAppsRecyclerView mAppsRecyclerView;
private SearchUiManager mSearchUiManager;
@@ -78,6 +79,8 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
private SpringAnimationHandler mSpringAnimationHandler;
+ private TransformingTouchDelegate mTouchDelegate;
+
public AllAppsContainerView(Context context) {
this(context, null);
}
@@ -98,19 +101,69 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
mSearchQueryBuilder = new SpannableStringBuilder();
Selection.setSelection(mSearchQueryBuilder, 0);
+
+ mTouchFeedbackView = new ClickShadowView(context);
+ // Make the feedback view large enough to hold the blur bitmap.
+ int size = mLauncher.getDeviceProfile().allAppsIconSizePx
+ + mTouchFeedbackView.getExtraSize();
+ addView(mTouchFeedbackView, size, size);
}
@Override
- protected void updateBackground(
- int paddingLeft, int paddingTop, int paddingRight, int paddingBottom) {
- if (mLauncher.getDeviceProfile().isVerticalBarLayout()) {
- getRevealView().setBackground(new InsetDrawable(mBaseDrawable,
- paddingLeft, paddingTop, paddingRight, paddingBottom));
- getContentView().setBackground(
- new InsetDrawable(new ColorDrawable(Color.TRANSPARENT),
- paddingLeft, paddingTop, paddingRight, paddingBottom));
- } else {
- getRevealView().setBackground(mBaseDrawable);
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ DeviceProfile grid = Launcher.getLauncher(getContext()).getDeviceProfile();
+ grid.addLauncherLayoutChangedListener(this);
+
+ mTouchDelegate = new TransformingTouchDelegate(mAppsRecyclerView);
+ setTouchDelegate(mTouchDelegate);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+
+ DeviceProfile grid = Launcher.getLauncher(getContext()).getDeviceProfile();
+ grid.removeLauncherLayoutChangedListener(this);
+ }
+
+ /**
+ * Calculate the background padding as it can change due to insets/content padding change.
+ */
+ @Override
+ public void onLauncherLayoutChanged() {
+ DeviceProfile grid = mLauncher.getDeviceProfile();
+ if (!grid.isVerticalBarLayout()) {
+ return;
+ }
+
+ int[] padding = grid.getContainerPadding();
+ int paddingLeft = padding[0];
+ int paddingRight = padding[1];
+ mBasePadding.set(paddingLeft, 0, paddingRight, 0);
+ setPadding(paddingLeft, 0, paddingRight, 0);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ mTouchDelegate.setBounds(
+ mAppsRecyclerView.getLeft() - mBasePadding.left,
+ mAppsRecyclerView.getTop() - mBasePadding.top,
+ mAppsRecyclerView.getRight() + mBasePadding.right,
+ mAppsRecyclerView.getBottom() + mBasePadding.bottom);
+ }
+
+ @Override
+ public void setPressedIcon(BubbleTextView icon, Bitmap background) {
+ if (icon == null || background == null) {
+ mTouchFeedbackView.setBitmap(null);
+ mTouchFeedbackView.animate().cancel();
+ } else if (mTouchFeedbackView.setBitmap(background)) {
+ View rv = findViewById(R.id.apps_list_view);
+ mTouchFeedbackView.alignWithIconView(icon, (ViewGroup) icon.getParent(), rv);
+ mTouchFeedbackView.animateShadow();
}
}
@@ -157,23 +210,7 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
if (mLauncher.getDragLayer().isEventOverView(mSearchContainer, ev)) {
return true;
}
-
- int[] point = new int[2];
- point[0] = (int) ev.getX();
- point[1] = (int) ev.getY();
- Utilities.mapCoordInSelfToDescendant(
- mAppsRecyclerView.getScrollBar(), mLauncher.getDragLayer(), point);
- // IF the MotionEvent is inside the thumb, container should not be pulled down.
- if (mAppsRecyclerView.getScrollBar().shouldBlockIntercept(point[0], point[1])) {
- return false;
- }
-
- // IF scroller is at the very top OR there is no scroll bar because there is probably not
- // enough items to scroll, THEN it's okay for the container to be pulled down.
- if (mAppsRecyclerView.getCurrentScrollY() == 0) {
- return true;
- }
- return false;
+ return mAppsRecyclerView.shouldContainerScroll(ev, mLauncher.getDragLayer());
}
/**
@@ -191,7 +228,7 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
// This is a focus listener that proxies focus from a view into the list view. This is to
// work around the search box from getting first focus and showing the cursor.
- getContentView().setOnFocusChangeListener(new View.OnFocusChangeListener() {
+ setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
@@ -222,9 +259,7 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
mAppsRecyclerView.preMeasureViews(mAdapter);
mAdapter.setIconFocusListener(focusedItemDecorator.getFocusListener());
- getRevealView().setVisibility(View.VISIBLE);
- getContentView().setVisibility(View.VISIBLE);
- getContentView().setBackground(null);
+ onLauncherLayoutChanged();
}
public SearchUiManager getSearchUiManager() {
@@ -232,11 +267,6 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
}
@Override
- public View getTouchDelegateTargetView() {
- return mAppsRecyclerView;
- }
-
- @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
DeviceProfile grid = mLauncher.getDeviceProfile();
// Update the number of items in the grid before we measure the view
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java
deleted file mode 100644
index 517dc947e..000000000
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java
+++ /dev/null
@@ -1,73 +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.allapps;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.RelativeLayout;
-
-import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.BubbleTextView.BubbleTextShadowHandler;
-import com.android.launcher3.ClickShadowView;
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.R;
-
-/**
- * A container for RecyclerView to allow for the click shadow view to be shown behind an icon that
- * is launching.
- */
-public class AllAppsRecyclerViewContainerView extends RelativeLayout
- implements BubbleTextShadowHandler {
-
- private final ClickShadowView mTouchFeedbackView;
-
- public AllAppsRecyclerViewContainerView(Context context) {
- this(context, null);
- }
-
- public AllAppsRecyclerViewContainerView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public AllAppsRecyclerViewContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
-
- Launcher launcher = Launcher.getLauncher(context);
- DeviceProfile grid = launcher.getDeviceProfile();
-
- mTouchFeedbackView = new ClickShadowView(context);
-
- // Make the feedback view large enough to hold the blur bitmap.
- int size = grid.allAppsIconSizePx + mTouchFeedbackView.getExtraSize();
- addView(mTouchFeedbackView, size, size);
- }
-
- @Override
- public void setPressedIcon(BubbleTextView icon, Bitmap background) {
- if (icon == null || background == null) {
- mTouchFeedbackView.setBitmap(null);
- mTouchFeedbackView.animate().cancel();
- } else if (mTouchFeedbackView.setBitmap(background)) {
- View rv = findViewById(R.id.apps_list_view);
- mTouchFeedbackView.alignWithIconView(icon, (ViewGroup) icon.getParent(), rv);
- mTouchFeedbackView.animateShadow();
- }
- }
-}
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index b844ba303..bb0822f95 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -294,7 +294,7 @@ public class AllAppsTransitionController implements TouchController, SwipeDetect
float hotseatAlpha = mHotseatAccelInterpolator.getInterpolation(workspaceHotseatAlpha);
updateAllAppsBg(alpha);
- mAppsView.getContentView().setAlpha(alpha);
+ mAppsView.setAlpha(alpha);
mAppsView.setTranslationY(shiftCurrent);
if (!mLauncher.getDeviceProfile().isVerticalBarLayout()) {
diff --git a/src/com/android/launcher3/anim/CircleRevealOutlineProvider.java b/src/com/android/launcher3/anim/CircleRevealOutlineProvider.java
deleted file mode 100644
index 9fb6b498b..000000000
--- a/src/com/android/launcher3/anim/CircleRevealOutlineProvider.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.anim;
-
-public class CircleRevealOutlineProvider extends RevealOutlineAnimation {
-
- private int mCenterX;
- private int mCenterY;
- private float mRadius0;
- private float mRadius1;
-
- /**
- * @param x reveal center x
- * @param y reveal center y
- * @param r0 initial radius
- * @param r1 final radius
- */
- public CircleRevealOutlineProvider(int x, int y, float r0, float r1) {
- mCenterX = x;
- mCenterY = y;
- mRadius0 = r0;
- mRadius1 = r1;
- }
-
- @Override
- public boolean shouldRemoveElevationDuringAnimation() {
- return true;
- }
-
- @Override
- public void setProgress(float progress) {
- mOutlineRadius = (1 - progress) * mRadius0 + progress * mRadius1;
-
- mOutline.left = (int) (mCenterX - mOutlineRadius);
- mOutline.top = (int) (mCenterY - mOutlineRadius);
- mOutline.right = (int) (mCenterX + mOutlineRadius);
- mOutline.bottom = (int) (mCenterY + mOutlineRadius);
- }
-}
diff --git a/src/com/android/launcher3/model/BaseModelUpdateTask.java b/src/com/android/launcher3/model/BaseModelUpdateTask.java
index d5b5aa7cf..9aa30e7cc 100644
--- a/src/com/android/launcher3/model/BaseModelUpdateTask.java
+++ b/src/com/android/launcher3/model/BaseModelUpdateTask.java
@@ -28,6 +28,8 @@ import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.MultiHashMap;
+import com.android.launcher3.widget.WidgetListRowEntry;
+import com.android.launcher3.widget.WidgetsListAdapter;
import java.util.ArrayList;
import java.util.concurrent.Executor;
@@ -117,8 +119,8 @@ public abstract class BaseModelUpdateTask implements ModelUpdateTask {
}
public void bindUpdatedWidgets(BgDataModel dataModel) {
- final MultiHashMap<PackageItemInfo, WidgetItem> widgets
- = dataModel.widgetsModel.getWidgetsMap();
+ final ArrayList<WidgetListRowEntry> widgets =
+ dataModel.widgetsModel.getWidgetsList(mApp.getContext());
scheduleCallbackTask(new CallbackTask() {
@Override
public void execute(Callbacks callbacks) {
diff --git a/src/com/android/launcher3/model/LoaderResults.java b/src/com/android/launcher3/model/LoaderResults.java
index b7a6b68e8..24e5b9c58 100644
--- a/src/com/android/launcher3/model/LoaderResults.java
+++ b/src/com/android/launcher3/model/LoaderResults.java
@@ -36,6 +36,8 @@ import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.LooperIdleLock;
import com.android.launcher3.util.MultiHashMap;
import com.android.launcher3.util.ViewOnDrawExecutor;
+import com.android.launcher3.widget.WidgetListRowEntry;
+import com.android.launcher3.widget.WidgetsListAdapter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -362,8 +364,8 @@ public class LoaderResults {
}
public void bindWidgets() {
- final MultiHashMap<PackageItemInfo, WidgetItem> widgets
- = mBgDataModel.widgetsModel.getWidgetsMap();
+ final ArrayList<WidgetListRowEntry> widgets =
+ mBgDataModel.widgetsModel.getWidgetsList(mApp.getContext());
Runnable r = new Runnable() {
public void run() {
Callbacks callbacks = mCallbacks.get();
diff --git a/src/com/android/launcher3/model/WidgetsModel.java b/src/com/android/launcher3/model/WidgetsModel.java
index ed900bf35..1ff0daca0 100644
--- a/src/com/android/launcher3/model/WidgetsModel.java
+++ b/src/com/android/launcher3/model/WidgetsModel.java
@@ -15,6 +15,7 @@ import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.Utilities;
+import com.android.launcher3.compat.AlphabeticIndexCompat;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.ShortcutConfigActivityInfo;
@@ -22,10 +23,14 @@ import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.MultiHashMap;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.Preconditions;
+import com.android.launcher3.widget.WidgetItemComparator;
+import com.android.launcher3.widget.WidgetListRowEntry;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
+import java.util.Map;
/**
* Widgets data model that is used by the adapters of the widget views and controllers.
@@ -42,8 +47,26 @@ public class WidgetsModel {
private AppFilter mAppFilter;
- public synchronized MultiHashMap<PackageItemInfo, WidgetItem> getWidgetsMap() {
- return mWidgetsList.clone();
+ /**
+ * Returns a list of {@link WidgetListRowEntry}. All {@link WidgetItem} in a single row
+ * are sorted (based on label and user), but the overall list of {@link WidgetListRowEntry}s
+ * is not sorted. This list is sorted at the UI when using
+ * {@link com.android.launcher3.widget.WidgetsDiffReporter}
+ *
+ * @see com.android.launcher3.widget.WidgetsListAdapter#setWidgets(ArrayList)
+ */
+ public synchronized ArrayList<WidgetListRowEntry> getWidgetsList(Context context) {
+ ArrayList<WidgetListRowEntry> result = new ArrayList<>();
+ AlphabeticIndexCompat indexer = new AlphabeticIndexCompat(context);
+
+ WidgetItemComparator widgetComparator = new WidgetItemComparator();
+ for (Map.Entry<PackageItemInfo, ArrayList<WidgetItem>> entry : mWidgetsList.entrySet()) {
+ WidgetListRowEntry row = new WidgetListRowEntry(entry.getKey(), entry.getValue());
+ row.titleSectionName = indexer.computeSectionName(row.pkgItem.title);
+ Collections.sort(row.widgets, widgetComparator);
+ result.add(row);
+ }
+ return result;
}
/**
diff --git a/src/com/android/launcher3/popup/PopupDataProvider.java b/src/com/android/launcher3/popup/PopupDataProvider.java
index aeb713479..070ac3971 100644
--- a/src/com/android/launcher3/popup/PopupDataProvider.java
+++ b/src/com/android/launcher3/popup/PopupDataProvider.java
@@ -25,6 +25,8 @@ import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.Utilities;
import com.android.launcher3.badge.BadgeInfo;
+import com.android.launcher3.model.PackageItemInfo;
+import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.notification.NotificationInfo;
import com.android.launcher3.notification.NotificationKeyData;
import com.android.launcher3.notification.NotificationListener;
@@ -32,6 +34,7 @@ import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.MultiHashMap;
import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.widget.WidgetListRowEntry;
import java.util.ArrayList;
import java.util.Collections;
@@ -62,6 +65,8 @@ public class PopupDataProvider implements NotificationListener.NotificationsChan
private MultiHashMap<ComponentKey, String> mDeepShortcutMap = new MultiHashMap<>();
/** Maps packages to their BadgeInfo's . */
private Map<PackageUserKey, BadgeInfo> mPackageUserToBadgeInfos = new HashMap<>();
+ /** Maps packages to their Widgets */
+ private ArrayList<WidgetListRowEntry> mAllWidgets = new ArrayList<>();
public PopupDataProvider(Launcher launcher) {
mLauncher = launcher;
@@ -265,4 +270,29 @@ public class PopupDataProvider implements NotificationListener.NotificationsChan
}
notificationListener.cancelNotification(notificationKey);
}
+
+ public void setAllWidgets(ArrayList<WidgetListRowEntry> allWidgets) {
+ mAllWidgets = allWidgets;
+ }
+
+ public ArrayList<WidgetListRowEntry> getAllWidgets() {
+ return mAllWidgets;
+ }
+
+ public List<WidgetItem> getWidgetsForPackageUser(PackageUserKey packageUserKey) {
+ for (WidgetListRowEntry entry : mAllWidgets) {
+ 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;
+ }
}
diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java
index 3f7bf4214..e709b9368 100644
--- a/src/com/android/launcher3/popup/SystemShortcut.java
+++ b/src/com/android/launcher3/popup/SystemShortcut.java
@@ -59,8 +59,9 @@ public abstract class SystemShortcut extends ItemInfo {
@Override
public View.OnClickListener getOnClickListener(final Launcher launcher,
final ItemInfo itemInfo) {
- final List<WidgetItem> widgets = launcher.getWidgetsForPackageUser(new PackageUserKey(
- itemInfo.getTargetComponent().getPackageName(), itemInfo.user));
+ final List<WidgetItem> widgets =
+ launcher.getPopupDataProvider().getWidgetsForPackageUser(new PackageUserKey(
+ itemInfo.getTargetComponent().getPackageName(), itemInfo.user));
if (widgets == null) {
return null;
}
diff --git a/src/com/android/launcher3/views/RecyclerViewFastScroller.java b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
index 7b5bcdbd4..8c9a44186 100644
--- a/src/com/android/launcher3/views/RecyclerViewFastScroller.java
+++ b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
@@ -285,7 +285,7 @@ public class RecyclerViewFastScroller extends View {
return;
}
int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
- canvas.translate(getWidth() / 2, mRv.getPaddingTop());
+ canvas.translate(getWidth() / 2, mRv.getScrollBarTop());
// Draw the track
float halfW = mWidth / 2;
canvas.drawRoundRect(-halfW, 0, halfW, mRv.getScrollbarTrackHeight(),
@@ -317,7 +317,7 @@ public class RecyclerViewFastScroller extends View {
* Returns whether the specified point is inside the thumb bounds.
*/
private boolean isNearThumb(int x, int y) {
- int offset = y - mRv.getPaddingTop() - mThumbOffsetY;
+ int offset = y - mRv.getScrollBarTop() - mThumbOffsetY;
return x >= 0 && x < getWidth() && offset >= 0 && offset <= mThumbHeight;
}
@@ -348,7 +348,7 @@ public class RecyclerViewFastScroller extends View {
private void updatePopupY(int lastTouchY) {
int height = mPopupView.getHeight();
float top = lastTouchY - (FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR * height)
- + mRv.getPaddingTop();
+ + mRv.getScrollBarTop();
top = Utilities.boundToRange(top,
mMaxWidth, mRv.getScrollbarTrackHeight() - mMaxWidth - height);
mPopupView.setTranslationY(top);
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
diff --git a/tests/src/com/android/launcher3/widget/WidgetsListAdapterTest.java b/tests/src/com/android/launcher3/widget/WidgetsListAdapterTest.java
index 40b65e4fb..0185f130e 100644
--- a/tests/src/com/android/launcher3/widget/WidgetsListAdapterTest.java
+++ b/tests/src/com/android/launcher3/widget/WidgetsListAdapterTest.java
@@ -22,13 +22,13 @@ import android.graphics.Bitmap;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import com.android.launcher3.IconCache;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.WidgetPreviewLoader;
-import com.android.launcher3.compat.AlphabeticIndexCompat;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.model.PackageItemInfo;
import com.android.launcher3.model.WidgetItem;
@@ -40,8 +40,11 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
+import java.util.ArrayList;
+import java.util.Map;
+
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.isNull;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -49,15 +52,12 @@ import static org.mockito.Mockito.verify;
@RunWith(AndroidJUnit4.class)
public class WidgetsListAdapterTest {
- private final String TAG = "WidgetsListAdapterTest";
-
@Mock private LayoutInflater mMockLayoutInflater;
@Mock private WidgetPreviewLoader mMockWidgetCache;
- @Mock private WidgetsDiffReporter.NotifyListener mListener;
+ @Mock private RecyclerView.AdapterDataObserver mListener;
@Mock private IconCache mIconCache;
private WidgetsListAdapter mAdapter;
- private AlphabeticIndexCompat mIndexCompat;
private InvariantDeviceProfile mTestProfile;
private Context mContext;
@@ -68,41 +68,39 @@ public class WidgetsListAdapterTest {
mTestProfile = new InvariantDeviceProfile();
mTestProfile.numRows = 5;
mTestProfile.numColumns = 5;
- mIndexCompat = new AlphabeticIndexCompat(mContext);
- WidgetsDiffReporter reporter = new WidgetsDiffReporter(mIconCache);
- reporter.setListener(mListener);
mAdapter = new WidgetsListAdapter(mContext, mMockLayoutInflater, mMockWidgetCache,
- mIndexCompat, null, null, reporter);
+ mIconCache, null, null);
+ mAdapter.registerAdapterDataObserver(mListener);
}
@Test
public void test_notifyDataSetChanged() throws Exception {
mAdapter.setWidgets(generateSampleMap(1));
- verify(mListener, times(1)).notifyDataSetChanged();
+ verify(mListener, times(1)).onChanged();
}
@Test
public void test_notifyItemInserted() throws Exception {
mAdapter.setWidgets(generateSampleMap(1));
mAdapter.setWidgets(generateSampleMap(2));
- verify(mListener, times(1)).notifyDataSetChanged();
- verify(mListener, times(1)).notifyItemInserted(1);
+ verify(mListener, times(1)).onChanged();
+ verify(mListener, times(1)).onItemRangeInserted(eq(1), eq(1));
}
@Test
public void test_notifyItemRemoved() throws Exception {
mAdapter.setWidgets(generateSampleMap(2));
mAdapter.setWidgets(generateSampleMap(1));
- verify(mListener, times(1)).notifyDataSetChanged();
- verify(mListener, times(1)).notifyItemRemoved(1);
+ verify(mListener, times(1)).onChanged();
+ verify(mListener, times(1)).onItemRangeRemoved(eq(1), eq(1));
}
@Test
public void testNotifyItemChanged_PackageIconDiff() throws Exception {
mAdapter.setWidgets(generateSampleMap(1));
mAdapter.setWidgets(generateSampleMap(1));
- verify(mListener, times(1)).notifyDataSetChanged();
- verify(mListener, times(1)).notifyItemChanged(0);
+ verify(mListener, times(1)).onChanged();
+ verify(mListener, times(1)).onItemRangeChanged(eq(0), eq(1), isNull());
}
@Test
@@ -125,10 +123,11 @@ public class WidgetsListAdapterTest {
* @param num the number of WidgetItem the map should contain
* @return
*/
- private MultiHashMap<PackageItemInfo, WidgetItem> generateSampleMap(int num) {
- MultiHashMap<PackageItemInfo, WidgetItem> newMap = new MultiHashMap();
- if (num <= 0) return newMap;
+ private ArrayList<WidgetListRowEntry> generateSampleMap(int num) {
+ ArrayList<WidgetListRowEntry> result = new ArrayList<>();
+ if (num <= 0) return result;
+ MultiHashMap<PackageItemInfo, WidgetItem> newMap = new MultiHashMap();
PackageManager pm = mContext.getPackageManager();
AppWidgetManagerCompat widgetManager = AppWidgetManagerCompat.getInstance(mContext);
for (AppWidgetProviderInfo widgetInfo : widgetManager.getAllProviders(null)) {
@@ -144,6 +143,10 @@ public class WidgetsListAdapterTest {
break;
}
}
- return newMap;
+ for (Map.Entry<PackageItemInfo, ArrayList<WidgetItem>> entry : newMap.entrySet()) {
+ result.add(new WidgetListRowEntry(entry.getKey(), entry.getValue()));
+ }
+
+ return result;
}
}