Loading packages/DocumentsUI/src/com/android/documentsui/CopyService.java +35 −22 Original line number Diff line number Diff line Loading @@ -24,7 +24,11 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.CancellationSignal; import android.os.Environment; import android.os.ParcelFileDescriptor; import android.os.SystemClock; import android.provider.DocumentsContract; import android.text.format.DateUtils; import android.util.Log; Loading Loading @@ -82,18 +86,15 @@ public class CopyService extends IntentService { } ArrayList<DocumentInfo> srcs = intent.getParcelableArrayListExtra(EXTRA_SRC_LIST); // Use the app local files dir as a copy destination for now. This resolves to // /data/data/com.android.documentsui/files. // TODO: Add actual destination picking. File destinationDir = getFilesDir(); Uri destinationUri = intent.getData(); setupCopyJob(srcs, destinationDir); setupCopyJob(srcs, destinationUri); ArrayList<String> failedFilenames = new ArrayList<String>(); for (int i = 0; i < srcs.size() && !mIsCancelled; ++i) { DocumentInfo src = srcs.get(i); try { copyFile(src, destinationDir); copyFile(src, destinationUri); } catch (IOException e) { Log.e(TAG, "Failed to copy " + src.displayName, e); failedFilenames.add(src.displayName); Loading Loading @@ -121,8 +122,9 @@ public class CopyService extends IntentService { * files. * * @param srcs A list of src files to copy. * @param destinationUri The URI of the destination directory. */ private void setupCopyJob(ArrayList<DocumentInfo> srcs, File destinationDir) { private void setupCopyJob(ArrayList<DocumentInfo> srcs, Uri destinationUri) { // Create an ID for this copy job. Use the timestamp. mJobId = String.valueOf(SystemClock.elapsedRealtime()); // Reset the cancellation flag. Loading Loading @@ -238,42 +240,53 @@ public class CopyService extends IntentService { * Copies a file to a given location. * * @param srcInfo The source file. * @param destination The directory to copy into. * @param destinationUri The URI of the destination directory. * @throws IOException */ private void copyFile(DocumentInfo srcInfo, File destinationDir) throws IOException { private void copyFile(DocumentInfo srcInfo, Uri destinationUri) throws IOException { final Context context = getApplicationContext(); final ContentResolver resolver = context.getContentResolver(); final File destinationFile = new File(destinationDir, srcInfo.displayName); final Uri destinationUri = Uri.fromFile(destinationFile); InputStream source = null; OutputStream destination = null; final Uri writableDstUri = DocumentsContract.buildDocumentUriUsingTree(destinationUri, DocumentsContract.getTreeDocumentId(destinationUri)); final Uri dstFileUri = DocumentsContract.createDocument(resolver, writableDstUri, srcInfo.mimeType, srcInfo.displayName); CancellationSignal canceller = new CancellationSignal(); ParcelFileDescriptor srcFile = null; ParcelFileDescriptor dstFile = null; InputStream src = null; OutputStream dst = null; boolean errorOccurred = false; try { source = resolver.openInputStream(srcInfo.derivedUri); destination = resolver.openOutputStream(destinationUri); srcFile = resolver.openFileDescriptor(srcInfo.derivedUri, "r", canceller); dstFile = resolver.openFileDescriptor(dstFileUri, "w", canceller); src = new ParcelFileDescriptor.AutoCloseInputStream(srcFile); dst = new ParcelFileDescriptor.AutoCloseOutputStream(dstFile); byte[] buffer = new byte[8192]; int len; while (!mIsCancelled && ((len = source.read(buffer)) != -1)) { destination.write(buffer, 0, len); while (!mIsCancelled && ((len = src.read(buffer)) != -1)) { dst.write(buffer, 0, len); makeProgress(len); } srcFile.checkError(); dstFile.checkError(); } catch (IOException e) { errorOccurred = true; Log.e(TAG, "Error while copying " + srcInfo.displayName, e); } finally { IoUtils.closeQuietly(source); IoUtils.closeQuietly(destination); // This also ensures the file descriptors are closed. IoUtils.closeQuietly(src); IoUtils.closeQuietly(dst); } if (errorOccurred || mIsCancelled) { // Clean up half-copied files. if (!destinationFile.delete()) { Log.w(TAG, "Failed to clean up partially copied file " + srcInfo.displayName); canceller.cancel(); if (!DocumentsContract.deleteDocument(resolver, dstFileUri)) { Log.w(TAG, "Failed to clean up: " + srcInfo.displayName); } } } Loading packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java +40 −10 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ import static com.android.documentsui.model.DocumentInfo.getCursorInt; import static com.android.documentsui.model.DocumentInfo.getCursorLong; import static com.android.documentsui.model.DocumentInfo.getCursorString; import android.app.Activity; import android.app.ActivityManager; import android.app.Fragment; import android.app.FragmentManager; Loading Loading @@ -99,6 +100,8 @@ public class DirectoryFragment extends Fragment { private AbsListView mCurrentView; private List<DocumentInfo> mSelectedDocumentsForCopy; public static final int TYPE_NORMAL = 1; public static final int TYPE_SEARCH = 2; public static final int TYPE_RECENT_OPEN = 3; Loading @@ -108,6 +111,8 @@ public class DirectoryFragment extends Fragment { public static final int ANIM_DOWN = 3; public static final int ANIM_UP = 4; public static final int REQUEST_COPY_DESTINATION = 1; private int mType = TYPE_NORMAL; private String mStateKey; Loading Loading @@ -336,6 +341,36 @@ public class DirectoryFragment extends Fragment { updateDisplayState(); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { final Context context = getActivity(); final Resources res = context.getResources(); // There's only one request code right now. Replace this with a switch statement or // something more scalable when more codes are added. if (requestCode != REQUEST_COPY_DESTINATION) { return; } if (resultCode == Activity.RESULT_CANCELED || data == null) { // User pressed the back button or otherwise cancelled the destination pick. Don't // proceed with the copy. return; } Uri destination = data.getData(); List<DocumentInfo> docs = mSelectedDocumentsForCopy; Intent copyIntent = new Intent(context, CopyService.class); copyIntent.putParcelableArrayListExtra(CopyService.EXTRA_SRC_LIST, new ArrayList<DocumentInfo>(docs)); copyIntent.setData(destination); Toast.makeText(context, res.getQuantityString(R.plurals.copy_begin, docs.size(), docs.size()), Toast.LENGTH_SHORT).show(); context.startService(copyIntent); } @Override public void onStop() { super.onStop(); Loading Loading @@ -634,17 +669,12 @@ public class DirectoryFragment extends Fragment { } private void onCopyDocuments(List<DocumentInfo> docs) { final Context context = getActivity(); final Resources res = context.getResources(); mSelectedDocumentsForCopy = docs; Intent copyIntent = new Intent(context, CopyService.class); copyIntent.putParcelableArrayListExtra(CopyService.EXTRA_SRC_LIST, new ArrayList<DocumentInfo>(docs)); Toast.makeText(context, res.getQuantityString(R.plurals.copy_begin, docs.size(), docs.size()), Toast.LENGTH_SHORT).show(); context.startService(copyIntent); // Pop up a dialog to pick a destination. This is inadequate but works for now. // TODO: Implement a picker that is to spec. Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); startActivityForResult(intent, REQUEST_COPY_DESTINATION); } private static State getDisplayState(Fragment fragment) { Loading Loading
packages/DocumentsUI/src/com/android/documentsui/CopyService.java +35 −22 Original line number Diff line number Diff line Loading @@ -24,7 +24,11 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.CancellationSignal; import android.os.Environment; import android.os.ParcelFileDescriptor; import android.os.SystemClock; import android.provider.DocumentsContract; import android.text.format.DateUtils; import android.util.Log; Loading Loading @@ -82,18 +86,15 @@ public class CopyService extends IntentService { } ArrayList<DocumentInfo> srcs = intent.getParcelableArrayListExtra(EXTRA_SRC_LIST); // Use the app local files dir as a copy destination for now. This resolves to // /data/data/com.android.documentsui/files. // TODO: Add actual destination picking. File destinationDir = getFilesDir(); Uri destinationUri = intent.getData(); setupCopyJob(srcs, destinationDir); setupCopyJob(srcs, destinationUri); ArrayList<String> failedFilenames = new ArrayList<String>(); for (int i = 0; i < srcs.size() && !mIsCancelled; ++i) { DocumentInfo src = srcs.get(i); try { copyFile(src, destinationDir); copyFile(src, destinationUri); } catch (IOException e) { Log.e(TAG, "Failed to copy " + src.displayName, e); failedFilenames.add(src.displayName); Loading Loading @@ -121,8 +122,9 @@ public class CopyService extends IntentService { * files. * * @param srcs A list of src files to copy. * @param destinationUri The URI of the destination directory. */ private void setupCopyJob(ArrayList<DocumentInfo> srcs, File destinationDir) { private void setupCopyJob(ArrayList<DocumentInfo> srcs, Uri destinationUri) { // Create an ID for this copy job. Use the timestamp. mJobId = String.valueOf(SystemClock.elapsedRealtime()); // Reset the cancellation flag. Loading Loading @@ -238,42 +240,53 @@ public class CopyService extends IntentService { * Copies a file to a given location. * * @param srcInfo The source file. * @param destination The directory to copy into. * @param destinationUri The URI of the destination directory. * @throws IOException */ private void copyFile(DocumentInfo srcInfo, File destinationDir) throws IOException { private void copyFile(DocumentInfo srcInfo, Uri destinationUri) throws IOException { final Context context = getApplicationContext(); final ContentResolver resolver = context.getContentResolver(); final File destinationFile = new File(destinationDir, srcInfo.displayName); final Uri destinationUri = Uri.fromFile(destinationFile); InputStream source = null; OutputStream destination = null; final Uri writableDstUri = DocumentsContract.buildDocumentUriUsingTree(destinationUri, DocumentsContract.getTreeDocumentId(destinationUri)); final Uri dstFileUri = DocumentsContract.createDocument(resolver, writableDstUri, srcInfo.mimeType, srcInfo.displayName); CancellationSignal canceller = new CancellationSignal(); ParcelFileDescriptor srcFile = null; ParcelFileDescriptor dstFile = null; InputStream src = null; OutputStream dst = null; boolean errorOccurred = false; try { source = resolver.openInputStream(srcInfo.derivedUri); destination = resolver.openOutputStream(destinationUri); srcFile = resolver.openFileDescriptor(srcInfo.derivedUri, "r", canceller); dstFile = resolver.openFileDescriptor(dstFileUri, "w", canceller); src = new ParcelFileDescriptor.AutoCloseInputStream(srcFile); dst = new ParcelFileDescriptor.AutoCloseOutputStream(dstFile); byte[] buffer = new byte[8192]; int len; while (!mIsCancelled && ((len = source.read(buffer)) != -1)) { destination.write(buffer, 0, len); while (!mIsCancelled && ((len = src.read(buffer)) != -1)) { dst.write(buffer, 0, len); makeProgress(len); } srcFile.checkError(); dstFile.checkError(); } catch (IOException e) { errorOccurred = true; Log.e(TAG, "Error while copying " + srcInfo.displayName, e); } finally { IoUtils.closeQuietly(source); IoUtils.closeQuietly(destination); // This also ensures the file descriptors are closed. IoUtils.closeQuietly(src); IoUtils.closeQuietly(dst); } if (errorOccurred || mIsCancelled) { // Clean up half-copied files. if (!destinationFile.delete()) { Log.w(TAG, "Failed to clean up partially copied file " + srcInfo.displayName); canceller.cancel(); if (!DocumentsContract.deleteDocument(resolver, dstFileUri)) { Log.w(TAG, "Failed to clean up: " + srcInfo.displayName); } } } Loading
packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java +40 −10 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ import static com.android.documentsui.model.DocumentInfo.getCursorInt; import static com.android.documentsui.model.DocumentInfo.getCursorLong; import static com.android.documentsui.model.DocumentInfo.getCursorString; import android.app.Activity; import android.app.ActivityManager; import android.app.Fragment; import android.app.FragmentManager; Loading Loading @@ -99,6 +100,8 @@ public class DirectoryFragment extends Fragment { private AbsListView mCurrentView; private List<DocumentInfo> mSelectedDocumentsForCopy; public static final int TYPE_NORMAL = 1; public static final int TYPE_SEARCH = 2; public static final int TYPE_RECENT_OPEN = 3; Loading @@ -108,6 +111,8 @@ public class DirectoryFragment extends Fragment { public static final int ANIM_DOWN = 3; public static final int ANIM_UP = 4; public static final int REQUEST_COPY_DESTINATION = 1; private int mType = TYPE_NORMAL; private String mStateKey; Loading Loading @@ -336,6 +341,36 @@ public class DirectoryFragment extends Fragment { updateDisplayState(); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { final Context context = getActivity(); final Resources res = context.getResources(); // There's only one request code right now. Replace this with a switch statement or // something more scalable when more codes are added. if (requestCode != REQUEST_COPY_DESTINATION) { return; } if (resultCode == Activity.RESULT_CANCELED || data == null) { // User pressed the back button or otherwise cancelled the destination pick. Don't // proceed with the copy. return; } Uri destination = data.getData(); List<DocumentInfo> docs = mSelectedDocumentsForCopy; Intent copyIntent = new Intent(context, CopyService.class); copyIntent.putParcelableArrayListExtra(CopyService.EXTRA_SRC_LIST, new ArrayList<DocumentInfo>(docs)); copyIntent.setData(destination); Toast.makeText(context, res.getQuantityString(R.plurals.copy_begin, docs.size(), docs.size()), Toast.LENGTH_SHORT).show(); context.startService(copyIntent); } @Override public void onStop() { super.onStop(); Loading Loading @@ -634,17 +669,12 @@ public class DirectoryFragment extends Fragment { } private void onCopyDocuments(List<DocumentInfo> docs) { final Context context = getActivity(); final Resources res = context.getResources(); mSelectedDocumentsForCopy = docs; Intent copyIntent = new Intent(context, CopyService.class); copyIntent.putParcelableArrayListExtra(CopyService.EXTRA_SRC_LIST, new ArrayList<DocumentInfo>(docs)); Toast.makeText(context, res.getQuantityString(R.plurals.copy_begin, docs.size(), docs.size()), Toast.LENGTH_SHORT).show(); context.startService(copyIntent); // Pop up a dialog to pick a destination. This is inadequate but works for now. // TODO: Implement a picker that is to spec. Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); startActivityForResult(intent, REQUEST_COPY_DESTINATION); } private static State getDisplayState(Fragment fragment) { Loading