blob: ec06d1e6dbd5ede47db86c5a179aa6a3042fe126 [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;
23import android.util.AttributeSet;
Sunny Goyalde753212018-05-15 13:55:57 -070024import android.util.Pair;
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -070025import android.view.LayoutInflater;
26import android.view.MotionEvent;
Sunny Goyalde753212018-05-15 13:55:57 -070027import android.view.View;
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -070028import android.view.animation.AnimationUtils;
29
30import com.android.launcher3.Insettable;
31import com.android.launcher3.Launcher;
32import com.android.launcher3.LauncherAppState;
33import com.android.launcher3.LauncherAppWidgetHost.ProviderChangedListener;
34import com.android.launcher3.R;
Sunny Goyal3661bfa2018-03-01 16:29:15 -080035import com.android.launcher3.views.RecyclerViewFastScroller;
36import com.android.launcher3.views.TopRoundedCornerView;
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -070037
Sunny Goyald2303072018-08-14 15:21:45 -070038import androidx.annotation.VisibleForTesting;
39
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -070040/**
41 * Popup for showing the full list of available widgets
42 */
43public class WidgetsFullSheet extends BaseWidgetSheet
44 implements Insettable, ProviderChangedListener {
45
46 private static final long DEFAULT_OPEN_DURATION = 267;
47 private static final long FADE_IN_DURATION = 150;
48 private static final float VERTICAL_START_POSITION = 0.3f;
49
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -070050 private final Rect mInsets = new Rect();
51
52 private final WidgetsListAdapter mAdapter;
53
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -070054 private WidgetsRecyclerView mRecyclerView;
55
56 public WidgetsFullSheet(Context context, AttributeSet attrs, int defStyleAttr) {
57 super(context, attrs, defStyleAttr);
58 LauncherAppState apps = LauncherAppState.getInstance(context);
59 mAdapter = new WidgetsListAdapter(context,
60 LayoutInflater.from(context), apps.getWidgetCache(), apps.getIconCache(),
61 this, this);
Sunny Goyalde753212018-05-15 13:55:57 -070062
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -070063 }
64
65 public WidgetsFullSheet(Context context, AttributeSet attrs) {
66 this(context, attrs, 0);
67 }
68
69 @Override
70 protected void onFinishInflate() {
71 super.onFinishInflate();
72 mContent = findViewById(R.id.container);
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -070073
74 mRecyclerView = findViewById(R.id.widgets_list_view);
75 mRecyclerView.setAdapter(mAdapter);
76 mAdapter.setApplyBitmapDeferred(true, mRecyclerView);
77
Sunny Goyal016d7e92018-03-09 11:09:20 -080078 TopRoundedCornerView springLayout = (TopRoundedCornerView) mContent;
79 springLayout.addSpringView(R.id.widgets_list_view);
80 mRecyclerView.setEdgeEffectFactory(springLayout.createEdgeEffectFactory());
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -070081 onWidgetsBound();
82 }
83
84 @Override
Sunny Goyalde753212018-05-15 13:55:57 -070085 protected Pair<View, String> getAccessibilityTarget() {
86 return Pair.create(mRecyclerView, getContext().getString(
87 mIsOpen ? R.string.widgets_list : R.string.widgets_list_closed));
88 }
89
90 @Override
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -070091 protected void onAttachedToWindow() {
92 super.onAttachedToWindow();
93 mLauncher.getAppWidgetHost().addProviderChangeListener(this);
94 notifyWidgetProvidersChanged();
95 }
96
97 @Override
98 protected void onDetachedFromWindow() {
99 super.onDetachedFromWindow();
100 mLauncher.getAppWidgetHost().removeProviderChangeListener(this);
101 }
102
103 @Override
104 public void setInsets(Rect insets) {
105 mInsets.set(insets);
106
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700107 mRecyclerView.setPadding(
108 mRecyclerView.getPaddingLeft(), mRecyclerView.getPaddingTop(),
109 mRecyclerView.getPaddingRight(), insets.bottom);
110 if (insets.bottom > 0) {
111 setupNavBarColor();
Sunny Goyalf8d56fc2018-01-31 15:18:11 -0800112 } else {
113 clearNavBarColor();
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700114 }
Sunny Goyal3661bfa2018-03-01 16:29:15 -0800115
116 ((TopRoundedCornerView) mContent).setNavBarScrimHeight(mInsets.bottom);
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700117 requestLayout();
118 }
119
120 @Override
121 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
122 int widthUsed;
123 if (mInsets.bottom > 0) {
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700124 widthUsed = 0;
125 } else {
Sunny Goyal07b69292018-01-08 14:19:34 -0800126 Rect padding = mLauncher.getDeviceProfile().workspacePadding;
127 widthUsed = Math.max(padding.left + padding.right,
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700128 2 * (mInsets.left + mInsets.right));
129 }
130
131 int heightUsed = mInsets.top + mLauncher.getDeviceProfile().edgeMarginPx;
132 measureChildWithMargins(mContent, widthMeasureSpec,
133 widthUsed, heightMeasureSpec, heightUsed);
Sunny Goyal6639a5d2018-02-28 15:09:36 -0800134 setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec),
135 MeasureSpec.getSize(heightMeasureSpec));
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700136 }
137
138 @Override
139 protected void onLayout(boolean changed, int l, int t, int r, int b) {
140 int width = r - l;
141 int height = b - t;
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700142
143 // Content is laid out as center bottom aligned
144 int contentWidth = mContent.getMeasuredWidth();
145 int contentLeft = (width - contentWidth) / 2;
146 mContent.layout(contentLeft, height - mContent.getMeasuredHeight(),
147 contentLeft + contentWidth, height);
148
149 setTranslationShift(mTranslationShift);
150 }
151
152 @Override
153 public void notifyWidgetProvidersChanged() {
154 mLauncher.refreshAndBindWidgetsForPackageUser(null);
155 }
156
157 @Override
Sunny Goyal202ae0b2019-02-04 16:26:42 -0800158 public void onWidgetsBound() {
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700159 mAdapter.setWidgets(mLauncher.getPopupDataProvider().getAllWidgets());
160 }
161
162 private void open(boolean animate) {
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700163 if (animate) {
Sunny Goyaldb6cdb02018-07-13 11:03:04 -0700164 if (getPopupContainer().getInsets().bottom > 0) {
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700165 mContent.setAlpha(0);
166 setTranslationShift(VERTICAL_START_POSITION);
167 }
168 mOpenCloseAnimator.setValues(
169 PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
170 mOpenCloseAnimator
171 .setDuration(DEFAULT_OPEN_DURATION)
172 .setInterpolator(AnimationUtils.loadInterpolator(
173 getContext(), android.R.interpolator.linear_out_slow_in));
174 mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() {
175 @Override
176 public void onAnimationEnd(Animator animation) {
177 mRecyclerView.setLayoutFrozen(false);
178 mAdapter.setApplyBitmapDeferred(false, mRecyclerView);
179 mOpenCloseAnimator.removeListener(this);
180 }
181 });
Sunny Goyal6639a5d2018-02-28 15:09:36 -0800182 post(() -> {
183 mRecyclerView.setLayoutFrozen(true);
184 mOpenCloseAnimator.start();
185 mContent.animate().alpha(1).setDuration(FADE_IN_DURATION);
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700186 });
187 } else {
188 setTranslationShift(TRANSLATION_SHIFT_OPENED);
189 mAdapter.setApplyBitmapDeferred(false, mRecyclerView);
Sunny Goyalde753212018-05-15 13:55:57 -0700190 post(this::announceAccessibilityChanges);
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700191 }
192 }
193
194 @Override
195 protected void handleClose(boolean animate) {
196 handleClose(animate, DEFAULT_OPEN_DURATION);
197 }
198
199 @Override
200 protected boolean isOfType(int type) {
201 return (type & TYPE_WIDGETS_FULL_SHEET) != 0;
202 }
203
204 @Override
205 public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
206 // Disable swipe down when recycler view is scrolling
207 if (ev.getAction() == MotionEvent.ACTION_DOWN) {
208 mNoIntercept = false;
Sunny Goyal3661bfa2018-03-01 16:29:15 -0800209 RecyclerViewFastScroller scroller = mRecyclerView.getScrollbar();
210 if (scroller.getThumbOffsetY() >= 0 &&
Sunny Goyaldb6cdb02018-07-13 11:03:04 -0700211 getPopupContainer().isEventOverView(scroller, ev)) {
Sunny Goyal3661bfa2018-03-01 16:29:15 -0800212 mNoIntercept = true;
Sunny Goyaldb6cdb02018-07-13 11:03:04 -0700213 } else if (getPopupContainer().isEventOverView(mContent, ev)) {
214 mNoIntercept = !mRecyclerView.shouldContainerScroll(ev, getPopupContainer());
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700215 }
216 }
217 return super.onControllerInterceptTouchEvent(ev);
218 }
219
220 public static WidgetsFullSheet show(Launcher launcher, boolean animate) {
221 WidgetsFullSheet sheet = (WidgetsFullSheet) launcher.getLayoutInflater()
222 .inflate(R.layout.widgets_full_sheet, launcher.getDragLayer(), false);
Sunny Goyal1642f712018-09-18 11:40:35 -0700223 sheet.attachToContainer();
Sunny Goyalde753212018-05-15 13:55:57 -0700224 sheet.mIsOpen = true;
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700225 sheet.open(animate);
226 return sheet;
227 }
Sunny Goyalaeb16432017-10-16 11:46:41 -0700228
Vadim Tryshev2ce6a132018-06-18 19:14:44 -0700229 @VisibleForTesting
230 public static WidgetsRecyclerView getWidgetsView(Launcher launcher) {
231 return launcher.findViewById(R.id.widgets_list_view);
232 }
233
Sunny Goyalaeb16432017-10-16 11:46:41 -0700234 @Override
235 protected int getElementsRowCount() {
236 return mAdapter.getItemCount();
237 }
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700238}