Home | History | Annotate | Download | only in launcher3
      1 /*
      2  * Copyright (C) 2011 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 static com.android.launcher3.ButtonDropTarget.TOOLTIP_DEFAULT;
     20 import static com.android.launcher3.ButtonDropTarget.TOOLTIP_LEFT;
     21 import static com.android.launcher3.ButtonDropTarget.TOOLTIP_RIGHT;
     22 import static com.android.launcher3.anim.AlphaUpdateListener.updateVisibility;
     23 
     24 import android.animation.TimeInterpolator;
     25 import android.content.Context;
     26 import android.graphics.Rect;
     27 import android.util.AttributeSet;
     28 import android.view.Gravity;
     29 import android.view.View;
     30 import android.view.ViewDebug;
     31 import android.view.ViewPropertyAnimator;
     32 import android.widget.FrameLayout;
     33 
     34 import com.android.launcher3.anim.Interpolators;
     35 import com.android.launcher3.dragndrop.DragController;
     36 import com.android.launcher3.dragndrop.DragController.DragListener;
     37 import com.android.launcher3.dragndrop.DragOptions;
     38 
     39 /*
     40  * The top bar containing various drop targets: Delete/App Info/Uninstall.
     41  */
     42 public class DropTargetBar extends FrameLayout
     43         implements DragListener, Insettable {
     44 
     45     protected static final int DEFAULT_DRAG_FADE_DURATION = 175;
     46     protected static final TimeInterpolator DEFAULT_INTERPOLATOR = Interpolators.ACCEL;
     47 
     48     private final Runnable mFadeAnimationEndRunnable =
     49             () -> updateVisibility(DropTargetBar.this);
     50 
     51     @ViewDebug.ExportedProperty(category = "launcher")
     52     protected boolean mDeferOnDragEnd;
     53 
     54     @ViewDebug.ExportedProperty(category = "launcher")
     55     protected boolean mVisible = false;
     56 
     57     private ButtonDropTarget[] mDropTargets;
     58     private ViewPropertyAnimator mCurrentAnimation;
     59 
     60     private boolean mIsVertical = true;
     61 
     62     public DropTargetBar(Context context, AttributeSet attrs) {
     63         super(context, attrs);
     64     }
     65 
     66     public DropTargetBar(Context context, AttributeSet attrs, int defStyle) {
     67         super(context, attrs, defStyle);
     68     }
     69 
     70     @Override
     71     protected void onFinishInflate() {
     72         super.onFinishInflate();
     73         mDropTargets = new ButtonDropTarget[getChildCount()];
     74         for (int i = 0; i < mDropTargets.length; i++) {
     75             mDropTargets[i] = (ButtonDropTarget) getChildAt(i);
     76             mDropTargets[i].setDropTargetBar(this);
     77         }
     78     }
     79 
     80     @Override
     81     public void setInsets(Rect insets) {
     82         FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
     83         DeviceProfile grid = Launcher.getLauncher(getContext()).getDeviceProfile();
     84         mIsVertical = grid.isVerticalBarLayout();
     85 
     86         lp.leftMargin = insets.left;
     87         lp.topMargin = insets.top;
     88         lp.bottomMargin = insets.bottom;
     89         lp.rightMargin = insets.right;
     90         int tooltipLocation = TOOLTIP_DEFAULT;
     91 
     92         if (grid.isVerticalBarLayout()) {
     93             lp.width = grid.dropTargetBarSizePx;
     94             lp.height = grid.availableHeightPx - 2 * grid.edgeMarginPx;
     95             lp.gravity = grid.isSeascape() ? Gravity.RIGHT : Gravity.LEFT;
     96             tooltipLocation = grid.isSeascape() ? TOOLTIP_LEFT : TOOLTIP_RIGHT;
     97         } else {
     98             int gap;
     99             if (grid.isTablet) {
    100                 // XXX: If the icon size changes across orientations, we will have to take
    101                 //      that into account here too.
    102                 gap = ((grid.widthPx - 2 * grid.edgeMarginPx
    103                         - (grid.inv.numColumns * grid.cellWidthPx))
    104                         / (2 * (grid.inv.numColumns + 1)))
    105                         + grid.edgeMarginPx;
    106             } else {
    107                 gap = grid.desiredWorkspaceLeftRightMarginPx - grid.defaultWidgetPadding.right;
    108             }
    109             lp.width = grid.availableWidthPx - 2 * gap;
    110 
    111             lp.topMargin += grid.edgeMarginPx;
    112             lp.height = grid.dropTargetBarSizePx;
    113             lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
    114         }
    115         setLayoutParams(lp);
    116         for (ButtonDropTarget button : mDropTargets) {
    117             button.setToolTipLocation(tooltipLocation);
    118         }
    119     }
    120 
    121     public void setup(DragController dragController) {
    122         dragController.addDragListener(this);
    123         for (int i = 0; i < mDropTargets.length; i++) {
    124             dragController.addDragListener(mDropTargets[i]);
    125             dragController.addDropTarget(mDropTargets[i]);
    126         }
    127     }
    128 
    129     @Override
    130     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    131         int width = MeasureSpec.getSize(widthMeasureSpec);
    132         int height = MeasureSpec.getSize(heightMeasureSpec);
    133 
    134         if (mIsVertical) {
    135             int widthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
    136             int heightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
    137 
    138             for (ButtonDropTarget button : mDropTargets) {
    139                 if (button.getVisibility() != GONE) {
    140                     button.setTextVisible(false);
    141                     button.measure(widthSpec, heightSpec);
    142                 }
    143             }
    144         } else {
    145             int visibleCount = getVisibleButtonsCount();
    146             int availableWidth = width / visibleCount;
    147             boolean textVisible = true;
    148             for (ButtonDropTarget buttons : mDropTargets) {
    149                 if (buttons.getVisibility() != GONE) {
    150                     textVisible = textVisible && !buttons.isTextTruncated(availableWidth);
    151                 }
    152             }
    153 
    154             int widthSpec = MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST);
    155             int heightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
    156             for (ButtonDropTarget button : mDropTargets) {
    157                 if (button.getVisibility() != GONE) {
    158                     button.setTextVisible(textVisible);
    159                     button.measure(widthSpec, heightSpec);
    160                 }
    161             }
    162         }
    163         setMeasuredDimension(width, height);
    164     }
    165 
    166     @Override
    167     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    168         if (mIsVertical) {
    169             int gap = getResources().getDimensionPixelSize(R.dimen.drop_target_vertical_gap);
    170             int start = gap;
    171             int end;
    172 
    173             for (ButtonDropTarget button : mDropTargets) {
    174                 if (button.getVisibility() != GONE) {
    175                     end = start + button.getMeasuredHeight();
    176                     button.layout(0, start, button.getMeasuredWidth(), end);
    177                     start = end + gap;
    178                 }
    179             }
    180         } else {
    181             int visibleCount = getVisibleButtonsCount();
    182             int frameSize = (right - left) / visibleCount;
    183 
    184             int start = frameSize / 2;
    185             int halfWidth;
    186             for (ButtonDropTarget button : mDropTargets) {
    187                 if (button.getVisibility() != GONE) {
    188                     halfWidth = button.getMeasuredWidth() / 2;
    189                     button.layout(start - halfWidth, 0,
    190                             start + halfWidth, button.getMeasuredHeight());
    191                     start = start + frameSize;
    192                 }
    193             }
    194         }
    195     }
    196 
    197     private int getVisibleButtonsCount() {
    198         int visibleCount = 0;
    199         for (ButtonDropTarget buttons : mDropTargets) {
    200             if (buttons.getVisibility() != GONE) {
    201                 visibleCount++;
    202             }
    203         }
    204         return visibleCount;
    205     }
    206 
    207     private void animateToVisibility(boolean isVisible) {
    208         if (mVisible != isVisible) {
    209             mVisible = isVisible;
    210 
    211             // Cancel any existing animation
    212             if (mCurrentAnimation != null) {
    213                 mCurrentAnimation.cancel();
    214                 mCurrentAnimation = null;
    215             }
    216 
    217             float finalAlpha = mVisible ? 1 : 0;
    218             if (Float.compare(getAlpha(), finalAlpha) != 0) {
    219                 setVisibility(View.VISIBLE);
    220                 mCurrentAnimation = animate().alpha(finalAlpha)
    221                         .setInterpolator(DEFAULT_INTERPOLATOR)
    222                         .setDuration(DEFAULT_DRAG_FADE_DURATION)
    223                         .withEndAction(mFadeAnimationEndRunnable);
    224             }
    225 
    226         }
    227     }
    228 
    229     /*
    230      * DragController.DragListener implementation
    231      */
    232     @Override
    233     public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
    234         animateToVisibility(true);
    235     }
    236 
    237     /**
    238      * This is called to defer hiding the delete drop target until the drop animation has completed,
    239      * instead of hiding immediately when the drag has ended.
    240      */
    241     protected void deferOnDragEnd() {
    242         mDeferOnDragEnd = true;
    243     }
    244 
    245     @Override
    246     public void onDragEnd() {
    247         if (!mDeferOnDragEnd) {
    248             animateToVisibility(false);
    249         } else {
    250             mDeferOnDragEnd = false;
    251         }
    252     }
    253 
    254     public ButtonDropTarget[] getDropTargets() {
    255         return mDropTargets;
    256     }
    257 }
    258