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