summaryrefslogtreecommitdiffstats
path: root/src/com/android/launcher3/widget/WidgetsBottomSheet.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/launcher3/widget/WidgetsBottomSheet.java')
-rw-r--r--src/com/android/launcher3/widget/WidgetsBottomSheet.java304
1 files changed, 304 insertions, 0 deletions
diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
new file mode 100644
index 000000000..a423154f4
--- /dev/null
+++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
@@ -0,0 +1,304 @@
+/*
+ * 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 android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.graphics.Rect;
+import android.support.v4.view.animation.FastOutSlowInInterpolator;
+import android.util.AttributeSet;
+import android.view.ContextThemeWrapper;
+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.TextView;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.DropTarget;
+import com.android.launcher3.Insettable;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAnimUtils;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.allapps.VerticalPullDetector;
+import com.android.launcher3.anim.PropertyListBuilder;
+import com.android.launcher3.dragndrop.DragController;
+import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.model.WidgetItem;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.util.TouchController;
+
+import java.util.List;
+
+/**
+ * Bottom sheet for the "Widgets" system shortcut in the long-press popup.
+ */
+public class WidgetsBottomSheet extends AbstractFloatingView implements Insettable, TouchController,
+ VerticalPullDetector.Listener, View.OnClickListener, View.OnLongClickListener,
+ DragController.DragListener {
+
+ private int mTranslationYOpen;
+ private int mTranslationYClosed;
+ private float mTranslationYRange;
+
+ private Launcher mLauncher;
+ private ItemInfo mOriginalItemInfo;
+ private ObjectAnimator mOpenCloseAnimator;
+ private Interpolator mFastOutSlowInInterpolator;
+ private VerticalPullDetector.ScrollInterpolator mScrollInterpolator;
+ private Rect mInsets;
+ private boolean mWasNavBarLight;
+ private VerticalPullDetector mVerticalPullDetector;
+
+ public WidgetsBottomSheet(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public WidgetsBottomSheet(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(new ContextThemeWrapper(context, R.style.WidgetContainerTheme), attrs, defStyleAttr);
+ setWillNotDraw(false);
+ mLauncher = Launcher.getLauncher(context);
+ mOpenCloseAnimator = LauncherAnimUtils.ofPropertyValuesHolder(this);
+ mFastOutSlowInInterpolator = new FastOutSlowInInterpolator();
+ mScrollInterpolator = new VerticalPullDetector.ScrollInterpolator();
+ mInsets = new Rect();
+ mVerticalPullDetector = new VerticalPullDetector(context);
+ mVerticalPullDetector.setListener(this);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ mTranslationYOpen = 0;
+ mTranslationYClosed = getMeasuredHeight();
+ mTranslationYRange = mTranslationYClosed - mTranslationYOpen;
+ }
+
+ public void populateAndShow(ItemInfo itemInfo) {
+ mOriginalItemInfo = itemInfo;
+ ((TextView) findViewById(R.id.title)).setText(getContext().getString(
+ R.string.widgets_bottom_sheet_title, mOriginalItemInfo.title));
+
+ onWidgetsBound();
+
+ mWasNavBarLight = (mLauncher.getWindow().getDecorView().getSystemUiVisibility()
+ & View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR) != 0;
+ mLauncher.getDragLayer().addView(this);
+ measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+ setTranslationY(mTranslationYClosed);
+ mIsOpen = false;
+ open(true);
+ }
+
+ @Override
+ protected void onWidgetsBound() {
+ List<WidgetItem> widgets = mLauncher.getWidgetsForPackageUser(new PackageUserKey(
+ mOriginalItemInfo.getTargetComponent().getPackageName(), mOriginalItemInfo.user));
+
+ ViewGroup widgetRow = (ViewGroup) findViewById(R.id.widgets);
+ ViewGroup widgetCells = (ViewGroup) widgetRow.findViewById(R.id.widgets_cell_list);
+
+ widgetCells.removeAllViews();
+
+ for (int i = 0; i < widgets.size(); i++) {
+ WidgetCell widget = addItemCell(widgetCells);
+ widget.applyFromCellItem(widgets.get(i), LauncherAppState.getInstance(mLauncher)
+ .getWidgetCache());
+ widget.ensurePreview();
+ widget.setVisibility(View.VISIBLE);
+ if (i < widgets.size() - 1) {
+ addDivider(widgetCells);
+ }
+ }
+
+ // If there is only one widget, we want to center it instead of left-align.
+ WidgetsBottomSheet.LayoutParams params = (WidgetsBottomSheet.LayoutParams)
+ widgetRow.getLayoutParams();
+ params.gravity = widgets.size() == 1 ? Gravity.CENTER_HORIZONTAL : Gravity.START;
+ }
+
+ private void addDivider(ViewGroup parent) {
+ LayoutInflater.from(getContext()).inflate(R.layout.widget_list_divider, parent, true);
+ }
+
+ private WidgetCell addItemCell(ViewGroup parent) {
+ WidgetCell widget = (WidgetCell) LayoutInflater.from(getContext()).inflate(
+ R.layout.widget_cell, parent, false);
+
+ widget.setOnClickListener(this);
+ widget.setOnLongClickListener(this);
+ widget.setAnimatePreview(false);
+
+ parent.addView(widget);
+ return widget;
+ }
+
+ @Override
+ public void onClick(View view) {
+ mLauncher.getWidgetsView().handleClick();
+ }
+
+ @Override
+ public boolean onLongClick(View view) {
+ mLauncher.getDragController().addDragListener(this);
+ return mLauncher.getWidgetsView().handleLongClick(view);
+ }
+
+ private void open(boolean animate) {
+ if (mIsOpen || mOpenCloseAnimator.isRunning()) {
+ return;
+ }
+ mIsOpen = true;
+ setLightNavBar(true);
+ if (animate) {
+ mOpenCloseAnimator.setValues(new PropertyListBuilder()
+ .translationY(mTranslationYOpen).build());
+ mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mVerticalPullDetector.finishedScrolling();
+ }
+ });
+ mOpenCloseAnimator.setInterpolator(mFastOutSlowInInterpolator);
+ mOpenCloseAnimator.start();
+ } else {
+ setTranslationY(mTranslationYOpen);
+ }
+ }
+
+ @Override
+ protected void handleClose(boolean animate) {
+ if (!mIsOpen || mOpenCloseAnimator.isRunning()) {
+ return;
+ }
+ if (animate) {
+ mOpenCloseAnimator.setValues(new PropertyListBuilder()
+ .translationY(mTranslationYClosed).build());
+ mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mIsOpen = false;
+ mVerticalPullDetector.finishedScrolling();
+ ((ViewGroup) getParent()).removeView(WidgetsBottomSheet.this);
+ setLightNavBar(mWasNavBarLight);
+ }
+ });
+ mOpenCloseAnimator.setInterpolator(mVerticalPullDetector.isIdleState()
+ ? mFastOutSlowInInterpolator : mScrollInterpolator);
+ mOpenCloseAnimator.start();
+ } else {
+ setTranslationY(mTranslationYClosed);
+ setLightNavBar(mWasNavBarLight);
+ mIsOpen = false;
+ }
+ }
+
+ private void setLightNavBar(boolean lightNavBar) {
+ mLauncher.activateLightSystemBars(lightNavBar, false /* statusBar */, true /* navBar */);
+ }
+
+ @Override
+ protected boolean isOfType(@FloatingViewType int type) {
+ return (type & TYPE_WIDGETS_BOTTOM_SHEET) != 0;
+ }
+
+ @Override
+ public int getLogContainerType() {
+ return LauncherLogProto.ContainerType.WIDGETS; // TODO: be more specific
+ }
+
+ /**
+ * Returns a {@link WidgetsBottomSheet} which is already open or null
+ */
+ public static WidgetsBottomSheet getOpen(Launcher launcher) {
+ return getOpenView(launcher, TYPE_WIDGETS_BOTTOM_SHEET);
+ }
+
+ @Override
+ public void setInsets(Rect insets) {
+ // Extend behind left, right, and bottom insets.
+ int leftInset = insets.left - mInsets.left;
+ int rightInset = insets.right - mInsets.right;
+ int bottomInset = insets.bottom - mInsets.bottom;
+ mInsets.set(insets);
+ setPadding(getPaddingLeft() + leftInset, getPaddingTop(),
+ getPaddingRight() + rightInset, getPaddingBottom() + bottomInset);
+ }
+
+ /* VerticalPullDetector.Listener */
+
+ @Override
+ public void onDragStart(boolean start) {
+ }
+
+ @Override
+ public boolean onDrag(float displacement, float velocity) {
+ setTranslationY(Utilities.boundToRange(displacement, mTranslationYOpen,
+ mTranslationYClosed));
+ return true;
+ }
+
+ @Override
+ public void onDragEnd(float velocity, boolean fling) {
+ if ((fling && velocity > 0) || getTranslationY() > (mTranslationYRange) / 2) {
+ mScrollInterpolator.setVelocityAtZero(velocity);
+ mOpenCloseAnimator.setDuration(mVerticalPullDetector.calculateDuration(velocity,
+ (mTranslationYClosed - getTranslationY()) / mTranslationYRange));
+ close(true);
+ } else {
+ mIsOpen = false;
+ mOpenCloseAnimator.setDuration(mVerticalPullDetector.calculateDuration(velocity,
+ (getTranslationY() - mTranslationYOpen) / mTranslationYRange));
+ open(true);
+ }
+ }
+
+ @Override
+ public boolean onControllerTouchEvent(MotionEvent ev) {
+ return mVerticalPullDetector.onTouchEvent(ev);
+ }
+
+ @Override
+ public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+ int directionsToDetectScroll = mVerticalPullDetector.isIdleState() ?
+ VerticalPullDetector.DIRECTION_DOWN : 0;
+ mVerticalPullDetector.setDetectableScrollConditions(
+ directionsToDetectScroll, false);
+ mVerticalPullDetector.onTouchEvent(ev);
+ return mVerticalPullDetector.isDraggingOrSettling();
+ }
+
+ /* DragListener */
+
+ @Override
+ public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
+ // A widget or custom shortcut was dragged.
+ close(true);
+ }
+
+ @Override
+ public void onDragEnd() {
+ }
+}