This source file includes following definitions.
- onCreate
- queryRoots
- getNotificationUri
- notifyChange
- notifyChangeByName
- getMimeType
- queryDocument1
- queryDocument
- queryChildDocuments
- openDocument
- createDocument
- deleteDocument1
- deleteDocument
- removeDocument
- getDocumentType
- renameDocument
- isChildDocument
- moveDocument
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.gnu.emacs;
21
22 import android.content.Context;
23
24 import android.database.Cursor;
25 import android.database.MatrixCursor;
26
27 import android.os.Build;
28 import android.os.CancellationSignal;
29 import android.os.ParcelFileDescriptor;
30
31 import android.provider.DocumentsContract.Document;
32 import android.provider.DocumentsContract.Root;
33 import static android.provider.DocumentsContract.buildChildDocumentsUri;
34 import android.provider.DocumentsProvider;
35
36 import android.webkit.MimeTypeMap;
37
38 import android.net.Uri;
39
40 import java.io.File;
41 import java.io.FileInputStream;
42 import java.io.FileNotFoundException;
43 import java.io.FileOutputStream;
44 import java.io.IOException;
45
46
47
48
49
50
51
52
53 public final class EmacsDocumentsProvider extends DocumentsProvider
54 {
55
56
57 private File baseDir;
58
59
60 private static final String[] DEFAULT_ROOT_PROJECTION;
61
62
63 private static final String[] DEFAULT_DOCUMENT_PROJECTION;
64
65 static
66 {
67 DEFAULT_ROOT_PROJECTION = new String[] {
68 Root.COLUMN_ROOT_ID,
69 Root.COLUMN_MIME_TYPES,
70 Root.COLUMN_FLAGS,
71 Root.COLUMN_ICON,
72 Root.COLUMN_TITLE,
73 Root.COLUMN_SUMMARY,
74 Root.COLUMN_DOCUMENT_ID,
75 Root.COLUMN_AVAILABLE_BYTES,
76 };
77
78 DEFAULT_DOCUMENT_PROJECTION = new String[] {
79 Document.COLUMN_DOCUMENT_ID,
80 Document.COLUMN_MIME_TYPE,
81 Document.COLUMN_DISPLAY_NAME,
82 Document.COLUMN_LAST_MODIFIED,
83 Document.COLUMN_FLAGS,
84 Document.COLUMN_SIZE,
85 };
86 }
87
88 @Override
89 public boolean
90 onCreate ()
91 {
92
93 baseDir = getContext ().getFilesDir ();
94 return true;
95 }
96
97 @Override
98 public Cursor
99 queryRoots (String[] projection)
100 {
101 MatrixCursor result;
102 MatrixCursor.RowBuilder row;
103
104
105
106
107 if (projection == null)
108 projection = DEFAULT_ROOT_PROJECTION;
109
110 result = new MatrixCursor (projection);
111 row = result.newRow ();
112
113
114
115 row.add (Root.COLUMN_ROOT_ID, baseDir.getAbsolutePath ());
116 row.add (Root.COLUMN_SUMMARY, "Emacs home directory");
117
118
119
120 row.add (Root.COLUMN_FLAGS, (Root.FLAG_SUPPORTS_CREATE
121 | Root.FLAG_SUPPORTS_IS_CHILD));
122 row.add (Root.COLUMN_ICON, R.drawable.emacs);
123 row.add (Root.FLAG_LOCAL_ONLY);
124 row.add (Root.COLUMN_TITLE, "Emacs");
125 row.add (Root.COLUMN_DOCUMENT_ID, baseDir.getAbsolutePath ());
126
127 return result;
128 }
129
130 private Uri
131 getNotificationUri (File file)
132 {
133 Uri updatedUri;
134
135 updatedUri
136 = buildChildDocumentsUri ("org.gnu.emacs",
137 file.getAbsolutePath ());
138
139 return updatedUri;
140 }
141
142
143
144
145 private void
146 notifyChange (File file)
147 {
148 Uri updatedUri;
149 Context context;
150
151 context = getContext ();
152 updatedUri
153 = buildChildDocumentsUri ("org.gnu.emacs",
154 file.getAbsolutePath ());
155 context.getContentResolver ().notifyChange (updatedUri, null);
156 }
157
158
159
160
161
162 private void
163 notifyChangeByName (String file)
164 {
165 Uri updatedUri;
166 Context context;
167
168 context = getContext ();
169 updatedUri
170 = buildChildDocumentsUri ("org.gnu.emacs", file);
171 context.getContentResolver ().notifyChange (updatedUri, null);
172 }
173
174
175
176 private String
177 getMimeType (File file)
178 {
179 String name, extension, mime;
180 int extensionSeparator;
181 MimeTypeMap singleton;
182
183 if (file.isDirectory ())
184 return Document.MIME_TYPE_DIR;
185
186
187 name = file.getName ();
188 extensionSeparator = name.lastIndexOf ('.');
189
190 if (extensionSeparator > 0)
191 {
192 singleton = MimeTypeMap.getSingleton ();
193 extension = name.substring (extensionSeparator + 1);
194 mime = singleton.getMimeTypeFromExtension (extension);
195
196 if (mime != null)
197 return mime;
198 }
199
200 return "application/octet-stream";
201 }
202
203
204
205
206 private void
207 queryDocument1 (MatrixCursor result, File file)
208 {
209 MatrixCursor.RowBuilder row;
210 String fileName, displayName, mimeType;
211 int flags;
212
213 row = result.newRow ();
214 flags = 0;
215
216
217
218 fileName = file.getAbsolutePath ();
219
220
221
222 if (file.isDirectory ())
223 {
224 if (file.canWrite ())
225 {
226 flags |= Document.FLAG_DIR_SUPPORTS_CREATE;
227 flags |= Document.FLAG_SUPPORTS_DELETE;
228
229 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
230 flags |= Document.FLAG_SUPPORTS_RENAME;
231
232 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
233 flags |= Document.FLAG_SUPPORTS_MOVE;
234 }
235 }
236 else if (file.canWrite ())
237 {
238
239 flags |= Document.FLAG_SUPPORTS_WRITE;
240 flags |= Document.FLAG_SUPPORTS_DELETE;
241
242 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
243 flags |= Document.FLAG_SUPPORTS_RENAME;
244
245 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
246 {
247 flags |= Document.FLAG_SUPPORTS_REMOVE;
248 flags |= Document.FLAG_SUPPORTS_MOVE;
249 }
250 }
251
252 displayName = file.getName ();
253 mimeType = getMimeType (file);
254
255 row.add (Document.COLUMN_DOCUMENT_ID, fileName);
256 row.add (Document.COLUMN_DISPLAY_NAME, displayName);
257 row.add (Document.COLUMN_SIZE, file.length ());
258 row.add (Document.COLUMN_MIME_TYPE, mimeType);
259 row.add (Document.COLUMN_LAST_MODIFIED, file.lastModified ());
260 row.add (Document.COLUMN_FLAGS, flags);
261 }
262
263 @Override
264 public Cursor
265 queryDocument (String documentId, String[] projection)
266 throws FileNotFoundException
267 {
268 MatrixCursor result;
269 File file;
270 Context context;
271
272 file = new File (documentId);
273 context = getContext ();
274
275 if (projection == null)
276 projection = DEFAULT_DOCUMENT_PROJECTION;
277
278 result = new MatrixCursor (projection);
279 queryDocument1 (result, file);
280
281
282 result.setNotificationUri (context.getContentResolver (),
283 getNotificationUri (file));
284
285 return result;
286 }
287
288 @Override
289 public Cursor
290 queryChildDocuments (String parentDocumentId, String[] projection,
291 String sortOrder) throws FileNotFoundException
292 {
293 MatrixCursor result;
294 File directory;
295 File[] files;
296 Context context;
297
298 if (projection == null)
299 projection = DEFAULT_DOCUMENT_PROJECTION;
300
301 result = new MatrixCursor (projection);
302
303
304
305 directory = new File (parentDocumentId);
306
307
308 files = directory.listFiles ();
309
310 if (files != null)
311 {
312
313 for (File child : files)
314 queryDocument1 (result, child);
315 }
316
317 context = getContext ();
318
319
320 result.setNotificationUri (context.getContentResolver (),
321 getNotificationUri (directory));
322
323 return result;
324 }
325
326 @Override
327 public ParcelFileDescriptor
328 openDocument (String documentId, String mode,
329 CancellationSignal signal) throws FileNotFoundException
330 {
331 return ParcelFileDescriptor.open (new File (documentId),
332 ParcelFileDescriptor.parseMode (mode));
333 }
334
335 @Override
336 public String
337 createDocument (String documentId, String mimeType,
338 String displayName) throws FileNotFoundException
339 {
340 File file, parentFile;
341 boolean rc;
342
343 file = new File (documentId, displayName);
344
345 try
346 {
347 rc = false;
348
349 if (Document.MIME_TYPE_DIR.equals (mimeType))
350 {
351 file.mkdirs ();
352
353 if (file.isDirectory ())
354 rc = true;
355 }
356 else
357 {
358 file.createNewFile ();
359
360 if (file.isFile ()
361 && file.setWritable (true)
362 && file.setReadable (true))
363 rc = true;
364 }
365
366 if (!rc)
367 throw new FileNotFoundException ("rc != 1");
368 }
369 catch (IOException e)
370 {
371 throw new FileNotFoundException (e.toString ());
372 }
373
374 parentFile = file.getParentFile ();
375
376 if (parentFile != null)
377 notifyChange (parentFile);
378
379 return file.getAbsolutePath ();
380 }
381
382 private void
383 deleteDocument1 (File child)
384 {
385 File[] children;
386
387
388
389
390
391
392
393 if (child.delete ())
394 return;
395
396 children = child.listFiles ();
397
398 if (children != null)
399 {
400 for (File file : children)
401 deleteDocument1 (file);
402 }
403
404 child.delete ();
405 }
406
407 @Override
408 public void
409 deleteDocument (String documentId)
410 throws FileNotFoundException
411 {
412 File file, parent;
413 File[] children;
414
415
416
417
418 file = new File (documentId);
419 parent = file.getParentFile ();
420
421 if (parent == null)
422 throw new RuntimeException ("trying to delete file without"
423 + " parent!");
424
425 if (file.delete ())
426 {
427
428 notifyChange (parent);
429 return;
430 }
431
432 children = file.listFiles ();
433
434 if (children != null)
435 {
436 for (File child : children)
437 deleteDocument1 (child);
438 }
439
440 if (file.delete ())
441
442 notifyChange (parent);
443 }
444
445 @Override
446 public void
447 removeDocument (String documentId, String parentDocumentId)
448 throws FileNotFoundException
449 {
450 deleteDocument (documentId);
451 }
452
453 @Override
454 public String
455 getDocumentType (String documentId)
456 {
457 return getMimeType (new File (documentId));
458 }
459
460 @Override
461 public String
462 renameDocument (String documentId, String displayName)
463 throws FileNotFoundException
464 {
465 File file, newName;
466 File parent;
467
468 file = new File (documentId);
469 parent = file.getParentFile ();
470 newName = new File (parent, displayName);
471
472 if (parent == null)
473 throw new FileNotFoundException ("parent is null");
474
475 file = new File (documentId);
476
477 if (!file.renameTo (newName))
478 return null;
479
480 notifyChange (parent);
481 return newName.getAbsolutePath ();
482 }
483
484 @Override
485 public boolean
486 isChildDocument (String parentDocumentId, String documentId)
487 {
488 return documentId.startsWith (parentDocumentId);
489 }
490
491 @Override
492 public String
493 moveDocument (String sourceDocumentId,
494 String sourceParentDocumentId,
495 String targetParentDocumentId)
496 throws FileNotFoundException
497 {
498 File file, newName;
499 FileInputStream inputStream;
500 FileOutputStream outputStream;
501 byte buffer[];
502 int length;
503
504 file = new File (sourceDocumentId);
505
506
507 newName = new File (targetParentDocumentId,
508 file.getName ());
509
510
511
512
513 if (file.renameTo (newName))
514 {
515 notifyChangeByName (file.getParent ());
516 notifyChangeByName (targetParentDocumentId);
517 return newName.getAbsolutePath ();
518 }
519
520
521
522
523 inputStream = null;
524 outputStream = null;
525
526 try
527 {
528 if (!newName.createNewFile ()
529 || !newName.setWritable (true)
530 || !newName.setReadable (true))
531 throw new FileNotFoundException ("failed to create new file");
532
533
534
535 inputStream = new FileInputStream (file);
536 outputStream = new FileOutputStream (newName);
537
538
539
540 buffer = new byte[4096];
541
542 while ((length = inputStream.read (buffer)) > 0)
543 outputStream.write (buffer, 0, length);
544 }
545 catch (IOException e)
546 {
547 throw new FileNotFoundException ("IOException: " + e);
548 }
549 finally
550 {
551 try
552 {
553 if (inputStream != null)
554 inputStream.close ();
555 }
556 catch (IOException e)
557 {
558
559 }
560
561 try
562 {
563 if (outputStream != null)
564 outputStream.close ();
565 }
566 catch (IOException e)
567 {
568
569 }
570 }
571
572 file.delete ();
573 notifyChangeByName (file.getParent ());
574 notifyChangeByName (targetParentDocumentId);
575
576 return newName.getAbsolutePath ();
577 }
578 }