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 android.wm.cts.dndtargetapp; 18 19 import android.app.Activity; 20 import android.content.ClipData; 21 import android.content.ContentValues; 22 import android.database.Cursor; 23 import android.net.Uri; 24 import android.os.Bundle; 25 import android.os.PersistableBundle; 26 import android.util.Log; 27 import android.view.DragAndDropPermissions; 28 import android.view.DragEvent; 29 import android.view.View; 30 import android.widget.TextView; 31 32 public class DropTarget extends Activity { 33 public static final String LOG_TAG = "DropTarget"; 34 35 private static final String RESULT_KEY_DRAG_STARTED = "DRAG_STARTED"; 36 private static final String RESULT_KEY_EXTRAS = "EXTRAS"; 37 private static final String RESULT_KEY_DROP_RESULT = "DROP"; 38 private static final String RESULT_KEY_DETAILS = "DETAILS"; 39 40 public static final String RESULT_OK = "OK"; 41 public static final String RESULT_EXCEPTION = "Exception"; 42 43 protected static final String MAGIC_VALUE = "42"; 44 45 private TextView mTextView; 46 47 @Override onCreate(Bundle savedInstanceState)48 public void onCreate(Bundle savedInstanceState) { 49 super.onCreate(savedInstanceState); 50 51 View view = getLayoutInflater().inflate(R.layout.target_activity, null); 52 setContentView(view); 53 54 setUpDropTarget("request_none", new OnDragUriReadListener(false)); 55 setUpDropTarget("request_read", new OnDragUriReadListener()); 56 setUpDropTarget("request_write", new OnDragUriWriteListener()); 57 setUpDropTarget("request_read_nested", new OnDragUriReadPrefixListener()); 58 setUpDropTarget("request_take_persistable", new OnDragUriTakePersistableListener()); 59 } 60 setUpDropTarget(String mode, OnDragUriListener listener)61 private void setUpDropTarget(String mode, OnDragUriListener listener) { 62 if (!mode.equals(getIntent().getStringExtra("mode"))) { 63 return; 64 } 65 mTextView = (TextView)findViewById(R.id.drag_target); 66 mTextView.setText(mode); 67 mTextView.setOnDragListener(listener); 68 } 69 checkExtraValue(DragEvent event)70 private String checkExtraValue(DragEvent event) { 71 PersistableBundle extras = event.getClipDescription().getExtras(); 72 if (extras == null) { 73 return "Null"; 74 } 75 76 final String value = extras.getString("extraKey"); 77 if ("extraValue".equals(value)) { 78 return RESULT_OK; 79 } 80 return value; 81 } 82 logResult(String key, String value)83 private void logResult(String key, String value) { 84 Log.i(LOG_TAG, key + "=" + value); 85 mTextView.setText(mTextView.getText() + "\n" + key + "=" + value); 86 } 87 88 private abstract class OnDragUriListener implements View.OnDragListener { 89 private final boolean requestPermissions; 90 OnDragUriListener(boolean requestPermissions)91 public OnDragUriListener(boolean requestPermissions) { 92 this.requestPermissions = requestPermissions; 93 } 94 95 @Override onDrag(View v, DragEvent event)96 public boolean onDrag(View v, DragEvent event) { 97 switch (event.getAction()) { 98 case DragEvent.ACTION_DRAG_STARTED: 99 logResult(RESULT_KEY_DRAG_STARTED, RESULT_OK); 100 logResult(RESULT_KEY_EXTRAS, checkExtraValue(event)); 101 return true; 102 103 case DragEvent.ACTION_DRAG_ENTERED: 104 return true; 105 106 case DragEvent.ACTION_DRAG_LOCATION: 107 return true; 108 109 case DragEvent.ACTION_DRAG_EXITED: 110 return true; 111 112 case DragEvent.ACTION_DROP: 113 String result; 114 try { 115 result = processDrop(event, requestPermissions); 116 } catch (Exception e) { 117 result = RESULT_EXCEPTION; 118 logResult(RESULT_KEY_DETAILS, e.getMessage()); 119 } 120 logResult(RESULT_KEY_DROP_RESULT, result); 121 return true; 122 123 case DragEvent.ACTION_DRAG_ENDED: 124 return true; 125 126 default: 127 return false; 128 } 129 } 130 processDrop(DragEvent event, boolean requestPermissions)131 private String processDrop(DragEvent event, boolean requestPermissions) { 132 final ClipData clipData = event.getClipData(); 133 if (clipData == null) { 134 return "Null ClipData"; 135 } 136 if (clipData.getItemCount() == 0) { 137 return "Empty ClipData"; 138 } 139 ClipData.Item item = clipData.getItemAt(0); 140 if (item == null) { 141 return "Null ClipData.Item"; 142 } 143 Uri uri = item.getUri(); 144 if (uri == null) { 145 return "Null Uri"; 146 } 147 148 DragAndDropPermissions permissions = null; 149 if (requestPermissions) { 150 permissions = requestDragAndDropPermissions(event); 151 if (permissions == null) { 152 return "Null DragAndDropPermissions"; 153 } 154 } 155 156 try { 157 return processUri(uri); 158 } finally { 159 if (permissions != null) { 160 permissions.release(); 161 } 162 } 163 } 164 processUri(Uri uri)165 abstract protected String processUri(Uri uri); 166 } 167 168 private class OnDragUriReadListener extends OnDragUriListener { OnDragUriReadListener(boolean requestPermissions)169 OnDragUriReadListener(boolean requestPermissions) { 170 super(requestPermissions); 171 } 172 OnDragUriReadListener()173 OnDragUriReadListener() { 174 super(true); 175 } 176 processUri(Uri uri)177 protected String processUri(Uri uri) { 178 return checkQueryResult(uri, MAGIC_VALUE); 179 } 180 checkQueryResult(Uri uri, String expectedValue)181 protected String checkQueryResult(Uri uri, String expectedValue) { 182 Cursor cursor = null; 183 try { 184 cursor = getContentResolver().query(uri, null, null, null, null); 185 if (cursor == null) { 186 return "Null Cursor"; 187 } 188 cursor.moveToPosition(0); 189 String value = cursor.getString(0); 190 if (!expectedValue.equals(value)) { 191 return "Wrong value: " + value; 192 } 193 return RESULT_OK; 194 } finally { 195 if (cursor != null) { 196 cursor.close(); 197 } 198 } 199 } 200 } 201 202 private class OnDragUriWriteListener extends OnDragUriListener { OnDragUriWriteListener()203 OnDragUriWriteListener() { 204 super(true); 205 } 206 processUri(Uri uri)207 protected String processUri(Uri uri) { 208 ContentValues values = new ContentValues(); 209 values.put("key", 100); 210 getContentResolver().update(uri, values, null, null); 211 return RESULT_OK; 212 } 213 } 214 215 private class OnDragUriReadPrefixListener extends OnDragUriReadListener { 216 @Override processUri(Uri uri)217 protected String processUri(Uri uri) { 218 final String result1 = queryPrefixed(uri, "1"); 219 if (!result1.equals(RESULT_OK)) { 220 return result1; 221 } 222 final String result2 = queryPrefixed(uri, "2"); 223 if (!result2.equals(RESULT_OK)) { 224 return result2; 225 } 226 return queryPrefixed(uri, "3"); 227 } 228 queryPrefixed(Uri uri, String selector)229 private String queryPrefixed(Uri uri, String selector) { 230 final Uri prefixedUri = Uri.parse(uri.toString() + "/" + selector); 231 return checkQueryResult(prefixedUri, selector); 232 } 233 } 234 235 private class OnDragUriTakePersistableListener extends OnDragUriListener { OnDragUriTakePersistableListener()236 OnDragUriTakePersistableListener() { 237 super(true); 238 } 239 240 @Override processUri(Uri uri)241 protected String processUri(Uri uri) { 242 getContentResolver().takePersistableUriPermission( 243 uri, View.DRAG_FLAG_GLOBAL_URI_READ); 244 getContentResolver().releasePersistableUriPermission( 245 uri, View.DRAG_FLAG_GLOBAL_URI_READ); 246 return RESULT_OK; 247 } 248 } 249 } 250