summaryrefslogtreecommitdiffstats
path: root/src/com/android/launcher3/widget/WidgetsAndMore.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/launcher3/widget/WidgetsAndMore.java')
-rw-r--r--src/com/android/launcher3/widget/WidgetsAndMore.java326
1 files changed, 326 insertions, 0 deletions
diff --git a/src/com/android/launcher3/widget/WidgetsAndMore.java b/src/com/android/launcher3/widget/WidgetsAndMore.java
new file mode 100644
index 000000000..3ed2530b3
--- /dev/null
+++ b/src/com/android/launcher3/widget/WidgetsAndMore.java
@@ -0,0 +1,326 @@
+/*
+ * 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.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.TouchController;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Bottom sheet for the "Widgets & more" long-press option.
+ */
+public class WidgetsAndMore 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 ObjectAnimator mOpenCloseAnimator;
+ private Interpolator mFastOutSlowInInterpolator;
+ private VerticalPullDetector.ScrollInterpolator mScrollInterpolator;
+ private Rect mInsets;
+ private boolean mWasNavBarLight;
+ private VerticalPullDetector mVerticalPullDetector;
+
+ public WidgetsAndMore(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public WidgetsAndMore(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, List<WidgetItem> widgets) {
+ ((TextView) findViewById(R.id.title)).setText(itemInfo.title);
+
+ List<WidgetItem> shortcuts = new ArrayList<>();
+ // Transfer configurable widgets to shortcuts
+ Iterator<WidgetItem> widgetsIter = widgets.iterator();
+ WidgetItem nextWidget;
+ while (widgetsIter.hasNext()) {
+ nextWidget = widgetsIter.next();
+ if (nextWidget.activityInfo != null) {
+ shortcuts.add(nextWidget);
+ widgetsIter.remove();
+ }
+ }
+
+ ViewGroup widgetRow = (ViewGroup) findViewById(R.id.widgets);
+ ViewGroup widgetCells = (ViewGroup) widgetRow.findViewById(R.id.widgets_cell_list);
+
+ ViewGroup shortcutRow = (ViewGroup) findViewById(R.id.shortcuts);
+ ViewGroup shortcutCells = (ViewGroup) shortcutRow.findViewById(R.id.widgets_cell_list);
+
+ for (int i = 0; i < widgets.size(); i++) {
+ addItemCell(widgetCells);
+ if (i < widgets.size() - 1) {
+ addDivider(widgetCells);
+ }
+ }
+ for (int i = 0; i < shortcuts.size(); i++) {
+ addItemCell(shortcutCells);
+ if (i < shortcuts.size() - 1) {
+ addDivider(shortcutCells);
+ }
+ }
+
+ // Bind the views in the horizontal tray regions.
+ if (widgetCells.getChildCount() > 0) {
+ for (int i = 0; i < widgets.size(); i++) {
+ WidgetCell widget = (WidgetCell) widgetCells.getChildAt(i*2); // skip dividers
+ widget.applyFromCellItem(widgets.get(i), LauncherAppState.getInstance(mLauncher)
+ .getWidgetCache());
+ widget.ensurePreview();
+ widget.setVisibility(View.VISIBLE);
+ }
+ } else {
+ removeView(findViewById(R.id.widgets_header));
+ }
+ if (shortcutCells.getChildCount() > 0) {
+ for (int i = 0; i < shortcuts.size(); i++) {
+ WidgetCell shortcut = (WidgetCell) shortcutCells.getChildAt(i*2); // skip dividers
+ shortcut.applyFromCellItem(shortcuts.get(i), LauncherAppState.getInstance(mLauncher)
+ .getWidgetCache());
+ shortcut.ensurePreview();
+ shortcut.setVisibility(View.VISIBLE);
+ }
+ } else {
+ removeView(findViewById(R.id.shortcuts_header));
+ }
+
+ 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);
+ }
+
+ private void addDivider(ViewGroup parent) {
+ LayoutInflater.from(getContext()).inflate(R.layout.widget_list_divider, parent, true);
+ }
+
+ private void 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);
+ }
+
+ @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(WidgetsAndMore.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_AND_MORE) != 0;
+ }
+
+ @Override
+ public int getLogContainerType() {
+ return LauncherLogProto.ContainerType.WIDGETS; // TODO: be more specific
+ }
+
+ /**
+ * Returns a WidgetsAndMore which is already open or null
+ */
+ public static WidgetsAndMore getOpen(Launcher launcher) {
+ return getOpenView(launcher, TYPE_WIDGETS_AND_MORE);
+ }
+
+ @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() {
+ }
+}