1 /*
2  * Copyright (C) 2016 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 com.android.documentsui.base.SharedMinimal.DEBUG;
20 import static com.android.documentsui.services.FileOperationService.OPERATION_DELETE;
21 
22 import android.app.Notification;
23 import android.app.Notification.Builder;
24 import android.content.ContentResolver;
25 import android.content.Context;
26 import android.net.Uri;
27 import android.util.Log;
28 
29 import com.android.documentsui.MetricConsts;
30 import com.android.documentsui.Metrics;
31 import com.android.documentsui.R;
32 import com.android.documentsui.base.DocumentInfo;
33 import com.android.documentsui.base.DocumentStack;
34 import com.android.documentsui.base.Features;
35 import com.android.documentsui.base.UserId;
36 import com.android.documentsui.clipping.UrisSupplier;
37 
38 import java.io.FileNotFoundException;
39 
40 import javax.annotation.Nullable;
41 
42 final class DeleteJob extends ResolvedResourcesJob {
43 
44     private static final String TAG = "DeleteJob";
45 
46     private final Uri mParentUri;
47 
48     private volatile int mDocsProcessed = 0;
49 
50     /**
51      * Moves files to a destination identified by {@code destination}.
52      * Performs most work by delegating to CopyJob, then deleting
53      * a file after it has been copied.
54      *
55      * @see @link {@link Job} constructor for most param descriptions.
56      */
DeleteJob(Context service, Listener listener, String id, DocumentStack stack, UrisSupplier srcs, @Nullable Uri srcParent, Features features)57     DeleteJob(Context service, Listener listener, String id, DocumentStack stack,
58             UrisSupplier srcs, @Nullable Uri srcParent, Features features) {
59         super(service, listener, id, OPERATION_DELETE, stack, srcs, features);
60         mParentUri = srcParent;
61     }
62 
63     @Override
createProgressBuilder()64     Builder createProgressBuilder() {
65         return super.createProgressBuilder(
66                 service.getString(R.string.delete_notification_title),
67                 R.drawable.ic_menu_delete,
68                 service.getString(android.R.string.cancel),
69                 R.drawable.ic_cab_cancel);
70     }
71 
72     @Override
getSetupNotification()73     public Notification getSetupNotification() {
74         return getSetupNotification(service.getString(R.string.delete_preparing));
75     }
76 
77     @Override
getProgressNotification()78     public Notification getProgressNotification() {
79         mProgressBuilder.setProgress(mResourceUris.getItemCount(), mDocsProcessed, false);
80         String format = service.getString(R.string.delete_progress);
81         mProgressBuilder.setSubText(
82                 String.format(format, mDocsProcessed, mResourceUris.getItemCount()));
83 
84         mProgressBuilder.setContentText(null);
85 
86         return mProgressBuilder.build();
87     }
88 
89     @Override
getFailureNotification()90     Notification getFailureNotification() {
91         return getFailureNotification(
92                 R.plurals.delete_error_notification_title, R.drawable.ic_menu_delete);
93     }
94 
95     @Override
getWarningNotification()96     Notification getWarningNotification() {
97         throw new UnsupportedOperationException();
98     }
99 
100     @Override
start()101     void start() {
102         ContentResolver resolver = appContext.getContentResolver();
103 
104         DocumentInfo parentDoc;
105         try {
106             parentDoc = mParentUri != null
107                 ? DocumentInfo.fromUri(resolver, mParentUri, UserId.DEFAULT_USER)
108                 : null;
109         } catch (FileNotFoundException e) {
110           Log.e(TAG, "Failed to resolve parent from Uri: " + mParentUri + ". Cannot continue.", e);
111           failureCount += this.mResourceUris.getItemCount();
112           return;
113         }
114 
115         for (DocumentInfo doc : mResolvedDocs) {
116             if (DEBUG) {
117                 Log.d(TAG, "Deleting document @ " + doc.derivedUri);
118             }
119             try {
120                 deleteDocument(doc, parentDoc);
121             } catch (ResourceException e) {
122                 Metrics.logFileOperationFailure(
123                         appContext, MetricConsts.SUBFILEOP_DELETE_DOCUMENT, doc.derivedUri);
124                 Log.e(TAG, "Failed to delete document @ " + doc.derivedUri, e);
125                 onFileFailed(doc);
126             }
127 
128             mDocsProcessed++;
129             if (isCanceled()) {
130                 return;
131             }
132         }
133 
134         Metrics.logFileOperation(operationType, mResolvedDocs, null);
135     }
136 
137     @Override
toString()138     public String toString() {
139         return new StringBuilder()
140                 .append("DeleteJob")
141                 .append("{")
142                 .append("id=" + id)
143                 .append(", uris=" + mResourceUris)
144                 .append(", docs=" + mResolvedDocs)
145                 .append(", srcParent=" + mParentUri)
146                 .append(", location=" + stack)
147                 .append("}")
148                 .toString();
149     }
150 }
151