Home | History | Annotate | Download | only in widget
      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 package com.android.launcher3.widget;
     17 
     18 import android.animation.Animator;
     19 import android.animation.AnimatorListenerAdapter;
     20 import android.animation.PropertyValuesHolder;
     21 import android.content.Context;
     22 import android.graphics.Rect;
     23 import android.util.AttributeSet;
     24 import android.util.Pair;
     25 import android.view.LayoutInflater;
     26 import android.view.MotionEvent;
     27 import android.view.View;
     28 import android.view.animation.AnimationUtils;
     29 
     30 import com.android.launcher3.Insettable;
     31 import com.android.launcher3.Launcher;
     32 import com.android.launcher3.LauncherAppState;
     33 import com.android.launcher3.LauncherAppWidgetHost.ProviderChangedListener;
     34 import com.android.launcher3.R;
     35 import com.android.launcher3.views.RecyclerViewFastScroller;
     36 import com.android.launcher3.views.TopRoundedCornerView;
     37 
     38 /**
     39  * Popup for showing the full list of available widgets
     40  */
     41 public class WidgetsFullSheet extends BaseWidgetSheet
     42         implements Insettable, ProviderChangedListener {
     43 
     44     private static final long DEFAULT_OPEN_DURATION = 267;
     45     private static final long FADE_IN_DURATION = 150;
     46     private static final float VERTICAL_START_POSITION = 0.3f;
     47 
     48     private final Rect mInsets = new Rect();
     49 
     50     private final WidgetsListAdapter mAdapter;
     51 
     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 
     63     public WidgetsFullSheet(Context context, AttributeSet attrs) {
     64         this(context, attrs, 0);
     65     }
     66 
     67     @Override
     68     protected void onFinishInflate() {
     69         super.onFinishInflate();
     70         mContent = findViewById(R.id.container);
     71 
     72         mRecyclerView = findViewById(R.id.widgets_list_view);
     73         mRecyclerView.setAdapter(mAdapter);
     74         mAdapter.setApplyBitmapDeferred(true, mRecyclerView);
     75 
     76         TopRoundedCornerView springLayout = (TopRoundedCornerView) mContent;
     77         springLayout.addSpringView(R.id.widgets_list_view);
     78         mRecyclerView.setEdgeEffectFactory(springLayout.createEdgeEffectFactory());
     79         onWidgetsBound();
     80     }
     81 
     82     @Override
     83     protected Pair<View, String> getAccessibilityTarget() {
     84         return Pair.create(mRecyclerView, getContext().getString(
     85                 mIsOpen ? R.string.widgets_list : R.string.widgets_list_closed));
     86     }
     87 
     88     @Override
     89     protected void onAttachedToWindow() {
     90         super.onAttachedToWindow();
     91         mLauncher.getAppWidgetHost().addProviderChangeListener(this);
     92         notifyWidgetProvidersChanged();
     93     }
     94 
     95     @Override
     96     protected void onDetachedFromWindow() {
     97         super.onDetachedFromWindow();
     98         mLauncher.getAppWidgetHost().removeProviderChangeListener(this);
     99     }
    100 
    101     @Override
    102     public void setInsets(Rect insets) {
    103         mInsets.set(insets);
    104 
    105         mRecyclerView.setPadding(
    106                 mRecyclerView.getPaddingLeft(), mRecyclerView.getPaddingTop(),
    107                 mRecyclerView.getPaddingRight(), insets.bottom);
    108         if (insets.bottom > 0) {
    109             setupNavBarColor();
    110         } else {
    111             clearNavBarColor();
    112         }
    113 
    114         ((TopRoundedCornerView) mContent).setNavBarScrimHeight(mInsets.bottom);
    115         requestLayout();
    116     }
    117 
    118     @Override
    119     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    120         int widthUsed;
    121         if (mInsets.bottom > 0) {
    122             widthUsed = 0;
    123         } else {
    124             Rect padding = mLauncher.getDeviceProfile().workspacePadding;
    125             widthUsed = Math.max(padding.left + padding.right,
    126                     2 * (mInsets.left + mInsets.right));
    127         }
    128 
    129         int heightUsed = mInsets.top + mLauncher.getDeviceProfile().edgeMarginPx;
    130         measureChildWithMargins(mContent, widthMeasureSpec,
    131                 widthUsed, heightMeasureSpec, heightUsed);
    132         setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec),
    133                 MeasureSpec.getSize(heightMeasureSpec));
    134     }
    135 
    136     @Override
    137     protected void onLayout(boolean changed, int l, int t, int r, int b) {
    138         int width = r - l;
    139         int height = b - t;
    140 
    141         // Content is laid out as center bottom aligned
    142         int contentWidth = mContent.getMeasuredWidth();
    143         int contentLeft = (width - contentWidth) / 2;
    144         mContent.layout(contentLeft, height - mContent.getMeasuredHeight(),
    145                 contentLeft + contentWidth, height);
    146 
    147         setTranslationShift(mTranslationShift);
    148     }
    149 
    150     @Override
    151     public void notifyWidgetProvidersChanged() {
    152         mLauncher.refreshAndBindWidgetsForPackageUser(null);
    153     }
    154 
    155     @Override
    156     protected void onWidgetsBound() {
    157         mAdapter.setWidgets(mLauncher.getPopupDataProvider().getAllWidgets());
    158     }
    159 
    160     private void open(boolean animate) {
    161         if (animate) {
    162             if (mLauncher.getDragLayer().getInsets().bottom > 0) {
    163                 mContent.setAlpha(0);
    164                 setTranslationShift(VERTICAL_START_POSITION);
    165             }
    166             mOpenCloseAnimator.setValues(
    167                     PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
    168             mOpenCloseAnimator
    169                     .setDuration(DEFAULT_OPEN_DURATION)
    170                     .setInterpolator(AnimationUtils.loadInterpolator(
    171                             getContext(), android.R.interpolator.linear_out_slow_in));
    172             mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() {
    173                 @Override
    174                 public void onAnimationEnd(Animator animation) {
    175                     mRecyclerView.setLayoutFrozen(false);
    176                     mAdapter.setApplyBitmapDeferred(false, mRecyclerView);
    177                     mOpenCloseAnimator.removeListener(this);
    178                 }
    179             });
    180             post(() -> {
    181                 mRecyclerView.setLayoutFrozen(true);
    182                 mOpenCloseAnimator.start();
    183                 mContent.animate().alpha(1).setDuration(FADE_IN_DURATION);
    184             });
    185         } else {
    186             setTranslationShift(TRANSLATION_SHIFT_OPENED);
    187             mAdapter.setApplyBitmapDeferred(false, mRecyclerView);
    188             post(this::announceAccessibilityChanges);
    189         }
    190     }
    191 
    192     @Override
    193     protected void handleClose(boolean animate) {
    194         handleClose(animate, DEFAULT_OPEN_DURATION);
    195     }
    196 
    197     @Override
    198     protected boolean isOfType(int type) {
    199         return (type & TYPE_WIDGETS_FULL_SHEET) != 0;
    200     }
    201 
    202     @Override
    203     public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
    204         // Disable swipe down when recycler view is scrolling
    205         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
    206             mNoIntercept = false;
    207             RecyclerViewFastScroller scroller = mRecyclerView.getScrollbar();
    208             if (scroller.getThumbOffsetY() >= 0 &&
    209                     mLauncher.getDragLayer().isEventOverView(scroller, ev)) {
    210                 mNoIntercept = true;
    211             } else if (mLauncher.getDragLayer().isEventOverView(mContent, ev)) {
    212                 mNoIntercept = !mRecyclerView.shouldContainerScroll(ev, mLauncher.getDragLayer());
    213             }
    214         }
    215         return super.onControllerInterceptTouchEvent(ev);
    216     }
    217 
    218     public static WidgetsFullSheet show(Launcher launcher, boolean animate) {
    219         WidgetsFullSheet sheet = (WidgetsFullSheet) launcher.getLayoutInflater()
    220                 .inflate(R.layout.widgets_full_sheet, launcher.getDragLayer(), false);
    221         sheet.mIsOpen = true;
    222         launcher.getDragLayer().addView(sheet);
    223         sheet.open(animate);
    224         return sheet;
    225     }
    226 
    227     @Override
    228     protected int getElementsRowCount() {
    229         return mAdapter.getItemCount();
    230     }
    231 }
    232