content: /*
   * Copyright (C) 2011 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;
  
  import static com.android.launcher3.ButtonDropTarget.TOOLTIP_DEFAULT;
  import static com.android.launcher3.ButtonDropTarget.TOOLTIP_LEFT;
  import static com.android.launcher3.ButtonDropTarget.TOOLTIP_RIGHT;
  import static com.android.launcher3.anim.AlphaUpdateListener.updateVisibility;
  
  import android.animation.TimeInterpolator;
  import android.content.Context;
  import android.graphics.Rect;
  import android.util.AttributeSet;
  import android.view.Gravity;
  import android.view.View;
  import android.view.ViewDebug;
  import android.view.ViewPropertyAnimator;
  import android.widget.FrameLayout;
  
  import com.android.launcher3.anim.Interpolators;
  import com.android.launcher3.dragndrop.DragController;
  import com.android.launcher3.dragndrop.DragController.DragListener;
  import com.android.launcher3.dragndrop.DragOptions;
  
  /*
   * The top bar containing various drop targets: Delete/App Info/Uninstall.
   */
  public class DropTargetBar extends FrameLayout
          implements DragListener, Insettable {
  
      protected static final int DEFAULT_DRAG_FADE_DURATION = 175;
      protected static final TimeInterpolator DEFAULT_INTERPOLATOR = Interpolators.ACCEL;
  
      private final Runnable mFadeAnimationEndRunnable =
              () -> updateVisibility(DropTargetBar.this);
  
      @ViewDebug.ExportedProperty(category = "launcher")
      protected boolean mDeferOnDragEnd;
  
      @ViewDebug.ExportedProperty(category = "launcher")
      protected boolean mVisible = false;
  
      private ButtonDropTarget[] mDropTargets;
      private ViewPropertyAnimator mCurrentAnimation;
  
      private boolean mIsVertical = true;
  
      public DropTargetBar(Context context, AttributeSet attrs) {
          super(context, attrs);
      }
  
      public DropTargetBar(Context context, AttributeSet attrs, int defStyle) {
          super(context, attrs, defStyle);
      }
  
      @Override
      protected void onFinishInflate() {
          super.onFinishInflate();
          mDropTargets = new ButtonDropTarget[getChildCount()];
          for (int i = 0; i < mDropTargets.length; i++) {
              mDropTargets[i] = (ButtonDropTarget) getChildAt(i);
              mDropTargets[i].setDropTargetBar(this);
          }
      }
  
      @Override
      public void setInsets(Rect insets) {
          FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
          DeviceProfile grid = Launcher.getLauncher(getContext()).getDeviceProfile();
          mIsVertical = grid.isVerticalBarLayout();
  
          lp.leftMargin = insets.left;
          lp.topMargin = insets.top;
          lp.bottomMargin = insets.bottom;
          lp.rightMargin = insets.right;
          int tooltipLocation = TOOLTIP_DEFAULT;
  
          if (grid.isVerticalBarLayout()) {
              lp.width = grid.dropTargetBarSizePx;
              lp.height = grid.availableHeightPx - 2 * grid.edgeMarginPx;
              lp.gravity = grid.isSeascape() ? Gravity.RIGHT : Gravity.LEFT;
              tooltipLocation = grid.isSeascape() ? TOOLTIP_LEFT : TOOLTIP_RIGHT;
          } else {
              int gap;
              if (grid.isTablet) {
                  // XXX: If the icon size changes across orientations, we will have to take
                  //      that into account here too.
                  gap = ((grid.widthPx - 2 * grid.edgeMarginPx
                          - (grid.inv.numColumns * grid.cellWidthPx))
                          / (2 * (grid.inv.numColumns + 1)))
                          + grid.edgeMarginPx;
              } else {
                  gap = grid.desiredWorkspaceLeftRightMarginPx - grid.inv.defaultWidgetPadding.right;
              }
              lp.width = grid.availableWidthPx - 2 * gap;
  
              lp.topMargin += grid.edgeMarginPx;
              lp.height = grid.dropTargetBarSizePx;
              lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
          }
          setLayoutParams(lp);
          for (ButtonDropTarget button : mDropTargets) {
              button.setToolTipLocation(tooltipLocation);
          }
      }
  
      public void setup(DragController dragController) {
          dragController.addDragListener(this);
          for (int i = 0; i < mDropTargets.length; i++) {
              dragController.addDragListener(mDropTargets[i]);
              dragController.addDropTarget(mDropTargets[i]);
          }
      }
  
      @Override
      protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
          int width = MeasureSpec.getSize(widthMeasureSpec);
          int height = MeasureSpec.getSize(heightMeasureSpec);
  
          if (mIsVertical) {
              int widthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
              int heightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
  
              for (ButtonDropTarget button : mDropTargets) {
                  if (button.getVisibility() != GONE) {
                      button.setTextVisible(false);
                      button.measure(widthSpec, heightSpec);
                  }
              }
          } else {
              int visibleCount = getVisibleButtonsCount();
              int availableWidth = width / visibleCount;
              boolean textVisible = true;
              for (ButtonDropTarget buttons : mDropTargets) {
                  if (buttons.getVisibility() != GONE) {
                      textVisible = textVisible && !buttons.isTextTruncated(availableWidth);
                  }
              }
  
              int widthSpec = MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST);
              int heightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
              for (ButtonDropTarget button : mDropTargets) {
                  if (button.getVisibility() != GONE) {
                      button.setTextVisible(textVisible);
                      button.measure(widthSpec, heightSpec);
                  }
              }
          }
          setMeasuredDimension(width, height);
      }
  
      @Override
      protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
          if (mIsVertical) {
              int gap = getResources().getDimensionPixelSize(R.dimen.drop_target_vertical_gap);
              int start = gap;
              int end;
  
              for (ButtonDropTarget button : mDropTargets) {
                  if (button.getVisibility() != GONE) {
                      end = start + button.getMeasuredHeight();
                      button.layout(0, start, button.getMeasuredWidth(), end);
                      start = end + gap;
                  }
              }
          } else {
              int visibleCount = getVisibleButtonsCount();
              int frameSize = (right - left) / visibleCount;
  
              int start = frameSize / 2;
              int halfWidth;
              for (ButtonDropTarget button : mDropTargets) {
                  if (button.getVisibility() != GONE) {
                      halfWidth = button.getMeasuredWidth() / 2;
                      button.layout(start - halfWidth, 0,
                              start + halfWidth, button.getMeasuredHeight());
                      start = start + frameSize;
                  }
              }
          }
      }
  
      private int getVisibleButtonsCount() {
          int visibleCount = 0;
          for (ButtonDropTarget buttons : mDropTargets) {
              if (buttons.getVisibility() != GONE) {
                  visibleCount++;
              }
          }
          return visibleCount;
      }
  
      public void animateToVisibility(boolean isVisible) {
          if (mVisible != isVisible) {
              mVisible = isVisible;
  
              // Cancel any existing animation
              if (mCurrentAnimation != null) {
                  mCurrentAnimation.cancel();
                  mCurrentAnimation = null;
              }
  
              float finalAlpha = mVisible ? 1 : 0;
              if (Float.compare(getAlpha(), finalAlpha) != 0) {
                  setVisibility(View.VISIBLE);
                  mCurrentAnimation = animate().alpha(finalAlpha)
                          .setInterpolator(DEFAULT_INTERPOLATOR)
                          .setDuration(DEFAULT_DRAG_FADE_DURATION)
                          .withEndAction(mFadeAnimationEndRunnable);
              }
  
          }
      }
  
      /*
       * DragController.DragListener implementation
       */
      @Override
      public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
          animateToVisibility(true);
      }
  
      /**
       * This is called to defer hiding the delete drop target until the drop animation has completed,
       * instead of hiding immediately when the drag has ended.
       */
      protected void deferOnDragEnd() {
          mDeferOnDragEnd = true;
      }
  
      @Override
      public void onDragEnd() {
          if (!mDeferOnDragEnd) {
              animateToVisibility(false);
          } else {
              mDeferOnDragEnd = false;
          }
      }
  
      public ButtonDropTarget[] getDropTargets() {
          return mDropTargets;
      }
  }
  