blob: a40ea1b6449af0c49d8dc672e7a2f73ae65e7066 [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;
24import android.view.LayoutInflater;
25import android.view.MotionEvent;
26import android.view.View;
27import android.view.animation.AnimationUtils;
28
29import com.android.launcher3.Insettable;
30import com.android.launcher3.Launcher;
31import com.android.launcher3.LauncherAppState;
32import com.android.launcher3.LauncherAppWidgetHost.ProviderChangedListener;
33import com.android.launcher3.R;
34
35/**
36 * Popup for showing the full list of available widgets
37 */
38public class WidgetsFullSheet extends BaseWidgetSheet
39 implements Insettable, ProviderChangedListener {
40
41 private static final long DEFAULT_OPEN_DURATION = 267;
42 private static final long FADE_IN_DURATION = 150;
43 private static final float VERTICAL_START_POSITION = 0.3f;
44
45 private static final Rect sTempRect = new Rect();
46
47 private final Rect mInsets = new Rect();
48
49 private final WidgetsListAdapter mAdapter;
50
51 private View mNavBarScrim;
52 private WidgetsRecyclerView mRecyclerView;
53
54 public WidgetsFullSheet(Context context, AttributeSet attrs, int defStyleAttr) {
55 super(context, attrs, defStyleAttr);
56 LauncherAppState apps = LauncherAppState.getInstance(context);
57 mAdapter = new WidgetsListAdapter(context,
58 LayoutInflater.from(context), apps.getWidgetCache(), apps.getIconCache(),
59 this, this);
60 }
61
62 public WidgetsFullSheet(Context context, AttributeSet attrs) {
63 this(context, attrs, 0);
64 }
65
66 @Override
67 protected void onFinishInflate() {
68 super.onFinishInflate();
69 mContent = findViewById(R.id.container);
70 mNavBarScrim = findViewById(R.id.nav_bar_bg);
71
72 mRecyclerView = findViewById(R.id.widgets_list_view);
73 mRecyclerView.setAdapter(mAdapter);
74 mAdapter.setApplyBitmapDeferred(true, mRecyclerView);
75
76 mGradientView = findViewById(R.id.gradient_bg);
77 mGradientView.setProgress(1, false);
78
79 onWidgetsBound();
80 }
81
82 @Override
83 protected void onAttachedToWindow() {
84 super.onAttachedToWindow();
85 mLauncher.getAppWidgetHost().addProviderChangeListener(this);
86 notifyWidgetProvidersChanged();
87 }
88
89 @Override
90 protected void onDetachedFromWindow() {
91 super.onDetachedFromWindow();
92 mLauncher.getAppWidgetHost().removeProviderChangeListener(this);
93 }
94
95 @Override
96 public void setInsets(Rect insets) {
97 mInsets.set(insets);
98
99 mNavBarScrim.getLayoutParams().height = insets.bottom;
100 mRecyclerView.setPadding(
101 mRecyclerView.getPaddingLeft(), mRecyclerView.getPaddingTop(),
102 mRecyclerView.getPaddingRight(), insets.bottom);
103 if (insets.bottom > 0) {
104 setupNavBarColor();
105 }
106 requestLayout();
107 }
108
109 @Override
110 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
111 int widthUsed;
112 if (mInsets.bottom > 0) {
113 // If we have bottom insets, we do not show the scrim as it would overlap
114 // with the navbar scrim
115 mGradientView.setVisibility(View.INVISIBLE);
116 widthUsed = 0;
117 } else {
118 mLauncher.getDeviceProfile().getWorkspacePadding(sTempRect);
119 widthUsed = Math.max(sTempRect.left + sTempRect.right,
120 2 * (mInsets.left + mInsets.right));
121 }
122
123 int heightUsed = mInsets.top + mLauncher.getDeviceProfile().edgeMarginPx;
124 measureChildWithMargins(mContent, widthMeasureSpec,
125 widthUsed, heightMeasureSpec, heightUsed);
126 measureChild(mGradientView, widthMeasureSpec, heightMeasureSpec);
127 setMeasuredDimension(mGradientView.getMeasuredWidth(), mGradientView.getMeasuredHeight());
128 }
129
130 @Override
131 protected void onLayout(boolean changed, int l, int t, int r, int b) {
132 int width = r - l;
133 int height = b - t;
134 mGradientView.layout(0, 0, width, height);
135
136 // Content is laid out as center bottom aligned
137 int contentWidth = mContent.getMeasuredWidth();
138 int contentLeft = (width - contentWidth) / 2;
139 mContent.layout(contentLeft, height - mContent.getMeasuredHeight(),
140 contentLeft + contentWidth, height);
141
142 setTranslationShift(mTranslationShift);
143 }
144
145 @Override
146 public void notifyWidgetProvidersChanged() {
147 mLauncher.refreshAndBindWidgetsForPackageUser(null);
148 }
149
150 @Override
151 protected void onWidgetsBound() {
152 mAdapter.setWidgets(mLauncher.getPopupDataProvider().getAllWidgets());
153 }
154
155 private void open(boolean animate) {
156 if (mIsOpen) {
157 return;
158 }
159 mIsOpen = true;
160 if (animate) {
161 if (mLauncher.getDragLayer().getInsets().bottom > 0) {
162 mContent.setAlpha(0);
163 setTranslationShift(VERTICAL_START_POSITION);
164 }
165 mOpenCloseAnimator.setValues(
166 PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
167 mOpenCloseAnimator
168 .setDuration(DEFAULT_OPEN_DURATION)
169 .setInterpolator(AnimationUtils.loadInterpolator(
170 getContext(), android.R.interpolator.linear_out_slow_in));
171 mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() {
172 @Override
173 public void onAnimationEnd(Animator animation) {
174 mRecyclerView.setLayoutFrozen(false);
175 mAdapter.setApplyBitmapDeferred(false, mRecyclerView);
176 mOpenCloseAnimator.removeListener(this);
177 }
178 });
179 post(new Runnable() {
180 @Override
181 public void run() {
182 mRecyclerView.setLayoutFrozen(true);
183 mOpenCloseAnimator.start();
184 mContent.animate().alpha(1).setDuration(FADE_IN_DURATION);
185 }
186 });
187 } else {
188 setTranslationShift(TRANSLATION_SHIFT_OPENED);
189 mAdapter.setApplyBitmapDeferred(false, mRecyclerView);
190 }
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;
208 if (mLauncher.getDragLayer().isEventOverView(mContent, ev)) {
209 mNoIntercept = !mRecyclerView.shouldContainerScroll(ev, mLauncher.getDragLayer());
210 }
211 }
212 return super.onControllerInterceptTouchEvent(ev);
213 }
214
215 public static WidgetsFullSheet show(Launcher launcher, boolean animate) {
216 WidgetsFullSheet sheet = (WidgetsFullSheet) launcher.getLayoutInflater()
217 .inflate(R.layout.widgets_full_sheet, launcher.getDragLayer(), false);
218 launcher.getDragLayer().addView(sheet);
219 sheet.open(animate);
220 return sheet;
221 }
Sunny Goyalaeb16432017-10-16 11:46:41 -0700222
223 @Override
224 protected int getElementsRowCount() {
225 return mAdapter.getItemCount();
226 }
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700227}