1 /* 2 * Copyright (C) 2017 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 com.android.documentsui.services; 18 19 import static android.content.ContentResolver.wrap; 20 21 import static com.android.documentsui.services.FileOperationService.OPERATION_MOVE; 22 23 import android.app.Notification; 24 import android.app.Notification.Builder; 25 import android.content.ContentResolver; 26 import android.content.Context; 27 import android.net.Uri; 28 import android.os.Messenger; 29 import android.os.ParcelFileDescriptor; 30 import android.os.RemoteException; 31 import android.provider.DocumentsContract; 32 import android.util.Log; 33 34 import com.android.documentsui.R; 35 import com.android.documentsui.archives.ArchivesProvider; 36 import com.android.documentsui.base.DocumentInfo; 37 import com.android.documentsui.base.DocumentStack; 38 import com.android.documentsui.base.Features; 39 import com.android.documentsui.base.UserId; 40 import com.android.documentsui.clipping.UrisSupplier; 41 42 import java.io.FileNotFoundException; 43 44 // TODO: Stop extending CopyJob. 45 final class CompressJob extends CopyJob { 46 47 private static final String TAG = "CompressJob"; 48 private static final String NEW_ARCHIVE_EXTENSION = ".zip"; 49 50 private Uri mArchiveUri; 51 52 /** 53 * Moves files to a destination identified by {@code destination}. 54 * Performs most work by delegating to CopyJob, then deleting 55 * a file after it has been copied. 56 * 57 * @see @link {@link Job} constructor for most param descriptions. 58 */ CompressJob(Context service, Listener listener, String id, DocumentStack destination, UrisSupplier srcs, Messenger messenger, Features features)59 CompressJob(Context service, Listener listener, String id, DocumentStack destination, 60 UrisSupplier srcs, Messenger messenger, Features features) { 61 super(service, listener, id, OPERATION_MOVE, destination, srcs, messenger, features); 62 } 63 64 @Override createProgressBuilder()65 Builder createProgressBuilder() { 66 return super.createProgressBuilder( 67 service.getString(R.string.compress_notification_title), 68 R.drawable.ic_menu_compress, 69 service.getString(android.R.string.cancel), 70 R.drawable.ic_cab_cancel); 71 } 72 73 @Override getSetupNotification()74 public Notification getSetupNotification() { 75 return getSetupNotification(service.getString(R.string.compress_preparing)); 76 } 77 78 @Override getProgressNotification()79 public Notification getProgressNotification() { 80 return getProgressNotification(R.string.copy_remaining); 81 } 82 83 @Override getFailureNotification()84 Notification getFailureNotification() { 85 return getFailureNotification( 86 R.plurals.compress_error_notification_title, R.drawable.ic_menu_compress); 87 } 88 89 @Override setUp()90 public boolean setUp() { 91 if (!super.setUp()) { 92 return false; 93 } 94 95 final ContentResolver resolver = appContext.getContentResolver(); 96 97 // TODO: Move this to DocumentsProvider. 98 99 String displayName; 100 if (mResolvedDocs.size() == 1) { 101 displayName = mResolvedDocs.get(0).displayName + NEW_ARCHIVE_EXTENSION; 102 } else { 103 displayName = service.getString(R.string.new_archive_file_name, NEW_ARCHIVE_EXTENSION); 104 } 105 106 try { 107 mArchiveUri = DocumentsContract.createDocument( 108 resolver, mDstInfo.derivedUri, "application/zip", displayName); 109 } catch (Exception e) { 110 mArchiveUri = null; 111 } 112 113 try { 114 mDstInfo = DocumentInfo.fromUri(resolver, ArchivesProvider.buildUriForArchive( 115 mArchiveUri, ParcelFileDescriptor.MODE_WRITE_ONLY), UserId.DEFAULT_USER); 116 ArchivesProvider.acquireArchive(getClient(mDstInfo), mDstInfo.derivedUri); 117 } catch (FileNotFoundException e) { 118 Log.e(TAG, "Failed to create dstInfo.", e); 119 failureCount = mResourceUris.getItemCount(); 120 return false; 121 } catch (RemoteException e) { 122 Log.e(TAG, "Failed to acquire the archive.", e); 123 failureCount = mResourceUris.getItemCount(); 124 return false; 125 } 126 127 return true; 128 } 129 130 @Override finish()131 void finish() { 132 try { 133 ArchivesProvider.releaseArchive(getClient(mDstInfo), mDstInfo.derivedUri); 134 } catch (RemoteException e) { 135 Log.e(TAG, "Failed to release the archive."); 136 } 137 138 // Remove the archive file in case of an error. 139 try { 140 if (!isFinished() || isCanceled()) { 141 DocumentsContract.deleteDocument(wrap(getClient(mArchiveUri)), mArchiveUri); 142 } 143 } catch (RemoteException | FileNotFoundException e) { 144 Log.w(TAG, "Failed to cleanup after compress error: " + mDstInfo.toString(), e); 145 } 146 147 super.finish(); 148 } 149 150 /** 151 * {@inheritDoc} 152 * 153 * Only check space for moves across authorities. For now we don't know if the doc in 154 * {@link #mSrcs} is in the same root of destination, and if it's optimized move in the same 155 * root it should succeed regardless of free space, but it's for sure a failure if there is no 156 * enough free space if docs are moved from another authority. 157 */ 158 @Override checkSpace()159 boolean checkSpace() { 160 // We're unable to say how much space the archive will take, so assume 161 // it will fit. 162 return true; 163 } 164 processDocument(DocumentInfo src, DocumentInfo dest)165 void processDocument(DocumentInfo src, DocumentInfo dest) throws ResourceException { 166 byteCopyDocument(src, dest); 167 } 168 169 @Override toString()170 public String toString() { 171 return new StringBuilder() 172 .append("CompressJob") 173 .append("{") 174 .append("id=" + id) 175 .append(", uris=" + mResourceUris) 176 .append(", docs=" + mResolvedDocs) 177 .append(", destination=" + stack) 178 .append("}") 179 .toString(); 180 } 181 } 182