blob: 438f6cdc30b5649ee7d9544c1604568cba09b73b [file] [log] [blame]
Tomasz Mikolajewski0fc8beb2015-04-08 09:21:08 +09001/*
2 * Copyright (C) 2015 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
17package com.android.documentsui;
18
19import android.content.Context;
Ben Kwa448dbbb2015-04-16 18:14:35 -070020import android.content.SharedPreferences;
Tomasz Mikolajewski738154e2015-04-10 17:32:44 +090021import android.content.pm.ProviderInfo;
Tomasz Mikolajewski0fc8beb2015-04-08 09:21:08 +090022import android.content.res.AssetFileDescriptor;
23import android.database.Cursor;
24import android.database.MatrixCursor.RowBuilder;
25import android.database.MatrixCursor;
26import android.graphics.Point;
Ben Kwa448dbbb2015-04-16 18:14:35 -070027import android.os.Bundle;
Tomasz Mikolajewski0fc8beb2015-04-08 09:21:08 +090028import android.os.CancellationSignal;
Tomasz Mikolajewski78699be2015-04-08 19:38:55 +090029import android.os.FileUtils;
Tomasz Mikolajewski0fc8beb2015-04-08 09:21:08 +090030import android.os.ParcelFileDescriptor;
Tomasz Mikolajewski738154e2015-04-10 17:32:44 +090031import android.provider.DocumentsContract;
Tomasz Mikolajewski0fc8beb2015-04-08 09:21:08 +090032import android.provider.DocumentsContract.Document;
33import android.provider.DocumentsContract.Root;
Tomasz Mikolajewski0fc8beb2015-04-08 09:21:08 +090034import android.provider.DocumentsProvider;
Ben Kwa448dbbb2015-04-16 18:14:35 -070035import android.util.Log;
Tomasz Mikolajewski0fc8beb2015-04-08 09:21:08 +090036
Ben Kwa448dbbb2015-04-16 18:14:35 -070037import com.google.android.collect.Maps;
38
39import libcore.io.IoUtils;
40
Tomasz Mikolajewski30b66942015-04-10 17:28:53 +090041import java.io.FileOutputStream;
Tomasz Mikolajewski0fc8beb2015-04-08 09:21:08 +090042import java.io.File;
43import java.io.FileNotFoundException;
44import java.io.IOException;
Ben Kwa448dbbb2015-04-16 18:14:35 -070045import java.io.InputStream;
46import java.io.OutputStream;
47import java.util.Arrays;
48import java.util.Collection;
Tomasz Mikolajewski0fc8beb2015-04-08 09:21:08 +090049import java.util.HashMap;
Ben Kwa448dbbb2015-04-16 18:14:35 -070050import java.util.Map;
Tomasz Mikolajewski0fc8beb2015-04-08 09:21:08 +090051
52public class StubProvider extends DocumentsProvider {
Ben Kwa448dbbb2015-04-16 18:14:35 -070053 private static final String EXTRA_SIZE = "com.android.documentsui.stubprovider.SIZE";
54 private static final String EXTRA_ROOT = "com.android.documentsui.stubprovider.ROOT";
55 private static final String STORAGE_SIZE_KEY = "documentsui.stubprovider.size";
56 private static int DEFAULT_SIZE = 1024 * 1024; // 1 MB.
Tomasz Mikolajewski0fc8beb2015-04-08 09:21:08 +090057 private static final String TAG = "StubProvider";
Ben Kwa448dbbb2015-04-16 18:14:35 -070058 private static final String MY_ROOT_ID = "sd0";
Tomasz Mikolajewski0fc8beb2015-04-08 09:21:08 +090059 private static final String[] DEFAULT_ROOT_PROJECTION = new String[] {
60 Root.COLUMN_ROOT_ID, Root.COLUMN_FLAGS, Root.COLUMN_TITLE, Root.COLUMN_DOCUMENT_ID,
61 Root.COLUMN_AVAILABLE_BYTES
62 };
63 private static final String[] DEFAULT_DOCUMENT_PROJECTION = new String[] {
64 Document.COLUMN_DOCUMENT_ID, Document.COLUMN_MIME_TYPE, Document.COLUMN_DISPLAY_NAME,
65 Document.COLUMN_LAST_MODIFIED, Document.COLUMN_FLAGS, Document.COLUMN_SIZE,
66 };
67
Tomasz Mikolajewskid5d5c912015-04-13 12:17:51 +090068 private HashMap<String, StubDocument> mStorage = new HashMap<String, StubDocument>();
Tomasz Mikolajewski30b66942015-04-10 17:28:53 +090069 private Object mWriteLock = new Object();
Tomasz Mikolajewski738154e2015-04-10 17:32:44 +090070 private String mAuthority;
Ben Kwa448dbbb2015-04-16 18:14:35 -070071 private SharedPreferences mPrefs;
72 private Map<String, RootInfo> mRoots;
Tomasz Mikolajewski738154e2015-04-10 17:32:44 +090073
74 @Override
75 public void attachInfo(Context context, ProviderInfo info) {
76 mAuthority = info.authority;
77 super.attachInfo(context, info);
78 }
Tomasz Mikolajewski0fc8beb2015-04-08 09:21:08 +090079
80 @Override
81 public boolean onCreate() {
Ben Kwa448dbbb2015-04-16 18:14:35 -070082 clearCacheAndBuildRoots();
83 return true;
84 }
85
86 private void clearCacheAndBuildRoots() {
Tomasz Mikolajewski0fc8beb2015-04-08 09:21:08 +090087 final File cacheDir = getContext().getCacheDir();
88 removeRecursively(cacheDir);
Ben Kwa448dbbb2015-04-16 18:14:35 -070089 mStorage.clear();
90
91 mPrefs = getContext().getSharedPreferences(
92 "com.android.documentsui.stubprovider.preferences", Context.MODE_PRIVATE);
93 Collection<String> rootIds = mPrefs.getStringSet("roots", null);
94 if (rootIds == null) {
95 rootIds = Arrays.asList(new String[] {
96 "sd0", "sd1"
97 });
98 }
99 // Create new roots.
100 mRoots = Maps.newHashMap();
101 for (String rootId : rootIds) {
102 final RootInfo rootInfo = new RootInfo(rootId, getSize(rootId));
103 mRoots.put(rootId, rootInfo);
104 }
105 }
106
107 /**
108 * @return Storage size, in bytes.
109 */
110 private long getSize(String rootId) {
111 final String key = STORAGE_SIZE_KEY + "." + rootId;
112 return mPrefs.getLong(key, DEFAULT_SIZE);
Tomasz Mikolajewski0fc8beb2015-04-08 09:21:08 +0900113 }
114
115 @Override
116 public Cursor queryRoots(String[] projection) throws FileNotFoundException {
Ben Kwa448dbbb2015-04-16 18:14:35 -0700117 final MatrixCursor result = new MatrixCursor(projection != null ? projection
118 : DEFAULT_ROOT_PROJECTION);
119 for (Map.Entry<String, RootInfo> entry : mRoots.entrySet()) {
120 final String id = entry.getKey();
121 final RootInfo info = entry.getValue();
122 final RowBuilder row = result.newRow();
123 row.add(Root.COLUMN_ROOT_ID, id);
124 row.add(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_CREATE | Root.FLAG_SUPPORTS_IS_CHILD);
125 row.add(Root.COLUMN_TITLE, id);
126 row.add(Root.COLUMN_DOCUMENT_ID, info.rootDocument.documentId);
127 row.add(Root.COLUMN_AVAILABLE_BYTES, info.getRemainingCapacity());
128 }
Tomasz Mikolajewski0fc8beb2015-04-08 09:21:08 +0900129 return result;
130 }
131
132 @Override
Ben Kwa448dbbb2015-04-16 18:14:35 -0700133 public Cursor queryDocument(String documentId, String[] projection)
134 throws FileNotFoundException {
135 final MatrixCursor result = new MatrixCursor(projection != null ? projection
136 : DEFAULT_DOCUMENT_PROJECTION);
Tomasz Mikolajewskid5d5c912015-04-13 12:17:51 +0900137 final StubDocument file = mStorage.get(documentId);
Tomasz Mikolajewski0fc8beb2015-04-08 09:21:08 +0900138 if (file == null) {
139 throw new FileNotFoundException();
140 }
Tomasz Mikolajewski97397932015-04-13 13:27:14 +0900141 includeDocument(result, file);
Tomasz Mikolajewski0fc8beb2015-04-08 09:21:08 +0900142 return result;
143 }
144
145 @Override
Tomasz Mikolajewski78699be2015-04-08 19:38:55 +0900146 public boolean isChildDocument(String parentDocId, String docId) {
Tomasz Mikolajewskid5d5c912015-04-13 12:17:51 +0900147 final StubDocument parentDocument = mStorage.get(parentDocId);
148 final StubDocument childDocument = mStorage.get(docId);
149 return FileUtils.contains(parentDocument.file, childDocument.file);
Tomasz Mikolajewski78699be2015-04-08 19:38:55 +0900150 }
151
152 @Override
Tomasz Mikolajewski0fc8beb2015-04-08 09:21:08 +0900153 public String createDocument(String parentDocumentId, String mimeType, String displayName)
154 throws FileNotFoundException {
Tomasz Mikolajewskid5d5c912015-04-13 12:17:51 +0900155 final StubDocument parentDocument = mStorage.get(parentDocumentId);
156 if (parentDocument == null || !parentDocument.file.isDirectory()) {
Tomasz Mikolajewski0fc8beb2015-04-08 09:21:08 +0900157 throw new FileNotFoundException();
158 }
Tomasz Mikolajewskid5d5c912015-04-13 12:17:51 +0900159 final File file = new File(parentDocument.file, displayName);
Tomasz Mikolajewski0fc8beb2015-04-08 09:21:08 +0900160 if (mimeType.equals(Document.MIME_TYPE_DIR)) {
161 if (!file.mkdirs()) {
162 throw new FileNotFoundException();
163 }
164 } else {
165 try {
166 if (!file.createNewFile()) {
167 throw new FileNotFoundException();
168 }
Ben Kwa448dbbb2015-04-16 18:14:35 -0700169 } catch (IOException e) {
Tomasz Mikolajewski0fc8beb2015-04-08 09:21:08 +0900170 throw new FileNotFoundException();
171 }
172 }
173
Tomasz Mikolajewski97397932015-04-13 13:27:14 +0900174 final StubDocument document = new StubDocument(file, mimeType, parentDocument);
Tomasz Mikolajewski97397932015-04-13 13:27:14 +0900175 notifyParentChanged(document.parentId);
Tomasz Mikolajewskid5d5c912015-04-13 12:17:51 +0900176 return document.documentId;
Tomasz Mikolajewski0fc8beb2015-04-08 09:21:08 +0900177 }
178
179 @Override
Tomasz Mikolajewski738154e2015-04-10 17:32:44 +0900180 public void deleteDocument(String documentId)
181 throws FileNotFoundException {
Tomasz Mikolajewskid5d5c912015-04-13 12:17:51 +0900182 final StubDocument document = mStorage.get(documentId);
183 final long fileSize = document.file.length();
184 if (document == null || !document.file.delete())
Tomasz Mikolajewski738154e2015-04-10 17:32:44 +0900185 throw new FileNotFoundException();
186 synchronized (mWriteLock) {
Ben Kwa448dbbb2015-04-16 18:14:35 -0700187 document.rootInfo.size -= fileSize;
Tomasz Mikolajewski738154e2015-04-10 17:32:44 +0900188 }
Tomasz Mikolajewski97397932015-04-13 13:27:14 +0900189 notifyParentChanged(document.parentId);
Tomasz Mikolajewski738154e2015-04-10 17:32:44 +0900190 }
191
192 @Override
Tomasz Mikolajewski0fc8beb2015-04-08 09:21:08 +0900193 public Cursor queryChildDocuments(String parentDocumentId, String[] projection, String sortOrder)
194 throws FileNotFoundException {
Tomasz Mikolajewskid5d5c912015-04-13 12:17:51 +0900195 final StubDocument parentDocument = mStorage.get(parentDocumentId);
196 if (parentDocument == null || parentDocument.file.isFile()) {
Tomasz Mikolajewski0fc8beb2015-04-08 09:21:08 +0900197 throw new FileNotFoundException();
198 }
Ben Kwa448dbbb2015-04-16 18:14:35 -0700199 final MatrixCursor result = new MatrixCursor(projection != null ? projection
200 : DEFAULT_DOCUMENT_PROJECTION);
Tomasz Mikolajewski97397932015-04-13 13:27:14 +0900201 result.setNotificationUri(getContext().getContentResolver(),
202 DocumentsContract.buildChildDocumentsUri(mAuthority, parentDocumentId));
Tomasz Mikolajewskid5d5c912015-04-13 12:17:51 +0900203 StubDocument document;
204 for (File file : parentDocument.file.listFiles()) {
Ben Kwa448dbbb2015-04-16 18:14:35 -0700205 document = mStorage.get(getDocumentIdForFile(file));
Tomasz Mikolajewski97397932015-04-13 13:27:14 +0900206 if (document != null) {
207 includeDocument(result, document);
208 }
Tomasz Mikolajewski0fc8beb2015-04-08 09:21:08 +0900209 }
210 return result;
211 }
212
213 @Override
214 public Cursor queryRecentDocuments(String rootId, String[] projection)
215 throws FileNotFoundException {
Ben Kwa448dbbb2015-04-16 18:14:35 -0700216 final MatrixCursor result = new MatrixCursor(projection != null ? projection
217 : DEFAULT_DOCUMENT_PROJECTION);
218 return result;
Tomasz Mikolajewski0fc8beb2015-04-08 09:21:08 +0900219 }
220
221 @Override
222 public ParcelFileDescriptor openDocument(String docId, String mode, CancellationSignal signal)
223 throws FileNotFoundException {
Tomasz Mikolajewskid5d5c912015-04-13 12:17:51 +0900224 final StubDocument document = mStorage.get(docId);
225 if (document == null || !document.file.isFile())
226 throw new FileNotFoundException();
227
228 if ("r".equals(mode)) {
229 return ParcelFileDescriptor.open(document.file, ParcelFileDescriptor.MODE_READ_ONLY);
Tomasz Mikolajewski30b66942015-04-10 17:28:53 +0900230 }
Tomasz Mikolajewskid5d5c912015-04-13 12:17:51 +0900231 if ("w".equals(mode)) {
232 return startWrite(document);
233 }
234
Tomasz Mikolajewski30b66942015-04-10 17:28:53 +0900235 throw new FileNotFoundException();
Tomasz Mikolajewski0fc8beb2015-04-08 09:21:08 +0900236 }
237
238 @Override
239 public AssetFileDescriptor openDocumentThumbnail(
240 String docId, Point sizeHint, CancellationSignal signal) throws FileNotFoundException {
241 throw new FileNotFoundException();
242 }
243
Tomasz Mikolajewskid5d5c912015-04-13 12:17:51 +0900244 private ParcelFileDescriptor startWrite(final StubDocument document)
Tomasz Mikolajewski30b66942015-04-10 17:28:53 +0900245 throws FileNotFoundException {
Tomasz Mikolajewski30b66942015-04-10 17:28:53 +0900246 ParcelFileDescriptor[] pipe;
247 try {
248 pipe = ParcelFileDescriptor.createReliablePipe();
Ben Kwa448dbbb2015-04-16 18:14:35 -0700249 } catch (IOException exception) {
Tomasz Mikolajewski30b66942015-04-10 17:28:53 +0900250 throw new FileNotFoundException();
251 }
252 final ParcelFileDescriptor readPipe = pipe[0];
253 final ParcelFileDescriptor writePipe = pipe[1];
254
255 new Thread() {
256 @Override
257 public void run() {
Ben Kwa448dbbb2015-04-16 18:14:35 -0700258 InputStream inputStream = null;
259 OutputStream outputStream = null;
Tomasz Mikolajewski30b66942015-04-10 17:28:53 +0900260 try {
Ben Kwa448dbbb2015-04-16 18:14:35 -0700261 inputStream = new ParcelFileDescriptor.AutoCloseInputStream(readPipe);
262 outputStream = new FileOutputStream(document.file);
Tomasz Mikolajewski30b66942015-04-10 17:28:53 +0900263 byte[] buffer = new byte[32 * 1024];
264 int bytesToRead;
265 int bytesRead = 0;
266 while (bytesRead != -1) {
267 synchronized (mWriteLock) {
Ben Kwa448dbbb2015-04-16 18:14:35 -0700268 // This cast is safe because the max possible value is buffer.length.
269 bytesToRead = (int) Math.min(document.rootInfo.getRemainingCapacity(),
270 buffer.length);
Tomasz Mikolajewski30b66942015-04-10 17:28:53 +0900271 if (bytesToRead == 0) {
272 closePipeWithErrorSilently(readPipe, "Not enough space.");
273 break;
274 }
275 bytesRead = inputStream.read(buffer, 0, bytesToRead);
276 if (bytesRead == -1) {
277 break;
278 }
279 outputStream.write(buffer, 0, bytesRead);
Ben Kwa448dbbb2015-04-16 18:14:35 -0700280 document.rootInfo.size += bytesRead;
Tomasz Mikolajewski30b66942015-04-10 17:28:53 +0900281 }
282 }
Ben Kwa448dbbb2015-04-16 18:14:35 -0700283 } catch (IOException e) {
Tomasz Mikolajewski30b66942015-04-10 17:28:53 +0900284 closePipeWithErrorSilently(readPipe, e.getMessage());
Ben Kwa448dbbb2015-04-16 18:14:35 -0700285 } finally {
286 IoUtils.closeQuietly(inputStream);
287 IoUtils.closeQuietly(outputStream);
Tomasz Mikolajewski97397932015-04-13 13:27:14 +0900288 notifyParentChanged(document.parentId);
Tomasz Mikolajewski30b66942015-04-10 17:28:53 +0900289 }
290 }
291 }.start();
292
293 return writePipe;
294 }
295
296 private void closePipeWithErrorSilently(ParcelFileDescriptor pipe, String error) {
297 try {
298 pipe.closeWithError(error);
Ben Kwa448dbbb2015-04-16 18:14:35 -0700299 } catch (IOException ignore) {
Tomasz Mikolajewski30b66942015-04-10 17:28:53 +0900300 }
301 }
302
Ben Kwa448dbbb2015-04-16 18:14:35 -0700303 @Override
304 public Bundle call(String method, String arg, Bundle extras) {
305 Log.d(TAG, "call: " + method + arg);
306 switch (method) {
307 case "clear":
308 clearCacheAndBuildRoots();
309 return null;
310 case "configure":
311 configure(arg, extras);
312 return null;
313 default:
314 return super.call(method, arg, extras);
Tomasz Mikolajewski30b66942015-04-10 17:28:53 +0900315 }
Ben Kwa448dbbb2015-04-16 18:14:35 -0700316 }
317
318 private void configure(String arg, Bundle extras) {
319 Log.d(TAG, "Configure " + arg);
320 String rootName = extras.getString(EXTRA_ROOT, MY_ROOT_ID);
321 long rootSize = extras.getLong(EXTRA_SIZE, 1) * 1024 * 1024;
322 setSize(rootName, rootSize);
Tomasz Mikolajewski30b66942015-04-10 17:28:53 +0900323 }
324
Tomasz Mikolajewski97397932015-04-13 13:27:14 +0900325 private void notifyParentChanged(String parentId) {
326 getContext().getContentResolver().notifyChange(
327 DocumentsContract.buildChildDocumentsUri(mAuthority, parentId), null, false);
328 // Notify also about possible change in remaining space on the root.
Ben Kwa448dbbb2015-04-16 18:14:35 -0700329 getContext().getContentResolver().notifyChange(DocumentsContract.buildRootsUri(mAuthority),
330 null, false);
Tomasz Mikolajewski738154e2015-04-10 17:32:44 +0900331 }
332
Tomasz Mikolajewski97397932015-04-13 13:27:14 +0900333 private void includeDocument(MatrixCursor result, StubDocument document) {
Tomasz Mikolajewski0fc8beb2015-04-08 09:21:08 +0900334 final RowBuilder row = result.newRow();
Tomasz Mikolajewskid5d5c912015-04-13 12:17:51 +0900335 row.add(Document.COLUMN_DOCUMENT_ID, document.documentId);
336 row.add(Document.COLUMN_DISPLAY_NAME, document.file.getName());
337 row.add(Document.COLUMN_SIZE, document.file.length());
338 row.add(Document.COLUMN_MIME_TYPE, document.mimeType);
Tomasz Mikolajewski738154e2015-04-10 17:32:44 +0900339 int flags = Document.FLAG_SUPPORTS_DELETE;
340 // TODO: Add support for renaming.
Tomasz Mikolajewskid5d5c912015-04-13 12:17:51 +0900341 if (document.file.isDirectory()) {
Tomasz Mikolajewski0fc8beb2015-04-08 09:21:08 +0900342 flags |= Document.FLAG_DIR_SUPPORTS_CREATE;
343 } else {
344 flags |= Document.FLAG_SUPPORTS_WRITE;
345 }
346 row.add(Document.COLUMN_FLAGS, flags);
Tomasz Mikolajewskid5d5c912015-04-13 12:17:51 +0900347 row.add(Document.COLUMN_LAST_MODIFIED, document.file.lastModified());
Tomasz Mikolajewski78699be2015-04-08 19:38:55 +0900348 }
349
Tomasz Mikolajewski0fc8beb2015-04-08 09:21:08 +0900350 private void removeRecursively(File file) {
351 for (File childFile : file.listFiles()) {
352 if (childFile.isDirectory()) {
353 removeRecursively(childFile);
354 }
355 childFile.delete();
356 }
357 }
Tomasz Mikolajewskid5d5c912015-04-13 12:17:51 +0900358
Ben Kwa448dbbb2015-04-16 18:14:35 -0700359 public void setSize(String rootId, long rootSize) {
360 RootInfo root = mRoots.get(rootId);
361 if (root != null) {
362 final String key = STORAGE_SIZE_KEY + "." + rootId;
363 Log.d(TAG, "Set size of " + key + " : " + rootSize);
Tomasz Mikolajewskid5d5c912015-04-13 12:17:51 +0900364
Ben Kwa448dbbb2015-04-16 18:14:35 -0700365 // Persist the size.
366 SharedPreferences.Editor editor = mPrefs.edit();
367 editor.putLong(key, rootSize);
368 editor.apply();
369 // Apply the size in the current instance of this provider.
370 root.capacity = rootSize;
371 getContext().getContentResolver().notifyChange(
372 DocumentsContract.buildRootsUri(mAuthority),
373 null, false);
374 } else {
375 Log.e(TAG, "Attempt to configure non-existent root: " + rootId);
376 }
Tomasz Mikolajewskid5d5c912015-04-13 12:17:51 +0900377 }
378
Ben Kwa448dbbb2015-04-16 18:14:35 -0700379 public File createFile(String rootId, File parent, String mimeType, String name)
380 throws IOException {
381 StubDocument parentDoc = null;
382 if (parent == null) {
383 // Use the root dir as the parent, if one wasn't specified.
384 parentDoc = mRoots.get(rootId).rootDocument;
385 } else {
386 // Verify that the parent exists and is a directory.
387 parentDoc = mStorage.get(getDocumentIdForFile(parent));
388 if (parentDoc == null) {
389 throw new IllegalArgumentException("Parent file not found.");
390 }
391 if (!Document.MIME_TYPE_DIR.equals(parentDoc.mimeType)) {
392 throw new IllegalArgumentException("Parent file must be a directory.");
393 }
394 }
395 File file = new File(parentDoc.file, name);
396 if (Document.MIME_TYPE_DIR.equals(mimeType)) {
397 file.mkdir();
398 } else {
399 file.createNewFile();
400 }
401 new StubDocument(file, mimeType, parentDoc);
402 return file;
403 }
404
405 final class RootInfo {
406 public final String name;
407 public final StubDocument rootDocument;
408 public long capacity;
409 public long size;
410
411 RootInfo(String name, long capacity) {
412 this.name = name;
413 this.capacity = 1024 * 1024;
414 // Make a subdir in the cache dir for each root.
415 File rootDir = new File(getContext().getCacheDir(), name);
416 rootDir.mkdir();
417 this.rootDocument = new StubDocument(rootDir, Document.MIME_TYPE_DIR, this);
418 this.capacity = capacity;
419 this.size = 0;
420 }
421
422 public long getRemainingCapacity() {
423 return capacity - size;
424 }
425 }
426
427 final class StubDocument {
428 public final File file;
429 public final String mimeType;
430 public final String documentId;
431 public final String parentId;
432 public final RootInfo rootInfo;
433
434 StubDocument(File file, String mimeType, StubDocument parent) {
435 this.file = file;
436 this.mimeType = mimeType;
437 this.documentId = getDocumentIdForFile(file);
438 this.parentId = parent.documentId;
439 this.rootInfo = parent.rootInfo;
440 mStorage.put(this.documentId, this);
441 }
442
443 StubDocument(File file, String mimeType, RootInfo rootInfo) {
444 this.file = file;
445 this.mimeType = mimeType;
446 this.documentId = getDocumentIdForFile(file);
447 this.parentId = null;
448 this.rootInfo = rootInfo;
449 mStorage.put(this.documentId, this);
450 }
451 }
452
453 private static String getDocumentIdForFile(File file) {
Tomasz Mikolajewskid5d5c912015-04-13 12:17:51 +0900454 return file.getAbsolutePath();
455 }
456}