• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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  */
16 
17 package com.android.launcher3.widget;
18 
19 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_BOTTOM_WIDGETS_TRAY;
20 import static com.android.launcher3.widget.picker.model.data.WidgetPickerDataUtils.findAllWidgetsForPackageUser;
21 
22 import android.content.Context;
23 import android.graphics.Rect;
24 import android.util.AttributeSet;
25 import android.util.Pair;
26 import android.view.Gravity;
27 import android.view.LayoutInflater;
28 import android.view.MotionEvent;
29 import android.view.View;
30 import android.view.ViewGroup;
31 import android.view.ViewParent;
32 import android.view.animation.Interpolator;
33 import android.widget.ScrollView;
34 import android.widget.TableLayout;
35 import android.widget.TextView;
36 
37 import androidx.annotation.Px;
38 
39 import com.android.launcher3.R;
40 import com.android.launcher3.anim.PendingAnimation;
41 import com.android.launcher3.model.WidgetItem;
42 import com.android.launcher3.model.data.ItemInfo;
43 import com.android.launcher3.util.PackageUserKey;
44 import com.android.launcher3.widget.picker.model.data.WidgetPickerData;
45 import com.android.launcher3.widget.util.WidgetsTableUtils;
46 
47 import java.util.List;
48 
49 /**
50  * Bottom sheet for the "Widgets" system shortcut in the long-press popup.
51  */
52 public class WidgetsBottomSheet extends BaseWidgetSheet {
53     private static final int DEFAULT_CLOSE_DURATION = 200;
54 
55     private ItemInfo mOriginalItemInfo;
56     @Px private int mMaxHorizontalSpan;
57 
WidgetsBottomSheet(Context context, AttributeSet attrs)58     public WidgetsBottomSheet(Context context, AttributeSet attrs) {
59         this(context, attrs, 0);
60     }
61 
WidgetsBottomSheet(Context context, AttributeSet attrs, int defStyleAttr)62     public WidgetsBottomSheet(Context context, AttributeSet attrs, int defStyleAttr) {
63         super(context, attrs, defStyleAttr);
64         setWillNotDraw(false);
65     }
66 
67     @Override
onFinishInflate()68     protected void onFinishInflate() {
69         super.onFinishInflate();
70         mContent = findViewById(R.id.widgets_bottom_sheet);
71         setContentBackgroundWithParent(
72                 getContext().getDrawable(R.drawable.bg_rounded_corner_bottom_sheet), mContent);
73     }
74 
75     @Override
onMeasure(int widthMeasureSpec, int heightMeasureSpec)76     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
77         doMeasure(widthMeasureSpec, heightMeasureSpec);
78         if (updateMaxSpansPerRow()) {
79             doMeasure(widthMeasureSpec, heightMeasureSpec);
80         }
81     }
82 
83     /** Returns {@code true} if the max spans have been updated. */
updateMaxSpansPerRow()84     private boolean updateMaxSpansPerRow() {
85         if (getMeasuredWidth() == 0) return false;
86 
87         @Px int maxHorizontalSpan = mContent.getMeasuredWidth() - (2 * mContentHorizontalMargin);
88         if (mMaxHorizontalSpan != maxHorizontalSpan) {
89             // Ensure the table layout is showing widgets in the right column after measure.
90             mMaxHorizontalSpan = maxHorizontalSpan;
91             onWidgetsBound();
92             return true;
93         }
94         return false;
95     }
96 
97     @Override
onLayout(boolean changed, int l, int t, int r, int b)98     protected void onLayout(boolean changed, int l, int t, int r, int b) {
99         int width = r - l;
100         int height = b - t;
101 
102         // Content is laid out as center bottom aligned.
103         int contentWidth = mContent.getMeasuredWidth();
104         int contentLeft = (width - contentWidth - mInsets.left - mInsets.right) / 2 + mInsets.left;
105         mContent.layout(contentLeft, height - mContent.getMeasuredHeight(),
106                 contentLeft + contentWidth, height);
107 
108         setTranslationShift(mTranslationShift);
109 
110         ScrollView widgetsTableScrollView = findViewById(R.id.widgets_table_scroll_view);
111         TableLayout widgetsTable = findViewById(R.id.widgets_table);
112         if (widgetsTable.getMeasuredHeight() > widgetsTableScrollView.getMeasuredHeight()) {
113             findViewById(R.id.collapse_handle).setVisibility(VISIBLE);
114         }
115     }
116 
populateAndShow(ItemInfo itemInfo)117     public void populateAndShow(ItemInfo itemInfo) {
118         mOriginalItemInfo = itemInfo;
119         ((TextView) findViewById(R.id.title)).setText(mOriginalItemInfo.title);
120 
121         onWidgetsBound();
122         attachToContainer();
123         mIsOpen = false;
124         animateOpen();
125     }
126 
127     @Override
onWidgetsBound()128     public void onWidgetsBound() {
129         final WidgetPickerData data = mActivityContext.getWidgetPickerDataProvider().get();
130         final PackageUserKey packageUserKey = PackageUserKey.fromItemInfo(mOriginalItemInfo);
131         List<WidgetItem> widgets = packageUserKey != null ? findAllWidgetsForPackageUser(data,
132                 packageUserKey) : List.of();
133 
134         TableLayout widgetsTable = findViewById(R.id.widgets_table);
135         widgetsTable.removeAllViews();
136 
137         WidgetsTableUtils.groupWidgetItemsUsingRowPxWithReordering(widgets, mActivityContext,
138                 mActivityContext.getDeviceProfile(), mMaxHorizontalSpan,
139                 mWidgetCellHorizontalPadding)
140                 .forEach(row -> {
141                     WidgetTableRow tableRow = new WidgetTableRow(getContext());
142                     tableRow.setGravity(Gravity.TOP);
143                     tableRow.setupRow(row.size(), /*resizeDelayMs=*/ 0);
144                     row.forEach(widgetItem -> {
145                         WidgetCell widget = addItemCell(tableRow);
146                         widget.applyFromCellItem(widgetItem);
147                         if (widget.matchesItem(getLastSelectedWidgetItem())) {
148                             widget.callOnClick();
149                         }
150                     });
151                     widgetsTable.addView(tableRow);
152                 });
153     }
154 
155     @Override
onControllerInterceptTouchEvent(MotionEvent ev)156     public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
157         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
158             mNoIntercept = false;
159             ScrollView scrollView = findViewById(R.id.widgets_table_scroll_view);
160             if (getPopupContainer().isEventOverView(scrollView, ev)
161                     && scrollView.getScrollY() > 0) {
162                 mNoIntercept = true;
163             }
164         }
165         return super.onControllerInterceptTouchEvent(ev);
166     }
167 
addItemCell(WidgetTableRow parent)168     protected WidgetCell addItemCell(WidgetTableRow parent) {
169         WidgetCell widget = (WidgetCell) LayoutInflater.from(getContext())
170                 .inflate(R.layout.widget_cell, parent, false);
171         widget.addPreviewReadyListener(parent);
172         widget.setOnClickListener(this);
173 
174         View previewContainer = widget.findViewById(R.id.widget_preview_container);
175         previewContainer.setOnClickListener(this);
176         previewContainer.setOnLongClickListener(this);
177         widget.setAnimatePreview(false);
178         widget.setSourceContainer(CONTAINER_BOTTOM_WIDGETS_TRAY);
179 
180         parent.addView(widget);
181         return widget;
182     }
183 
animateOpen()184     private void animateOpen() {
185         if (mIsOpen || mOpenCloseAnimation.getAnimationPlayer().isRunning()) {
186             return;
187         }
188         mIsOpen = true;
189         setupNavBarColor();
190         setUpDefaultOpenAnimation().start();
191     }
192 
193     @Override
handleClose(boolean animate)194     protected void handleClose(boolean animate) {
195         handleClose(animate, DEFAULT_CLOSE_DURATION);
196     }
197 
198     @Override
isOfType(@loatingViewType int type)199     protected boolean isOfType(@FloatingViewType int type) {
200         return (type & TYPE_WIDGETS_BOTTOM_SHEET) != 0;
201     }
202 
203     @Override
setInsets(Rect insets)204     public void setInsets(Rect insets) {
205         super.setInsets(insets);
206         int bottomPadding = Math.max(insets.bottom, mNavBarScrimHeight);
207 
208         View widgetsTable = findViewById(R.id.widgets_table);
209         widgetsTable.setPadding(
210                 widgetsTable.getPaddingLeft(),
211                 widgetsTable.getPaddingTop(),
212                 widgetsTable.getPaddingRight(),
213                 bottomPadding);
214         if (bottomPadding > 0) {
215             setupNavBarColor();
216         } else {
217             clearNavBarColor();
218         }
219     }
220 
221     @Override
onContentHorizontalMarginChanged(int contentHorizontalMarginInPx)222     protected void onContentHorizontalMarginChanged(int contentHorizontalMarginInPx) {
223         ViewGroup.MarginLayoutParams layoutParams =
224                 ((ViewGroup.MarginLayoutParams) findViewById(R.id.widgets_table).getLayoutParams());
225         layoutParams.setMarginStart(contentHorizontalMarginInPx);
226         layoutParams.setMarginEnd(contentHorizontalMarginInPx);
227     }
228 
229     @Override
getAccessibilityTarget()230     protected Pair<View, String> getAccessibilityTarget() {
231         return Pair.create(findViewById(R.id.title),  getContext().getString(
232                 mIsOpen ? R.string.widgets_list : R.string.widgets_list_closed));
233     }
234 
235     @Override
addHintCloseAnim( float distanceToMove, Interpolator interpolator, PendingAnimation target)236     public void addHintCloseAnim(
237             float distanceToMove, Interpolator interpolator, PendingAnimation target) {
238         target.addAnimatedFloat(mSwipeToDismissProgress, 0f, 1f, interpolator);
239     }
240 
241     @Override
scrollCellContainerByY(WidgetCell wc, int scrollByY)242     protected void scrollCellContainerByY(WidgetCell wc, int scrollByY) {
243         for (ViewParent parent = wc.getParent(); parent != null; parent = parent.getParent()) {
244             if (parent instanceof ScrollView scrollView) {
245                 scrollView.smoothScrollBy(0, scrollByY);
246                 return;
247             } else if (parent == this) {
248                 return;
249             }
250         }
251     }
252 
253     @Override
onRecommendedWidgetsBound()254     public void onRecommendedWidgetsBound() {} // no op
255 }
256