Home | History | Annotate | Download | only in dragsource
      1 /*
      2  * Copyright 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.example.android.dragsource;
     18 
     19 import com.example.android.common.logger.Log;
     20 
     21 import android.content.ClipData;
     22 import android.content.ClipDescription;
     23 import android.content.Context;
     24 import android.graphics.Bitmap;
     25 import android.graphics.BitmapFactory;
     26 import android.graphics.Point;
     27 import android.net.Uri;
     28 import android.os.Bundle;
     29 import android.os.PersistableBundle;
     30 import android.support.annotation.Nullable;
     31 import android.support.v13.view.DragStartHelper;
     32 import android.support.v4.app.Fragment;
     33 import android.support.v4.content.FileProvider;
     34 import android.view.DragEvent;
     35 import android.view.LayoutInflater;
     36 import android.view.View;
     37 import android.view.ViewGroup;
     38 import android.widget.ImageView;
     39 
     40 import java.io.File;
     41 import java.io.FileOutputStream;
     42 import java.io.IOException;
     43 import java.util.Date;
     44 
     45 
     46 /**
     47  * This sample demonstrates data can be moved between views within the app or between different
     48  * apps via drag and drop.
     49  * <p>This is the source app for the drag and drop sample. This app contains several
     50  * {@link android.widget.ImageView} widgets which can be a drag source. Images can be dropped
     51  * to a drop target area within the same app or in the DropTarget app (a separate app in this
     52  * sample).
     53  * <p>
     54  * There is also one {@link android.widget.EditText} widget that can be a drag source (no extra
     55  * setup is necessary).
     56  * <p/>
     57  * To enable cross application drag and drop, the {@link android.view.View#DRAG_FLAG_GLOBAL}
     58  * permission needs to be passed to the {@link android.view.View#startDragAndDrop(ClipData,
     59  * View.DragShadowBuilder, Object, int)} method. If a Uri
     60  * requiring permission grants is being sent, then the
     61  * {@link android.view.View#DRAG_FLAG_GLOBAL_URI_READ} and/or the
     62  * {@link android.view.View#DRAG_FLAG_GLOBAL_URI_WRITE} flags must be used also.
     63  */
     64 public class DragSourceFragment extends Fragment {
     65 
     66     /**
     67      * Name of saved data that stores the dropped image URI on the local ImageView when set.
     68      */
     69     private static final String IMAGE_URI = "IMAGE_URI";
     70 
     71     /**
     72      * Name of the parameter for a {@link ClipData} extra that stores a text describing the dragged
     73      * image.
     74      */
     75     public static final String EXTRA_IMAGE_INFO = "IMAGE_INFO";
     76 
     77     /**
     78      * Uri of the ImageView source when set.
     79      */
     80     private Uri mLocalImageUri;
     81 
     82     private static final String TAG = "DragSourceFragment";
     83 
     84     private static final String CONTENT_AUTHORITY = "com.example.android.dragsource.fileprovider";
     85 
     86     @Nullable
     87     @Override
     88     public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
     89             @Nullable Bundle savedInstanceState) {
     90 
     91         View view = inflater.inflate(R.layout.fragment_dragsource, null);
     92 
     93         // Set up two image views for global drag and drop with a permission grant.
     94         Uri imageUri = getFileUri(R.drawable.image1, "image1.png");
     95         ImageView imageView = (ImageView) view.findViewById(R.id.image_one);
     96         setUpDraggableImage(imageView, imageUri);
     97         imageView.setImageURI(imageUri);
     98 
     99         imageUri = getFileUri(R.drawable.image2, "image2.png");
    100         imageView = (ImageView) view.findViewById(R.id.image_two);
    101         setUpDraggableImage(imageView, imageUri);
    102         imageView.setImageURI(imageUri);
    103 
    104         // Set up the local drop target area.
    105         final ImageView localImageTarget = (ImageView) view.findViewById(R.id.local_target);
    106         localImageTarget.setOnDragListener(new ImageDragListener() {
    107             @Override
    108             protected boolean setImageUri(View view, DragEvent event, Uri uri) {
    109                 mLocalImageUri = uri;
    110                 Log.d(TAG, "Setting local image to: " + uri);
    111                 return super.setImageUri(view, event, uri);
    112             }
    113         });
    114 
    115         if (savedInstanceState != null) {
    116             final String uriString = savedInstanceState.getString(IMAGE_URI);
    117             if (uriString != null) {
    118                 mLocalImageUri = Uri.parse(uriString);
    119                 Log.d(TAG, "Restoring local image to: " + mLocalImageUri);
    120                 localImageTarget.setImageURI(mLocalImageUri);
    121             }
    122         }
    123         return view;
    124     }
    125 
    126     @Override
    127     public void onSaveInstanceState(Bundle savedInstanceState) {
    128         if (mLocalImageUri != null) {
    129             savedInstanceState.putString(IMAGE_URI, mLocalImageUri.toString());
    130         }
    131         super.onSaveInstanceState(savedInstanceState);
    132     }
    133 
    134     private void setUpDraggableImage(ImageView imageView, final Uri imageUri) {
    135 
    136         // Set up a listener that starts the drag and drop event with flags and extra data.
    137         DragStartHelper.OnDragStartListener listener = new DragStartHelper.OnDragStartListener() {
    138             @Override
    139             public boolean onDragStart(View view, final DragStartHelper helper) {
    140                 Log.d(TAG, "Drag start event received from helper.");
    141 
    142                 // Use a DragShadowBuilder
    143                 View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view) {
    144                     @Override
    145                     public void onProvideShadowMetrics(Point shadowSize, Point shadowTouchPoint) {
    146                         super.onProvideShadowMetrics(shadowSize, shadowTouchPoint);
    147                         // Notify the DragStartHelper of point where the view was touched.
    148                         helper.getTouchPosition(shadowTouchPoint);
    149                         Log.d(TAG, "View was touched at: " + shadowTouchPoint);
    150                     }
    151                 };
    152 
    153                 // Set up the flags for the drag event.
    154                 // Enable drag and drop across apps (global)
    155                 // and require read permissions for this URI.
    156                 int flags = View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ;
    157 
    158                 // Add an optional clip description that that contains an extra String that is
    159                 // read out by the target app.
    160                 final ClipDescription clipDescription = new ClipDescription("", new String[]{
    161                         getContext().getContentResolver().getType(imageUri)});
    162                 // Extras are stored within a PersistableBundle.
    163                 PersistableBundle extras = new PersistableBundle(1);
    164                 // Add a String that the target app will display.
    165                 extras.putString(EXTRA_IMAGE_INFO,
    166                         "Drag Started at " + new Date());
    167                 clipDescription.setExtras(extras);
    168 
    169                 // The ClipData object describes the object that is being dragged and dropped.
    170                 final ClipData clipData =
    171                         new ClipData(clipDescription, new ClipData.Item(imageUri));
    172 
    173                 Log.d(TAG, "Created ClipDescription. Starting drag and drop.");
    174                 // Start the drag and drop event.
    175                 return view.startDragAndDrop(clipData, shadowBuilder, null, flags);
    176 
    177             }
    178 
    179         };
    180 
    181         // Use the DragStartHelper to detect drag and drop events and use the OnDragStartListener
    182         // defined above to start the event when it has been detected.
    183         DragStartHelper helper = new DragStartHelper(imageView, listener);
    184         helper.attach();
    185         Log.d(TAG, "DragStartHelper attached to view.");
    186     }
    187 
    188     /**
    189      * Copy a drawable resource into local storage and makes it available via the
    190      * {@link FileProvider}.
    191      *
    192      * @see Context#getFilesDir()
    193      * @see FileProvider
    194      * @see FileProvider#getUriForFile(Context, String, File)
    195      */
    196     private Uri getFileUri(int sourceResourceId, String targetName) {
    197         // Create the images/ sub directory if it does not exist yet.
    198         File filePath = new File(getContext().getFilesDir(), "images");
    199         if (!filePath.exists() && !filePath.mkdir()) {
    200             return null;
    201         }
    202 
    203         // Copy a drawable from resources to the internal directory.
    204         File newFile = new File(filePath, targetName);
    205         if (!newFile.exists()) {
    206             copyImageResourceToFile(sourceResourceId, newFile);
    207         }
    208 
    209         // Make the file accessible via the FileProvider and retrieve its URI.
    210         return FileProvider.getUriForFile(getContext(), CONTENT_AUTHORITY, newFile);
    211     }
    212 
    213 
    214     /**
    215      * Copy a PNG resource drawable to a {@File}.
    216      */
    217     private void copyImageResourceToFile(int resourceId, File filePath) {
    218         Bitmap image = BitmapFactory.decodeResource(getResources(), resourceId);
    219 
    220         FileOutputStream out = null;
    221         try {
    222             out = new FileOutputStream(filePath);
    223             image.compress(Bitmap.CompressFormat.PNG, 100, out);
    224         } catch (Exception e) {
    225             e.printStackTrace();
    226         } finally {
    227             try {
    228                 if (out != null) {
    229                     out.close();
    230                 }
    231             } catch (IOException e) {
    232                 e.printStackTrace();
    233             }
    234         }
    235     }
    236 
    237 }
    238