fbe1836627
Bug: 191644950
Test: 1. Open the full widget picker. Observe the clock widgets'
previews are shown properly. Then, rotate the screen and
observe the clock widgets' previews are shown properly.
2. Open the bottom widgets picker for clock. Observe the
clock widgets' previews are shown properly. Then, rotate
the screen and observe the clock widgets' previews are shown
properly.
3. Repeat 1, 2 after changing the system navigation from button
to gesture.
Change-Id: I564fc2ce0baf3103ae77499380ad69ec38ac6930
284 lines
11 KiB
Java
284 lines
11 KiB
Java
/*
|
|
* 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 static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
|
|
|
|
import android.animation.PropertyValuesHolder;
|
|
import android.content.Context;
|
|
import android.graphics.Rect;
|
|
import android.util.AttributeSet;
|
|
import android.util.IntProperty;
|
|
import android.util.Pair;
|
|
import android.view.Gravity;
|
|
import android.view.LayoutInflater;
|
|
import android.view.MotionEvent;
|
|
import android.view.View;
|
|
import android.view.ViewGroup;
|
|
import android.view.animation.Interpolator;
|
|
import android.widget.ScrollView;
|
|
import android.widget.TableLayout;
|
|
import android.widget.TableRow;
|
|
import android.widget.TextView;
|
|
|
|
import com.android.launcher3.DeviceProfile;
|
|
import com.android.launcher3.LauncherAppState;
|
|
import com.android.launcher3.R;
|
|
import com.android.launcher3.anim.PendingAnimation;
|
|
import com.android.launcher3.model.WidgetItem;
|
|
import com.android.launcher3.model.data.ItemInfo;
|
|
import com.android.launcher3.util.PackageUserKey;
|
|
import com.android.launcher3.widget.util.WidgetsTableUtils;
|
|
|
|
import java.util.List;
|
|
|
|
/**
|
|
* Bottom sheet for the "Widgets" system shortcut in the long-press popup.
|
|
*/
|
|
public class WidgetsBottomSheet extends BaseWidgetSheet {
|
|
private static final String TAG = "WidgetsBottomSheet";
|
|
|
|
private static final IntProperty<View> PADDING_BOTTOM =
|
|
new IntProperty<View>("paddingBottom") {
|
|
@Override
|
|
public void setValue(View view, int paddingBottom) {
|
|
view.setPadding(view.getPaddingLeft(), view.getPaddingTop(),
|
|
view.getPaddingRight(), paddingBottom);
|
|
}
|
|
|
|
@Override
|
|
public Integer get(View view) {
|
|
return view.getPaddingBottom();
|
|
}
|
|
};
|
|
|
|
private static final int DEFAULT_CLOSE_DURATION = 200;
|
|
private static final long EDUCATION_TIP_DELAY_MS = 300;
|
|
|
|
private ItemInfo mOriginalItemInfo;
|
|
private final int mMaxTableHeight;
|
|
private int mMaxHorizontalSpan = 4;
|
|
|
|
private final OnLayoutChangeListener mLayoutChangeListenerToShowTips =
|
|
new OnLayoutChangeListener() {
|
|
@Override
|
|
public void onLayoutChange(View v, int left, int top, int right, int bottom,
|
|
int oldLeft, int oldTop, int oldRight, int oldBottom) {
|
|
if (hasSeenEducationTip()) {
|
|
removeOnLayoutChangeListener(this);
|
|
return;
|
|
}
|
|
// Widgets are loaded asynchronously, We are adding a delay because we only want
|
|
// to show the tip when the widget preview has finished loading and rendering in
|
|
// this view.
|
|
removeCallbacks(mShowEducationTipTask);
|
|
postDelayed(mShowEducationTipTask, EDUCATION_TIP_DELAY_MS);
|
|
}
|
|
};
|
|
|
|
private final Runnable mShowEducationTipTask = () -> {
|
|
if (hasSeenEducationTip()) {
|
|
removeOnLayoutChangeListener(mLayoutChangeListenerToShowTips);
|
|
return;
|
|
}
|
|
View viewForTip = ((ViewGroup) ((TableLayout) findViewById(R.id.widgets_table))
|
|
.getChildAt(0)).getChildAt(0);
|
|
if (showEducationTipOnViewIfPossible(viewForTip) != null) {
|
|
removeOnLayoutChangeListener(mLayoutChangeListenerToShowTips);
|
|
}
|
|
};
|
|
|
|
public WidgetsBottomSheet(Context context, AttributeSet attrs) {
|
|
this(context, attrs, 0);
|
|
}
|
|
|
|
public WidgetsBottomSheet(Context context, AttributeSet attrs, int defStyleAttr) {
|
|
super(context, attrs, defStyleAttr);
|
|
setWillNotDraw(false);
|
|
DeviceProfile deviceProfile = mActivityContext.getDeviceProfile();
|
|
// Set the max table height to 2 / 3 of the grid height so that the bottom picker won't
|
|
// take over the entire view vertically.
|
|
mMaxTableHeight = deviceProfile.inv.numRows * 2 / 3 * deviceProfile.cellHeightPx;
|
|
if (!hasSeenEducationTip()) {
|
|
addOnLayoutChangeListener(mLayoutChangeListenerToShowTips);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void onFinishInflate() {
|
|
super.onFinishInflate();
|
|
mContent = findViewById(R.id.widgets_bottom_sheet);
|
|
}
|
|
|
|
@Override
|
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
|
doMeasure(widthMeasureSpec, heightMeasureSpec);
|
|
if (updateMaxSpansPerRow()) {
|
|
doMeasure(widthMeasureSpec, heightMeasureSpec);
|
|
}
|
|
}
|
|
|
|
/** Returns {@code true} if the max spans have been updated. */
|
|
private boolean updateMaxSpansPerRow() {
|
|
if (getMeasuredWidth() == 0) return false;
|
|
|
|
int paddingPx = 2 * getResources().getDimensionPixelOffset(
|
|
R.dimen.widget_cell_horizontal_padding);
|
|
int maxHorizontalSpan = findViewById(R.id.widgets_table).getMeasuredWidth()
|
|
/ (mActivityContext.getDeviceProfile().cellWidthPx + paddingPx);
|
|
if (mMaxHorizontalSpan != maxHorizontalSpan) {
|
|
// Ensure the table layout is showing widgets in the right column after measure.
|
|
mMaxHorizontalSpan = maxHorizontalSpan;
|
|
onWidgetsBound();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
|
int width = r - l;
|
|
int height = b - t;
|
|
|
|
// Content is laid out as center bottom aligned.
|
|
int contentWidth = mContent.getMeasuredWidth();
|
|
int contentLeft = (width - contentWidth - mInsets.left - mInsets.right) / 2 + mInsets.left;
|
|
mContent.layout(contentLeft, height - mContent.getMeasuredHeight(),
|
|
contentLeft + contentWidth, height);
|
|
|
|
setTranslationShift(mTranslationShift);
|
|
|
|
// Ensure the scroll view height is not larger than mMaxTableHeight, which is a value
|
|
// smaller than the entire screen height.
|
|
ScrollView widgetsTableScrollView = findViewById(R.id.widgets_table_scroll_view);
|
|
if (widgetsTableScrollView.getMeasuredHeight() > mMaxTableHeight) {
|
|
ViewGroup.LayoutParams layoutParams = widgetsTableScrollView.getLayoutParams();
|
|
layoutParams.height = mMaxTableHeight;
|
|
widgetsTableScrollView.setLayoutParams(layoutParams);
|
|
findViewById(R.id.collapse_handle).setVisibility(VISIBLE);
|
|
}
|
|
}
|
|
|
|
public void populateAndShow(ItemInfo itemInfo) {
|
|
mOriginalItemInfo = itemInfo;
|
|
((TextView) findViewById(R.id.title)).setText(mOriginalItemInfo.title);
|
|
|
|
onWidgetsBound();
|
|
attachToContainer();
|
|
mIsOpen = false;
|
|
animateOpen();
|
|
}
|
|
|
|
@Override
|
|
public void onWidgetsBound() {
|
|
List<WidgetItem> widgets = mActivityContext.getPopupDataProvider().getWidgetsForPackageUser(
|
|
new PackageUserKey(
|
|
mOriginalItemInfo.getTargetComponent().getPackageName(),
|
|
mOriginalItemInfo.user));
|
|
|
|
TableLayout widgetsTable = findViewById(R.id.widgets_table);
|
|
widgetsTable.removeAllViews();
|
|
|
|
WidgetsTableUtils.groupWidgetItemsIntoTable(widgets, mMaxHorizontalSpan).forEach(row -> {
|
|
TableRow tableRow = new TableRow(getContext());
|
|
tableRow.setGravity(Gravity.TOP);
|
|
row.forEach(widgetItem -> {
|
|
WidgetCell widget = addItemCell(tableRow);
|
|
widget.setPreviewSize(widgetItem.spanX, widgetItem.spanY);
|
|
widget.applyFromCellItem(widgetItem, LauncherAppState.getInstance(mActivityContext)
|
|
.getWidgetCache());
|
|
widget.ensurePreview();
|
|
widget.setVisibility(View.VISIBLE);
|
|
});
|
|
widgetsTable.addView(tableRow);
|
|
});
|
|
}
|
|
|
|
@Override
|
|
public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
|
|
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
|
|
mNoIntercept = false;
|
|
ScrollView scrollView = findViewById(R.id.widgets_table_scroll_view);
|
|
if (getPopupContainer().isEventOverView(scrollView, ev)
|
|
&& scrollView.getScrollY() > 0) {
|
|
mNoIntercept = true;
|
|
}
|
|
}
|
|
return super.onControllerInterceptTouchEvent(ev);
|
|
}
|
|
|
|
protected WidgetCell addItemCell(ViewGroup parent) {
|
|
WidgetCell widget = (WidgetCell) LayoutInflater.from(getContext())
|
|
.inflate(R.layout.widget_cell, parent, false);
|
|
|
|
View previewContainer = widget.findViewById(R.id.widget_preview_container);
|
|
previewContainer.setOnClickListener(this);
|
|
previewContainer.setOnLongClickListener(this);
|
|
widget.setAnimatePreview(false);
|
|
|
|
parent.addView(widget);
|
|
return widget;
|
|
}
|
|
|
|
private void animateOpen() {
|
|
if (mIsOpen || mOpenCloseAnimator.isRunning()) {
|
|
return;
|
|
}
|
|
mIsOpen = true;
|
|
setupNavBarColor();
|
|
mOpenCloseAnimator.setValues(
|
|
PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
|
|
mOpenCloseAnimator.setInterpolator(FAST_OUT_SLOW_IN);
|
|
mOpenCloseAnimator.start();
|
|
}
|
|
|
|
@Override
|
|
protected void handleClose(boolean animate) {
|
|
handleClose(animate, DEFAULT_CLOSE_DURATION);
|
|
}
|
|
|
|
@Override
|
|
protected boolean isOfType(@FloatingViewType int type) {
|
|
return (type & TYPE_WIDGETS_BOTTOM_SHEET) != 0;
|
|
}
|
|
|
|
@Override
|
|
public void setInsets(Rect insets) {
|
|
super.setInsets(insets);
|
|
|
|
mContent.setPadding(mContent.getPaddingStart(),
|
|
mContent.getPaddingTop(), mContent.getPaddingEnd(), insets.bottom);
|
|
if (insets.bottom > 0) {
|
|
setupNavBarColor();
|
|
} else {
|
|
clearNavBarColor();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected Pair<View, String> getAccessibilityTarget() {
|
|
return Pair.create(findViewById(R.id.title), getContext().getString(
|
|
mIsOpen ? R.string.widgets_list : R.string.widgets_list_closed));
|
|
}
|
|
|
|
@Override
|
|
public void addHintCloseAnim(
|
|
float distanceToMove, Interpolator interpolator, PendingAnimation target) {
|
|
target.setInt(this, PADDING_BOTTOM, (int) (distanceToMove + mInsets.bottom), interpolator);
|
|
}
|
|
}
|