/* * Copyright 2015, 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.example.android.dragsource; import com.example.android.common.logger.Log; import android.content.ClipData; import android.content.ClipDescription; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Point; import android.net.Uri; import android.os.Bundle; import android.os.PersistableBundle; import android.support.annotation.Nullable; import android.support.v13.view.DragStartHelper; import android.support.v4.app.Fragment; import android.support.v4.content.FileProvider; import android.view.DragEvent; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.util.Date; /** * This sample demonstrates data can be moved between views within the app or between different * apps via drag and drop. *

This is the source app for the drag and drop sample. This app contains several * {@link android.widget.ImageView} widgets which can be a drag source. Images can be dropped * to a drop target area within the same app or in the DropTarget app (a separate app in this * sample). *

* There is also one {@link android.widget.EditText} widget that can be a drag source (no extra * setup is necessary). *

* To enable cross application drag and drop, the {@link android.view.View#DRAG_FLAG_GLOBAL} * permission needs to be passed to the {@link android.view.View#startDragAndDrop(ClipData, * View.DragShadowBuilder, Object, int)} method. If a Uri * requiring permission grants is being sent, then the * {@link android.view.View#DRAG_FLAG_GLOBAL_URI_READ} and/or the * {@link android.view.View#DRAG_FLAG_GLOBAL_URI_WRITE} flags must be used also. */ public class DragSourceFragment extends Fragment { /** * Name of saved data that stores the dropped image URI on the local ImageView when set. */ private static final String IMAGE_URI = "IMAGE_URI"; /** * Name of the parameter for a {@link ClipData} extra that stores a text describing the dragged * image. */ public static final String EXTRA_IMAGE_INFO = "IMAGE_INFO"; /** * Uri of the ImageView source when set. */ private Uri mLocalImageUri; private static final String TAG = "DragSourceFragment"; private static final String CONTENT_AUTHORITY = "com.example.android.dragsource.fileprovider"; @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_dragsource, null); // Set up two image views for global drag and drop with a permission grant. Uri imageUri = getFileUri(R.drawable.image1, "image1.png"); ImageView imageView = (ImageView) view.findViewById(R.id.image_one); setUpDraggableImage(imageView, imageUri); imageView.setImageURI(imageUri); imageUri = getFileUri(R.drawable.image2, "image2.png"); imageView = (ImageView) view.findViewById(R.id.image_two); setUpDraggableImage(imageView, imageUri); imageView.setImageURI(imageUri); // Set up the local drop target area. final ImageView localImageTarget = (ImageView) view.findViewById(R.id.local_target); localImageTarget.setOnDragListener(new ImageDragListener() { @Override protected boolean setImageUri(View view, DragEvent event, Uri uri) { mLocalImageUri = uri; Log.d(TAG, "Setting local image to: " + uri); return super.setImageUri(view, event, uri); } }); if (savedInstanceState != null) { final String uriString = savedInstanceState.getString(IMAGE_URI); if (uriString != null) { mLocalImageUri = Uri.parse(uriString); Log.d(TAG, "Restoring local image to: " + mLocalImageUri); localImageTarget.setImageURI(mLocalImageUri); } } return view; } @Override public void onSaveInstanceState(Bundle savedInstanceState) { if (mLocalImageUri != null) { savedInstanceState.putString(IMAGE_URI, mLocalImageUri.toString()); } super.onSaveInstanceState(savedInstanceState); } private void setUpDraggableImage(ImageView imageView, final Uri imageUri) { // Set up a listener that starts the drag and drop event with flags and extra data. DragStartHelper.OnDragStartListener listener = new DragStartHelper.OnDragStartListener() { @Override public boolean onDragStart(View view, final DragStartHelper helper) { Log.d(TAG, "Drag start event received from helper."); // Use a DragShadowBuilder View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view) { @Override public void onProvideShadowMetrics(Point shadowSize, Point shadowTouchPoint) { super.onProvideShadowMetrics(shadowSize, shadowTouchPoint); // Notify the DragStartHelper of point where the view was touched. helper.getTouchPosition(shadowTouchPoint); Log.d(TAG, "View was touched at: " + shadowTouchPoint); } }; // Set up the flags for the drag event. // Enable drag and drop across apps (global) // and require read permissions for this URI. int flags = View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ; // Add an optional clip description that that contains an extra String that is // read out by the target app. final ClipDescription clipDescription = new ClipDescription("", new String[]{ getContext().getContentResolver().getType(imageUri)}); // Extras are stored within a PersistableBundle. PersistableBundle extras = new PersistableBundle(1); // Add a String that the target app will display. extras.putString(EXTRA_IMAGE_INFO, "Drag Started at " + new Date()); clipDescription.setExtras(extras); // The ClipData object describes the object that is being dragged and dropped. final ClipData clipData = new ClipData(clipDescription, new ClipData.Item(imageUri)); Log.d(TAG, "Created ClipDescription. Starting drag and drop."); // Start the drag and drop event. return view.startDragAndDrop(clipData, shadowBuilder, null, flags); } }; // Use the DragStartHelper to detect drag and drop events and use the OnDragStartListener // defined above to start the event when it has been detected. DragStartHelper helper = new DragStartHelper(imageView, listener); helper.attach(); Log.d(TAG, "DragStartHelper attached to view."); } /** * Copy a drawable resource into local storage and makes it available via the * {@link FileProvider}. * * @see Context#getFilesDir() * @see FileProvider * @see FileProvider#getUriForFile(Context, String, File) */ private Uri getFileUri(int sourceResourceId, String targetName) { // Create the images/ sub directory if it does not exist yet. File filePath = new File(getContext().getFilesDir(), "images"); if (!filePath.exists() && !filePath.mkdir()) { return null; } // Copy a drawable from resources to the internal directory. File newFile = new File(filePath, targetName); if (!newFile.exists()) { copyImageResourceToFile(sourceResourceId, newFile); } // Make the file accessible via the FileProvider and retrieve its URI. return FileProvider.getUriForFile(getContext(), CONTENT_AUTHORITY, newFile); } /** * Copy a PNG resource drawable to a {@File}. */ private void copyImageResourceToFile(int resourceId, File filePath) { Bitmap image = BitmapFactory.decodeResource(getResources(), resourceId); FileOutputStream out = null; try { out = new FileOutputStream(filePath); image.compress(Bitmap.CompressFormat.PNG, 100, out); } catch (Exception e) { e.printStackTrace(); } finally { try { if (out != null) { out.close(); } } catch (IOException e) { e.printStackTrace(); } } } }