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