Home | History | Annotate | Download | only in droptarget
      1 /*
      2  * Copyright (C) 2016 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.example.android.droptarget;
     18 
     19 import com.example.android.common.logger.Log;
     20 
     21 import android.app.Activity;
     22 import android.content.ClipDescription;
     23 import android.content.ContentResolver;
     24 import android.net.Uri;
     25 import android.os.Bundle;
     26 import android.os.PersistableBundle;
     27 import android.support.annotation.Nullable;
     28 import android.support.v13.app.ActivityCompat;
     29 import android.support.v13.view.DragAndDropPermissionsCompat;
     30 import android.support.v4.app.Fragment;
     31 import android.view.DragEvent;
     32 import android.view.LayoutInflater;
     33 import android.view.View;
     34 import android.view.ViewGroup;
     35 import android.widget.CheckBox;
     36 import android.widget.ImageView;
     37 
     38 /**
     39  * This sample demonstrates data can be moved between views in different applications via
     40  * drag and drop.
     41  * <p>This is the Target app for the drag and drop process. This app uses a
     42  * {@link android.widget.ImageView} as the drop target. Images onto this
     43  * view from the DragSource app that is also part of this sample.
     44  * <p>
     45  * There is also a {@link android.widget.EditText} widget that can accept dropped text (no
     46  * extra setup is necessary).
     47  * To access content URIs requiring permissions, the target app needs to request the
     48  * {@link android.view.DragAndDropPermissions} from the Activity via
     49  * {@link ActivityCompat#requestDragAndDropPermissions(Activity, DragEvent)}. This permission will
     50  * stay either as long as the activity is alive, or until the release() method is called on the
     51  * {@link android.view.DragAndDropPermissions} object.
     52  */
     53 public class DropTargetFragment extends Fragment {
     54 
     55     private static final String IMAGE_URI = "IMAGE_URI";
     56 
     57     public static final String EXTRA_IMAGE_INFO = "IMAGE_INFO";
     58 
     59     private static final String TAG = "DropTargetFragment";
     60 
     61     private Uri mImageUri;
     62 
     63     private CheckBox mReleasePermissionCheckBox;
     64 
     65     @Nullable
     66     @Override
     67     public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
     68             @Nullable Bundle savedInstanceState) {
     69 
     70         View rootView = inflater.inflate(R.layout.fragment_droptarget, container, false);
     71         final ImageView imageView = (ImageView) rootView.findViewById(R.id.image_target);
     72 
     73         ImageDragListener imageDragListener = new PermissionAwareImageDragListener();
     74 
     75         imageView.setOnDragListener(imageDragListener);
     76 
     77         // Restore the application state if an image was being displayed.
     78         if (savedInstanceState != null) {
     79             final String uriString = savedInstanceState.getString(IMAGE_URI);
     80             if (uriString != null) {
     81                 mImageUri = Uri.parse(uriString);
     82                 imageView.setImageURI(mImageUri);
     83             }
     84         }
     85 
     86         mReleasePermissionCheckBox = (CheckBox) rootView.findViewById(R.id.release_checkbox);
     87 
     88         return rootView;
     89     }
     90 
     91     @Override
     92     public void onSaveInstanceState(Bundle savedInstanceState) {
     93         if (mImageUri != null) {
     94             savedInstanceState.putString(IMAGE_URI, mImageUri.toString());
     95         }
     96         super.onSaveInstanceState(savedInstanceState);
     97     }
     98 
     99     private class PermissionAwareImageDragListener extends ImageDragListener {
    100 
    101         @Override
    102         protected void processLocation(float x, float y) {
    103             // Callback is received when the dragged image enters the drop area.
    104         }
    105 
    106         @Override
    107         protected boolean setImageUri(View view, DragEvent event, Uri uri) {
    108             // Read the string from the clip description extras.
    109             Log.d(TAG, "ClipDescription extra: " + getExtra(event));
    110 
    111             Log.d(TAG, "Setting image source to: " + uri.toString());
    112             mImageUri = uri;
    113 
    114             if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
    115                 // Accessing a "content" scheme Uri requires a permission grant.
    116                 DragAndDropPermissionsCompat dropPermissions = ActivityCompat
    117                         .requestDragAndDropPermissions(getActivity(), event);
    118                 Log.d(TAG, "Requesting permissions.");
    119 
    120                 if (dropPermissions == null) {
    121                     // Permission could not be obtained.
    122                     Log.d(TAG, "Drop permission request failed.");
    123                     return false;
    124                 }
    125 
    126                 final boolean result = super.setImageUri(view, event, uri);
    127 
    128                 if (mReleasePermissionCheckBox.isChecked()) {
    129                     /* Release the permissions if you are done with the URI.
    130                      Note that you may need to hold onto the permission until later if other
    131                      operations are performed on the content. For instance, releasing the
    132                      permissions here will prevent onCreateView from properly restoring the
    133                      ImageView state.
    134                      If permissions are not explicitly released, the permission grant will be
    135                      revoked when the activity is destroyed.
    136                      */
    137                     dropPermissions.release();
    138                     Log.d(TAG, "Permissions released.");
    139                 }
    140 
    141                 return result;
    142             } else {
    143                 // Other schemes (such as "android.resource") do not require a permission grant.
    144                 return super.setImageUri(view, event, uri);
    145             }
    146         }
    147 
    148         @Override
    149         public boolean onDrag(View view, DragEvent event) {
    150             // DragTarget is peeking into the MIME types of the dragged event in order to ignore
    151             // non-image drags completely.
    152             // DragSource does not do that but rejects non-image content once a drop has happened.
    153             ClipDescription clipDescription = event.getClipDescription();
    154             if (clipDescription != null && !clipDescription.hasMimeType("image/*")) {
    155                 return false;
    156             }
    157             // Callback received when image is being dragged.
    158             return super.onDrag(view, event);
    159         }
    160     }
    161 
    162     /**
    163      * DragEvents can contain additional data packaged in a {@link PersistableBundle}.
    164      * Extract the extras from the event and return the String stored for the
    165      * {@link #EXTRA_IMAGE_INFO} entry.
    166      */
    167     private String getExtra(DragEvent event) {
    168         // The extras are contained in the ClipDescription in the DragEvent.
    169         ClipDescription clipDescription = event.getClipDescription();
    170         if (clipDescription != null) {
    171             PersistableBundle extras = clipDescription.getExtras();
    172             if (extras != null) {
    173                 return extras.getString(EXTRA_IMAGE_INFO);
    174             }
    175         }
    176         return null;
    177     }
    178 }
    179