Home | History | Annotate | Download | only in launcher3
      1 /*
      2  * Copyright (C) 2015 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;
     18 
     19 import android.annotation.SuppressLint;
     20 import android.content.Context;
     21 import android.content.res.TypedArray;
     22 import android.graphics.PointF;
     23 import android.graphics.Rect;
     24 import android.graphics.drawable.ColorDrawable;
     25 import android.graphics.drawable.Drawable;
     26 import android.graphics.drawable.InsetDrawable;
     27 import android.util.AttributeSet;
     28 import android.view.MotionEvent;
     29 import android.view.View;
     30 import android.view.ViewConfiguration;
     31 import android.widget.FrameLayout;
     32 
     33 import com.android.launcher3.allapps.AllAppsContainerView;
     34 import com.android.launcher3.config.FeatureFlags;
     35 import com.android.launcher3.util.TransformingTouchDelegate;
     36 
     37 /**
     38  * A base container view, which supports resizing.
     39  */
     40 public abstract class BaseContainerView extends FrameLayout
     41         implements DeviceProfile.LauncherLayoutChangeListener {
     42 
     43     private static final Rect sBgPaddingRect = new Rect();
     44 
     45     protected final Drawable mBaseDrawable;
     46 
     47     private View mRevealView;
     48     private View mContent;
     49 
     50     private TransformingTouchDelegate mTouchDelegate;
     51 
     52     private final PointF mLastTouchDownPosPx = new PointF(-1.0f, -1.0f);
     53 
     54     public BaseContainerView(Context context) {
     55         this(context, null);
     56     }
     57 
     58     public BaseContainerView(Context context, AttributeSet attrs) {
     59         this(context, attrs, 0);
     60     }
     61 
     62     public BaseContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
     63         super(context, attrs, defStyleAttr);
     64 
     65         if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP && this instanceof AllAppsContainerView) {
     66             mBaseDrawable = new ColorDrawable();
     67         } else {
     68             TypedArray a = context.obtainStyledAttributes(attrs,
     69                     R.styleable.BaseContainerView, defStyleAttr, 0);
     70             mBaseDrawable = a.getDrawable(R.styleable.BaseContainerView_revealBackground);
     71             a.recycle();
     72         }
     73     }
     74 
     75     @Override
     76     protected void onAttachedToWindow() {
     77         super.onAttachedToWindow();
     78 
     79         DeviceProfile grid = Launcher.getLauncher(getContext()).getDeviceProfile();
     80         grid.addLauncherLayoutChangedListener(this);
     81 
     82         View touchDelegateTargetView = getTouchDelegateTargetView();
     83         if (touchDelegateTargetView != null) {
     84             mTouchDelegate = new TransformingTouchDelegate(touchDelegateTargetView);
     85             ((View) touchDelegateTargetView.getParent()).setTouchDelegate(mTouchDelegate);
     86         }
     87     }
     88 
     89     @Override
     90     protected void onDetachedFromWindow() {
     91         super.onDetachedFromWindow();
     92 
     93         DeviceProfile grid = Launcher.getLauncher(getContext()).getDeviceProfile();
     94         grid.removeLauncherLayoutChangedListener(this);
     95     }
     96 
     97     @Override
     98     protected void onFinishInflate() {
     99         super.onFinishInflate();
    100 
    101         mContent = findViewById(R.id.main_content);
    102         mRevealView = findViewById(R.id.reveal_view);
    103 
    104         updatePaddings();
    105     }
    106 
    107     @Override
    108     public void onLauncherLayoutChanged() {
    109         updatePaddings();
    110     }
    111 
    112     /**
    113      * Calculate the background padding as it can change due to insets/content padding change.
    114      */
    115     private void updatePaddings() {
    116         Context context = getContext();
    117         int paddingLeft;
    118         int paddingRight;
    119         int paddingTop;
    120         int paddingBottom;
    121 
    122         DeviceProfile grid = Launcher.getLauncher(context).getDeviceProfile();
    123         int[] padding = grid.getContainerPadding();
    124         paddingLeft = padding[0] + grid.edgeMarginPx;
    125         paddingRight = padding[1] + grid.edgeMarginPx;
    126         if (!grid.isVerticalBarLayout()) {
    127             paddingTop = paddingBottom = grid.edgeMarginPx;
    128         } else {
    129             paddingTop = paddingBottom = 0;
    130         }
    131         updateBackground(paddingLeft, paddingTop, paddingRight, paddingBottom);
    132     }
    133 
    134     /**
    135      * Update the background for the reveal view and content view based on the background padding.
    136      */
    137     protected void updateBackground(int paddingLeft, int paddingTop,
    138             int paddingRight, int paddingBottom) {
    139         mRevealView.setBackground(new InsetDrawable(mBaseDrawable,
    140                 paddingLeft, paddingTop, paddingRight, paddingBottom));
    141         mContent.setBackground(new InsetDrawable(mBaseDrawable,
    142                 paddingLeft, paddingTop, paddingRight, paddingBottom));
    143     }
    144 
    145     @Override
    146     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    147         super.onLayout(changed, left, top, right, bottom);
    148 
    149         View touchDelegateTargetView = getTouchDelegateTargetView();
    150         if (touchDelegateTargetView != null) {
    151             getRevealView().getBackground().getPadding(sBgPaddingRect);
    152             mTouchDelegate.setBounds(
    153                     touchDelegateTargetView.getLeft() - sBgPaddingRect.left,
    154                     touchDelegateTargetView.getTop() - sBgPaddingRect.top,
    155                     touchDelegateTargetView.getRight() + sBgPaddingRect.right,
    156                     touchDelegateTargetView.getBottom() + sBgPaddingRect.bottom);
    157         }
    158     }
    159 
    160     @Override
    161     public boolean onInterceptTouchEvent(MotionEvent ev) {
    162         return handleTouchEvent(ev);
    163     }
    164 
    165     @SuppressLint("ClickableViewAccessibility")
    166     @Override
    167     public boolean onTouchEvent(MotionEvent ev) {
    168         return handleTouchEvent(ev);
    169     }
    170 
    171     public void setRevealDrawableColor(int color) {
    172         ((ColorDrawable) mBaseDrawable).setColor(color);
    173     }
    174 
    175     public final View getContentView() {
    176         return mContent;
    177     }
    178 
    179     public final View getRevealView() {
    180         return mRevealView;
    181     }
    182 
    183 
    184     /**
    185      * Handles the touch events that shows the workspace when clicking outside the bounds of the
    186      * touch delegate target view.
    187      */
    188     private boolean handleTouchEvent(MotionEvent ev) {
    189         switch (ev.getAction()) {
    190             case MotionEvent.ACTION_DOWN:
    191                 // Check if the touch is outside touch delegate target view
    192                 View touchDelegateTargetView = getTouchDelegateTargetView();
    193                 float leftBoundPx = touchDelegateTargetView.getLeft();
    194                 if (ev.getX() < leftBoundPx ||
    195                         ev.getX() > (touchDelegateTargetView.getWidth() + leftBoundPx)) {
    196                     mLastTouchDownPosPx.set((int) ev.getX(), (int) ev.getY());
    197                 }
    198                 break;
    199             case MotionEvent.ACTION_UP:
    200                 if (mLastTouchDownPosPx.x > -1) {
    201                     ViewConfiguration viewConfig = ViewConfiguration.get(getContext());
    202                     float dx = ev.getX() - mLastTouchDownPosPx.x;
    203                     float dy = ev.getY() - mLastTouchDownPosPx.y;
    204                     float distance = PointF.length(dx, dy);
    205                     if (distance < viewConfig.getScaledTouchSlop()) {
    206                         // The background was clicked, so just go home
    207                         Launcher.getLauncher(getContext()).showWorkspace(true);
    208                         return true;
    209                     }
    210                 }
    211                 // Fall through
    212             case MotionEvent.ACTION_CANCEL:
    213                 mLastTouchDownPosPx.set(-1, -1);
    214                 break;
    215         }
    216         return false;
    217     }
    218 
    219     public abstract View getTouchDelegateTargetView();
    220 }
    221