content: /*
   * 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.dragndrop;
  
  import android.content.ClipDescription;
  import android.content.Intent;
  import android.graphics.Point;
  import android.graphics.Rect;
  import android.os.Handler;
  import android.os.Looper;
  import android.os.Parcel;
  import android.os.SystemClock;
  import android.util.Log;
  import android.view.DragEvent;
  import android.view.View;
  
  import com.android.launcher3.DeleteDropTarget;
  import com.android.launcher3.DragSource;
  import com.android.launcher3.DropTarget;
  import com.android.launcher3.Launcher;
  import com.android.launcher3.R;
  import com.android.launcher3.folder.Folder;
  import com.android.launcher3.widget.PendingItemDragHelper;
  
  import java.util.UUID;
  
  /**
   * {@link DragSource} for handling drop from a different window.
   */
  public abstract class BaseItemDragListener implements
          View.OnDragListener, DragSource, DragOptions.PreDragCondition {
  
      private static final String TAG = "BaseItemDragListener";
  
      private static final String MIME_TYPE_PREFIX = "com.android.launcher3.drag_and_drop/";
      public static final String EXTRA_PIN_ITEM_DRAG_LISTENER = "pin_item_drag_listener";
  
      // Position of preview relative to the touch location
      private final Rect mPreviewRect;
  
      private final int mPreviewBitmapWidth;
      private final int mPreviewViewWidth;
  
      // Randomly generated id used to verify the drag event.
      private final String mId;
  
      protected Launcher mLauncher;
      private DragController mDragController;
      private long mDragStartTime;
  
      public BaseItemDragListener(Rect previewRect, int previewBitmapWidth, int previewViewWidth) {
          mPreviewRect = previewRect;
          mPreviewBitmapWidth = previewBitmapWidth;
          mPreviewViewWidth = previewViewWidth;
          mId = UUID.randomUUID().toString();
      }
  
      protected BaseItemDragListener(Parcel parcel) {
          mPreviewRect = Rect.CREATOR.createFromParcel(parcel);
          mPreviewBitmapWidth = parcel.readInt();
          mPreviewViewWidth = parcel.readInt();
          mId = parcel.readString();
      }
  
      protected void writeToParcel(Parcel parcel, int i) {
          mPreviewRect.writeToParcel(parcel, i);
          parcel.writeInt(mPreviewBitmapWidth);
          parcel.writeInt(mPreviewViewWidth);
          parcel.writeString(mId);
      }
  
      public String getMimeType() {
          return MIME_TYPE_PREFIX + mId;
      }
  
      public void setLauncher(Launcher launcher) {
          mLauncher = launcher;
          mDragController = launcher.getDragController();
      }
  
      @Override
      public boolean onDrag(View view, DragEvent event) {
          if (mLauncher == null || mDragController == null) {
              postCleanup();
              return false;
          }
          if (event.getAction() == DragEvent.ACTION_DRAG_STARTED) {
              if (onDragStart(event)) {
                  return true;
              } else {
                  postCleanup();
                  return false;
              }
          }
          return mDragController.onDragEvent(mDragStartTime, event);
      }
  
      protected boolean onDragStart(DragEvent event) {
          ClipDescription desc =  event.getClipDescription();
          if (desc == null || !desc.hasMimeType(getMimeType())) {
              Log.e(TAG, "Someone started a dragAndDrop before us.");
              return false;
          }
  
          Point downPos = new Point((int) event.getX(), (int) event.getY());
          DragOptions options = new DragOptions();
          options.systemDndStartPoint = downPos;
          options.preDragCondition = this;
  
          // We use drag event position as the screenPos for the preview image. Since mPreviewRect
          // already includes the view position relative to the drag event on the source window,
          // and the absolute position (position relative to the screen) of drag event is same
          // across windows, using drag position here give a good estimate for relative position
          // to source window.
          createDragHelper().startDrag(new Rect(mPreviewRect),
                  mPreviewBitmapWidth, mPreviewViewWidth, downPos,  this, options);
          mDragStartTime = SystemClock.uptimeMillis();
          return true;
      }
  
      protected abstract PendingItemDragHelper createDragHelper();
  
      @Override
      public boolean shouldStartDrag(double distanceDragged) {
          // Stay in pre-drag mode, if workspace is locked.
          return !mLauncher.isWorkspaceLocked();
      }
  
      @Override
      public void onPreDragStart(DropTarget.DragObject dragObject) {
          // The predrag starts when the workspace is not yet loaded. In some cases we set
          // the dragLayer alpha to 0 to have a nice fade-in animation. But that will prevent the
          // dragView from being visible. Instead just skip the fade-in animation here.
          mLauncher.getDragLayer().setAlpha(1);
  
          dragObject.dragView.setColor(
                  mLauncher.getResources().getColor(R.color.delete_target_hover_tint));
      }
  
      @Override
      public void onPreDragEnd(DropTarget.DragObject dragObject, boolean dragStarted) {
          if (dragStarted) {
              dragObject.dragView.setColor(0);
          }
      }
  
      @Override
      public boolean supportsAppInfoDropTarget() {
          return false;
      }
  
      @Override
      public boolean supportsDeleteDropTarget() {
          return false;
      }
  
      @Override
      public float getIntrinsicIconScaleFactor() {
          return 1f;
      }
  
      @Override
      public void onDropCompleted(View target, DropTarget.DragObject d, boolean isFlingToDelete,
              boolean success) {
          if (isFlingToDelete || !success || (target != mLauncher.getWorkspace() &&
                  !(target instanceof DeleteDropTarget) && !(target instanceof Folder))) {
              // Exit spring loaded mode if we have not successfully dropped or have not handled the
              // drop in Workspace
              mLauncher.exitSpringLoadedDragModeDelayed(true,
                      Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
          }
  
          if (!success) {
              d.deferDragViewCleanupPostAnimation = false;
          }
          postCleanup();
      }
  
      private void postCleanup() {
          if (mLauncher != null) {
              // Remove any drag params from the launcher intent since the drag operation is complete.
              Intent newIntent = new Intent(mLauncher.getIntent());
              newIntent.removeExtra(EXTRA_PIN_ITEM_DRAG_LISTENER);
              mLauncher.setIntent(newIntent);
          }
  
          new Handler(Looper.getMainLooper()).post(new Runnable() {
              @Override
              public void run() {
                  removeListener();
              }
          });
      }
  
      public void removeListener() {
          if (mLauncher != null) {
              mLauncher.getDragLayer().setOnDragListener(null);
          }
      }
  }
  