Loading src/com/android/documentsui/RootsCache.java +13 −3 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ import android.os.Handler; import android.os.SystemClock; import android.provider.DocumentsContract; import android.provider.DocumentsContract.Root; import android.provider.DocumentsProvider; import android.support.annotation.VisibleForTesting; import android.util.Log; Loading Loading @@ -350,11 +351,20 @@ public class RootsCache { * waiting for all the other roots to come back. */ public RootInfo getRootOneshot(String authority, String rootId) { return getRootOneshot(authority, rootId, false); } /** * Return the requested {@link RootInfo}, but only loading the roots of the requested authority. * It always fetches from {@link DocumentsProvider} if forceRefresh is true, which is used to * get the most up-to-date free space before starting copy operations. */ public RootInfo getRootOneshot(String authority, String rootId, boolean forceRefresh) { synchronized (mLock) { RootInfo root = getRootLocked(authority, rootId); RootInfo root = forceRefresh ? null : getRootLocked(authority, rootId); if (root == null) { mRoots.putAll(authority, loadRootsForAuthority(mContext.getContentResolver(), authority, false)); mRoots.putAll(authority, loadRootsForAuthority( mContext.getContentResolver(), authority, forceRefresh)); root = getRootLocked(authority, rootId); } return root; Loading src/com/android/documentsui/services/CopyJob.java +53 −5 Original line number Diff line number Diff line Loading @@ -51,9 +51,11 @@ import android.text.format.DateUtils; import android.util.Log; import android.webkit.MimeTypeMap; import com.android.documentsui.UrisSupplier; import com.android.documentsui.DocumentsApplication; import com.android.documentsui.Metrics; import com.android.documentsui.R; import com.android.documentsui.RootsCache; import com.android.documentsui.UrisSupplier; import com.android.documentsui.model.DocumentInfo; import com.android.documentsui.model.DocumentStack; import com.android.documentsui.model.RootInfo; Loading Loading @@ -210,7 +212,6 @@ class CopyJob extends Job { @Override boolean setUp() { try { buildDocumentList(); } catch (ResourceException e) { Loading @@ -218,6 +219,7 @@ class CopyJob extends Job { return false; } // Check if user has canceled this task. if (isCanceled()) { return false; } Loading @@ -229,7 +231,15 @@ class CopyJob extends Job { mBatchSize = -1; } return true; // Check if user has canceled this task. We should check it again here as user cancels // tasks in main thread, but this is running in a worker thread. calculateSize() may // take a long time during which user can cancel this task, and we don't want to waste // resources doing useless large chunk of work. if (isCanceled()) { return false; } return checkSpace(); } @Override Loading Loading @@ -286,6 +296,44 @@ class CopyJob extends Job { return !root.isDownloads() || !doc.isDirectory(); } /** * Checks whether the destination folder has enough space to take all source files. * @return true if the root has enough space or doesn't provide free space info; otherwise false */ boolean checkSpace() { return checkSpace(mBatchSize); } /** * Checks whether the destination folder has enough space to take files of batchSize * @param batchSize the total size of files * @return true if the root has enough space or doesn't provide free space info; otherwise false */ final boolean checkSpace(long batchSize) { // Default to be true because if batchSize or available space is invalid, we still let the // copy start anyway. boolean result = true; if (batchSize >= 0) { RootsCache cache = DocumentsApplication.getRootsCache(appContext); // Query root info here instead of using stack.root because the number there may be // stale. RootInfo root = cache.getRootOneshot(stack.root.authority, stack.root.rootId, true); if (root.availableBytes >= 0) { result = (batchSize <= root.availableBytes); } else { Log.w(TAG, root.toString() + " doesn't provide available bytes."); } } if (!result) { failedFileCount += mSrcs.size(); failedFiles.addAll(mSrcs); } return result; } @Override boolean hasWarnings() { return !convertedFiles.isEmpty(); Loading Loading @@ -585,7 +633,7 @@ class CopyJob extends Job { result += calculateFileSizesRecursively(getClient(src), src.derivedUri); } catch (RemoteException e) { throw new ResourceException("Failed to obtain the client for %s.", src.derivedUri); src.derivedUri, e); } } else { result += src.size; Loading @@ -603,7 +651,7 @@ class CopyJob extends Job { * * @throws ResourceException */ private long calculateFileSizesRecursively( long calculateFileSizesRecursively( ContentProviderClient client, Uri uri) throws ResourceException { final String authority = uri.getAuthority(); final Uri queryUri = buildChildDocumentsUri(authority, getDocumentId(uri)); Loading src/com/android/documentsui/services/MoveJob.java +29 −3 Original line number Diff line number Diff line Loading @@ -29,8 +29,8 @@ import android.provider.DocumentsContract; import android.provider.DocumentsContract.Document; import android.util.Log; import com.android.documentsui.UrisSupplier; import com.android.documentsui.R; import com.android.documentsui.UrisSupplier; import com.android.documentsui.model.DocumentInfo; import com.android.documentsui.model.DocumentStack; Loading Loading @@ -96,9 +96,35 @@ final class MoveJob extends CopyJob { return super.setUp(); } /** * {@inheritDoc} * * Only check space for moves across authorities. For now we don't know if the doc in * {@link #mSrcs} is in the same root of destination, and if it's optimized move in the same * root it should succeed regardless of free space, but it's for sure a failure if there is no * enough free space if docs are moved from another authority. */ @Override public void start() { super.start(); boolean checkSpace() { long size = 0; for (DocumentInfo src : mSrcs) { if (!src.authority.equals(stack.root.authority)) { if (src.isDirectory()) { try { size += calculateFileSizesRecursively(getClient(src), src.derivedUri); } catch (RemoteException|ResourceException e) { Log.w(TAG, "Failed to obtain client for %s" + src.derivedUri + ".", e); // Failed to calculate size, but move may still succeed. return true; } } else { size += src.size; } } } return checkSpace(size); } void processDocument(DocumentInfo src, DocumentInfo srcParent, DocumentInfo dest) Loading Loading
src/com/android/documentsui/RootsCache.java +13 −3 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ import android.os.Handler; import android.os.SystemClock; import android.provider.DocumentsContract; import android.provider.DocumentsContract.Root; import android.provider.DocumentsProvider; import android.support.annotation.VisibleForTesting; import android.util.Log; Loading Loading @@ -350,11 +351,20 @@ public class RootsCache { * waiting for all the other roots to come back. */ public RootInfo getRootOneshot(String authority, String rootId) { return getRootOneshot(authority, rootId, false); } /** * Return the requested {@link RootInfo}, but only loading the roots of the requested authority. * It always fetches from {@link DocumentsProvider} if forceRefresh is true, which is used to * get the most up-to-date free space before starting copy operations. */ public RootInfo getRootOneshot(String authority, String rootId, boolean forceRefresh) { synchronized (mLock) { RootInfo root = getRootLocked(authority, rootId); RootInfo root = forceRefresh ? null : getRootLocked(authority, rootId); if (root == null) { mRoots.putAll(authority, loadRootsForAuthority(mContext.getContentResolver(), authority, false)); mRoots.putAll(authority, loadRootsForAuthority( mContext.getContentResolver(), authority, forceRefresh)); root = getRootLocked(authority, rootId); } return root; Loading
src/com/android/documentsui/services/CopyJob.java +53 −5 Original line number Diff line number Diff line Loading @@ -51,9 +51,11 @@ import android.text.format.DateUtils; import android.util.Log; import android.webkit.MimeTypeMap; import com.android.documentsui.UrisSupplier; import com.android.documentsui.DocumentsApplication; import com.android.documentsui.Metrics; import com.android.documentsui.R; import com.android.documentsui.RootsCache; import com.android.documentsui.UrisSupplier; import com.android.documentsui.model.DocumentInfo; import com.android.documentsui.model.DocumentStack; import com.android.documentsui.model.RootInfo; Loading Loading @@ -210,7 +212,6 @@ class CopyJob extends Job { @Override boolean setUp() { try { buildDocumentList(); } catch (ResourceException e) { Loading @@ -218,6 +219,7 @@ class CopyJob extends Job { return false; } // Check if user has canceled this task. if (isCanceled()) { return false; } Loading @@ -229,7 +231,15 @@ class CopyJob extends Job { mBatchSize = -1; } return true; // Check if user has canceled this task. We should check it again here as user cancels // tasks in main thread, but this is running in a worker thread. calculateSize() may // take a long time during which user can cancel this task, and we don't want to waste // resources doing useless large chunk of work. if (isCanceled()) { return false; } return checkSpace(); } @Override Loading Loading @@ -286,6 +296,44 @@ class CopyJob extends Job { return !root.isDownloads() || !doc.isDirectory(); } /** * Checks whether the destination folder has enough space to take all source files. * @return true if the root has enough space or doesn't provide free space info; otherwise false */ boolean checkSpace() { return checkSpace(mBatchSize); } /** * Checks whether the destination folder has enough space to take files of batchSize * @param batchSize the total size of files * @return true if the root has enough space or doesn't provide free space info; otherwise false */ final boolean checkSpace(long batchSize) { // Default to be true because if batchSize or available space is invalid, we still let the // copy start anyway. boolean result = true; if (batchSize >= 0) { RootsCache cache = DocumentsApplication.getRootsCache(appContext); // Query root info here instead of using stack.root because the number there may be // stale. RootInfo root = cache.getRootOneshot(stack.root.authority, stack.root.rootId, true); if (root.availableBytes >= 0) { result = (batchSize <= root.availableBytes); } else { Log.w(TAG, root.toString() + " doesn't provide available bytes."); } } if (!result) { failedFileCount += mSrcs.size(); failedFiles.addAll(mSrcs); } return result; } @Override boolean hasWarnings() { return !convertedFiles.isEmpty(); Loading Loading @@ -585,7 +633,7 @@ class CopyJob extends Job { result += calculateFileSizesRecursively(getClient(src), src.derivedUri); } catch (RemoteException e) { throw new ResourceException("Failed to obtain the client for %s.", src.derivedUri); src.derivedUri, e); } } else { result += src.size; Loading @@ -603,7 +651,7 @@ class CopyJob extends Job { * * @throws ResourceException */ private long calculateFileSizesRecursively( long calculateFileSizesRecursively( ContentProviderClient client, Uri uri) throws ResourceException { final String authority = uri.getAuthority(); final Uri queryUri = buildChildDocumentsUri(authority, getDocumentId(uri)); Loading
src/com/android/documentsui/services/MoveJob.java +29 −3 Original line number Diff line number Diff line Loading @@ -29,8 +29,8 @@ import android.provider.DocumentsContract; import android.provider.DocumentsContract.Document; import android.util.Log; import com.android.documentsui.UrisSupplier; import com.android.documentsui.R; import com.android.documentsui.UrisSupplier; import com.android.documentsui.model.DocumentInfo; import com.android.documentsui.model.DocumentStack; Loading Loading @@ -96,9 +96,35 @@ final class MoveJob extends CopyJob { return super.setUp(); } /** * {@inheritDoc} * * Only check space for moves across authorities. For now we don't know if the doc in * {@link #mSrcs} is in the same root of destination, and if it's optimized move in the same * root it should succeed regardless of free space, but it's for sure a failure if there is no * enough free space if docs are moved from another authority. */ @Override public void start() { super.start(); boolean checkSpace() { long size = 0; for (DocumentInfo src : mSrcs) { if (!src.authority.equals(stack.root.authority)) { if (src.isDirectory()) { try { size += calculateFileSizesRecursively(getClient(src), src.derivedUri); } catch (RemoteException|ResourceException e) { Log.w(TAG, "Failed to obtain client for %s" + src.derivedUri + ".", e); // Failed to calculate size, but move may still succeed. return true; } } else { size += src.size; } } } return checkSpace(size); } void processDocument(DocumentInfo src, DocumentInfo srcParent, DocumentInfo dest) Loading