blob: 2a7bb03aa10d3cae75d3ba37e6a8b1e77415c91a [file] [log] [blame]
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -07001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package com.android.launcher3.widget;
17
18import android.animation.Animator;
19import android.animation.AnimatorListenerAdapter;
20import android.animation.PropertyValuesHolder;
21import android.content.Context;
22import android.graphics.Rect;
Vadim Tryshev2ce6a132018-06-18 19:14:44 -070023import android.support.annotation.VisibleForTesting;
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -070024import android.util.AttributeSet;
Sunny Goyalde753212018-05-15 13:55:57 -070025import android.util.Pair;
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -070026import android.view.LayoutInflater;
27import android.view.MotionEvent;
Sunny Goyalde753212018-05-15 13:55:57 -070028import android.view.View;
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -070029import android.view.animation.AnimationUtils;
30
31import com.android.launcher3.Insettable;
32import com.android.launcher3.Launcher;
33import com.android.launcher3.LauncherAppState;
34import com.android.launcher3.LauncherAppWidgetHost.ProviderChangedListener;
35import com.android.launcher3.R;
Sunny Goyal3661bfa2018-03-01 16:29:15 -080036import com.android.launcher3.views.RecyclerViewFastScroller;
37import com.android.launcher3.views.TopRoundedCornerView;
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -070038
39/**
40 * Popup for showing the full list of available widgets
41 */
42public class WidgetsFullSheet extends BaseWidgetSheet
43 implements Insettable, ProviderChangedListener {
44
45 private static final long DEFAULT_OPEN_DURATION = 267;
46 private static final long FADE_IN_DURATION = 150;
47 private static final float VERTICAL_START_POSITION = 0.3f;
48
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -070049 private final Rect mInsets = new Rect();
50
51 private final WidgetsListAdapter mAdapter;
52
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -070053 private WidgetsRecyclerView mRecyclerView;
54
55 public WidgetsFullSheet(Context context, AttributeSet attrs, int defStyleAttr) {
56 super(context, attrs, defStyleAttr);
57 LauncherAppState apps = LauncherAppState.getInstance(context);
58 mAdapter = new WidgetsListAdapter(context,
59 LayoutInflater.from(context), apps.getWidgetCache(), apps.getIconCache(),
60 this, this);
Sunny Goyalde753212018-05-15 13:55:57 -070061
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -070062 }
63
64 public WidgetsFullSheet(Context context, AttributeSet attrs) {
65 this(context, attrs, 0);
66 }
67
68 @Override
69 protected void onFinishInflate() {
70 super.onFinishInflate();
71 mContent = findViewById(R.id.container);
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -070072
73 mRecyclerView = findViewById(R.id.widgets_list_view);
74 mRecyclerView.setAdapter(mAdapter);
75 mAdapter.setApplyBitmapDeferred(true, mRecyclerView);
76
Sunny Goyal016d7e92018-03-09 11:09:20 -080077 TopRoundedCornerView springLayout = (TopRoundedCornerView) mContent;
78 springLayout.addSpringView(R.id.widgets_list_view);
79 mRecyclerView.setEdgeEffectFactory(springLayout.createEdgeEffectFactory());
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -070080 onWidgetsBound();
81 }
82
83 @Override
Sunny Goyalde753212018-05-15 13:55:57 -070084 protected Pair<View, String> getAccessibilityTarget() {
85 return Pair.create(mRecyclerView, getContext().getString(
86 mIsOpen ? R.string.widgets_list : R.string.widgets_list_closed));
87 }
88
89 @Override
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -070090 protected void onAttachedToWindow() {
91 super.onAttachedToWindow();
92 mLauncher.getAppWidgetHost().addProviderChangeListener(this);
93 notifyWidgetProvidersChanged();
94 }
95
96 @Override
97 protected void onDetachedFromWindow() {
98 super.onDetachedFromWindow();
99 mLauncher.getAppWidgetHost().removeProviderChangeListener(this);
100 }
101
102 @Override
103 public void setInsets(Rect insets) {
104 mInsets.set(insets);
105
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700106 mRecyclerView.setPadding(
107 mRecyclerView.getPaddingLeft(), mRecyclerView.getPaddingTop(),
108 mRecyclerView.getPaddingRight(), insets.bottom);
109 if (insets.bottom > 0) {
110 setupNavBarColor();
Sunny Goyalf8d56fc2018-01-31 15:18:11 -0800111 } else {
112 clearNavBarColor();
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700113 }
Sunny Goyal3661bfa2018-03-01 16:29:15 -0800114
115 ((TopRoundedCornerView) mContent).setNavBarScrimHeight(mInsets.bottom);
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700116 requestLayout();
117 }
118
119 @Override
120 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
121 int widthUsed;
122 if (mInsets.bottom > 0) {
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700123 widthUsed = 0;
124 } else {
Sunny Goyal07b69292018-01-08 14:19:34 -0800125 Rect padding = mLauncher.getDeviceProfile().workspacePadding;
126 widthUsed = Math.max(padding.left + padding.right,
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700127 2 * (mInsets.left + mInsets.right));
128 }
129
130 int heightUsed = mInsets.top + mLauncher.getDeviceProfile().edgeMarginPx;
131 measureChildWithMargins(mContent, widthMeasureSpec,
132 widthUsed, heightMeasureSpec, heightUsed);
Sunny Goyal6639a5d2018-02-28 15:09:36 -0800133 setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec),
134 MeasureSpec.getSize(heightMeasureSpec));
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700135 }
136
137 @Override
138 protected void onLayout(boolean changed, int l, int t, int r, int b) {
139 int width = r - l;
140 int height = b - t;
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700141
142 // Content is laid out as center bottom aligned
143 int contentWidth = mContent.getMeasuredWidth();
144 int contentLeft = (width - contentWidth) / 2;
145 mContent.layout(contentLeft, height - mContent.getMeasuredHeight(),
146 contentLeft + contentWidth, height);
147
148 setTranslationShift(mTranslationShift);
149 }
150
151 @Override
152 public void notifyWidgetProvidersChanged() {
153 mLauncher.refreshAndBindWidgetsForPackageUser(null);
154 }
155
156 @Override
157 protected void onWidgetsBound() {
158 mAdapter.setWidgets(mLauncher.getPopupDataProvider().getAllWidgets());
159 }
160
161 private void open(boolean animate) {
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700162 if (animate) {
Sunny Goyaldb6cdb02018-07-13 11:03:04 -0700163 if (getPopupContainer().getInsets().bottom > 0) {
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700164 mContent.setAlpha(0);
165 setTranslationShift(VERTICAL_START_POSITION);
166 }
167 mOpenCloseAnimator.setValues(
168 PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
169 mOpenCloseAnimator
170 .setDuration(DEFAULT_OPEN_DURATION)
171 .setInterpolator(AnimationUtils.loadInterpolator(
172 getContext(), android.R.interpolator.linear_out_slow_in));
173 mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() {
174 @Override
175 public void onAnimationEnd(Animator animation) {
176 mRecyclerView.setLayoutFrozen(false);
177 mAdapter.setApplyBitmapDeferred(false, mRecyclerView);
178 mOpenCloseAnimator.removeListener(this);
179 }
180 });
Sunny Goyal6639a5d2018-02-28 15:09:36 -0800181 post(() -> {
182 mRecyclerView.setLayoutFrozen(true);
183 mOpenCloseAnimator.start();
184 mContent.animate().alpha(1).setDuration(FADE_IN_DURATION);
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700185 });
186 } else {
187 setTranslationShift(TRANSLATION_SHIFT_OPENED);
188 mAdapter.setApplyBitmapDeferred(false, mRecyclerView);
Sunny Goyalde753212018-05-15 13:55:57 -0700189 post(this::announceAccessibilityChanges);
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700190 }
191 }
192
193 @Override
194 protected void handleClose(boolean animate) {
195 handleClose(animate, DEFAULT_OPEN_DURATION);
196 }
197
198 @Override
199 protected boolean isOfType(int type) {
200 return (type & TYPE_WIDGETS_FULL_SHEET) != 0;
201 }
202
203 @Override
204 public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
205 // Disable swipe down when recycler view is scrolling
206 if (ev.getAction() == MotionEvent.ACTION_DOWN) {
207 mNoIntercept = false;
Sunny Goyal3661bfa2018-03-01 16:29:15 -0800208 RecyclerViewFastScroller scroller = mRecyclerView.getScrollbar();
209 if (scroller.getThumbOffsetY() >= 0 &&
Sunny Goyaldb6cdb02018-07-13 11:03:04 -0700210 getPopupContainer().isEventOverView(scroller, ev)) {
Sunny Goyal3661bfa2018-03-01 16:29:15 -0800211 mNoIntercept = true;
Sunny Goyaldb6cdb02018-07-13 11:03:04 -0700212 } else if (getPopupContainer().isEventOverView(mContent, ev)) {
213 mNoIntercept = !mRecyclerView.shouldContainerScroll(ev, getPopupContainer());
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700214 }
215 }
216 return super.onControllerInterceptTouchEvent(ev);
217 }
218
219 public static WidgetsFullSheet show(Launcher launcher, boolean animate) {
220 WidgetsFullSheet sheet = (WidgetsFullSheet) launcher.getLayoutInflater()
221 .inflate(R.layout.widgets_full_sheet, launcher.getDragLayer(), false);
Sunny Goyalde753212018-05-15 13:55:57 -0700222 sheet.mIsOpen = true;
Sunny Goyaldb6cdb02018-07-13 11:03:04 -0700223 sheet.getPopupContainer().addView(sheet);
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700224 sheet.open(animate);
225 return sheet;
226 }
Sunny Goyalaeb16432017-10-16 11:46:41 -0700227
Vadim Tryshev2ce6a132018-06-18 19:14:44 -0700228 @VisibleForTesting
229 public static WidgetsRecyclerView getWidgetsView(Launcher launcher) {
230 return launcher.findViewById(R.id.widgets_list_view);
231 }
232
Sunny Goyalaeb16432017-10-16 11:46:41 -0700233 @Override
234 protected int getElementsRowCount() {
235 return mAdapter.getItemCount();
236 }
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700237}