• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 package com.android.documentsui.ui;
17 
18 import android.app.Activity;
19 import android.app.AlertDialog;
20 import android.app.FragmentManager;
21 import android.content.DialogInterface;
22 import android.support.design.widget.Snackbar;
23 import android.widget.Button;
24 import android.widget.TextView;
25 
26 import com.android.documentsui.R;
27 import com.android.documentsui.base.ConfirmationCallback;
28 import com.android.documentsui.base.DocumentInfo;
29 import com.android.documentsui.base.Features;
30 import com.android.documentsui.picker.OverwriteConfirmFragment;
31 import com.android.documentsui.services.FileOperation;
32 import com.android.documentsui.services.FileOperationService.OpType;
33 import com.android.documentsui.services.FileOperationService;
34 import com.android.documentsui.services.FileOperations.Callback.Status;
35 import com.android.documentsui.services.FileOperations;
36 
37 import java.util.List;
38 import javax.annotation.Nullable;
39 
40 public interface DialogController {
41 
42     // Dialogs used in FilesActivity
confirmDelete(List<DocumentInfo> docs, ConfirmationCallback callback)43     void confirmDelete(List<DocumentInfo> docs, ConfirmationCallback callback);
showFileOperationStatus(int status, int opType, int docCount)44     void showFileOperationStatus(int status, int opType, int docCount);
45 
46     /**
47      * There can be only one progress dialog active at the time. Each call to this
48      * method will discard any previously created progress dialogs.
49      */
showProgressDialog(String jobId, FileOperation operation)50     void showProgressDialog(String jobId, FileOperation operation);
51 
showNoApplicationFound()52     void showNoApplicationFound();
showOperationUnsupported()53     void showOperationUnsupported();
showViewInArchivesUnsupported()54     void showViewInArchivesUnsupported();
showDocumentsClipped(int size)55     void showDocumentsClipped(int size);
56 
57     // Dialogs used in PickActivity
confirmOverwrite(FragmentManager fm, DocumentInfo overwriteTarget)58     void confirmOverwrite(FragmentManager fm, DocumentInfo overwriteTarget);
59 
60     // Should be private, but Java doesn't like me treating an interface like a mini-package.
61     public static final class RuntimeDialogController implements DialogController {
62 
63         private final Activity mActivity;
64         private final MessageBuilder mMessages;
65         private final Features mFeatures;
66         private OperationProgressDialog mCurrentProgressDialog = null;
67 
RuntimeDialogController(Features features, Activity activity, MessageBuilder messages)68         public RuntimeDialogController(Features features, Activity activity, MessageBuilder messages) {
69             mFeatures = features;
70             mActivity = activity;
71             mMessages = messages;
72         }
73 
74         @Override
confirmDelete(List<DocumentInfo> docs, ConfirmationCallback callback)75         public void confirmDelete(List<DocumentInfo> docs, ConfirmationCallback callback) {
76             assert(!docs.isEmpty());
77 
78             TextView message =
79                     (TextView) mActivity.getLayoutInflater().inflate(
80                             R.layout.dialog_delete_confirmation, null);
81             message.setText(mMessages.generateDeleteMessage(docs));
82 
83             // For now, we implement this dialog NOT
84             // as a fragment (which can survive rotation and have its own state),
85             // but as a simple runtime dialog. So rotating a device with an
86             // active delete dialog...results in that dialog disappearing.
87             // We can do better, but don't have cycles for it now.
88             final AlertDialog alertDialog = new AlertDialog.Builder(mActivity)
89                     .setView(message)
90                     .setPositiveButton(
91                             android.R.string.ok,
92                             new DialogInterface.OnClickListener() {
93                                 @Override
94                                 public void onClick(DialogInterface dialog, int id) {
95                                     callback.accept(ConfirmationCallback.CONFIRM);
96                                 }
97                             })
98                     .setNegativeButton(android.R.string.cancel, null)
99                     .create();
100 
101             alertDialog.setOnShowListener(
102                     (DialogInterface) -> {
103                         Button positive = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
104                         positive.setFocusable(true);
105                         positive.requestFocus();
106                     });
107             alertDialog.show();
108         }
109 
110         @Override
showFileOperationStatus(@tatus int status, @OpType int opType, int docCount)111         public void showFileOperationStatus(@Status int status, @OpType int opType,
112                 int docCount) {
113             if (status == FileOperations.Callback.STATUS_REJECTED) {
114                 showOperationUnsupported();
115                 return;
116             }
117             if (status == FileOperations.Callback.STATUS_FAILED) {
118                 Snackbars.showOperationFailed(mActivity);
119                 return;
120             }
121 
122             if (docCount == 0) {
123                 // Nothing has been pasted, so there is no need to show a snackbar.
124                 return;
125             }
126 
127             if (shouldShowProgressDialogForOperation(opType)) {
128                 // The operation has a progress dialog created, so do not show a snackbar
129                 // for operation start, as it would duplicate the UI.
130                 return;
131             }
132 
133             switch (opType) {
134                 case FileOperationService.OPERATION_MOVE:
135                     Snackbars.showMove(mActivity, docCount);
136                     break;
137                 case FileOperationService.OPERATION_COPY:
138                     Snackbars.showCopy(mActivity, docCount);
139                     break;
140                 case FileOperationService.OPERATION_COMPRESS:
141                     Snackbars.showCompress(mActivity, docCount);
142                     break;
143                 case FileOperationService.OPERATION_EXTRACT:
144                     Snackbars.showExtract(mActivity, docCount);
145                     break;
146                 case FileOperationService.OPERATION_DELETE:
147                     Snackbars.showDelete(mActivity, docCount);
148                     break;
149                 default:
150                     throw new UnsupportedOperationException("Unsupported Operation: " + opType);
151             }
152         }
153 
shouldShowProgressDialogForOperation(@pType int opType)154         private boolean shouldShowProgressDialogForOperation(@OpType int opType) {
155             // TODO: Hook up progress dialog to the delete operation.
156             if (opType == FileOperationService.OPERATION_DELETE) {
157                 return false;
158             }
159 
160             return mFeatures.isJobProgressDialogEnabled();
161         }
162 
163         @Override
showProgressDialog(String jobId, FileOperation operation)164         public void showProgressDialog(String jobId, FileOperation operation) {
165             assert(operation.getOpType() != FileOperationService.OPERATION_UNKNOWN);
166 
167             if (!shouldShowProgressDialogForOperation(operation.getOpType())) {
168                 return;
169             }
170 
171             if (mCurrentProgressDialog != null) {
172                 mCurrentProgressDialog.dismiss();
173             }
174 
175             mCurrentProgressDialog = OperationProgressDialog.create(mActivity, jobId, operation);
176             mCurrentProgressDialog.show();
177         }
178 
179         @Override
showNoApplicationFound()180         public void showNoApplicationFound() {
181             Snackbars.makeSnackbar(
182                     mActivity, R.string.toast_no_application, Snackbar.LENGTH_SHORT).show();
183         }
184 
185         @Override
showOperationUnsupported()186         public void showOperationUnsupported() {
187             Snackbars.showOperationRejected(mActivity);
188         }
189 
190         @Override
showViewInArchivesUnsupported()191         public void showViewInArchivesUnsupported() {
192             Snackbars.makeSnackbar(mActivity, R.string.toast_view_in_archives_unsupported,
193                     Snackbar.LENGTH_SHORT).show();
194         }
195 
196         @Override
showDocumentsClipped(int size)197         public void showDocumentsClipped(int size) {
198             Snackbars.showDocumentsClipped(mActivity, size);
199         }
200 
201         @Override
confirmOverwrite(FragmentManager fm, DocumentInfo overwriteTarget)202         public void confirmOverwrite(FragmentManager fm, DocumentInfo overwriteTarget) {
203             OverwriteConfirmFragment.show(fm, overwriteTarget);
204         }
205     }
206 
create(Features features, Activity activity, MessageBuilder messages)207     static DialogController create(Features features, Activity activity, MessageBuilder messages) {
208         return new RuntimeDialogController(features, activity, messages);
209     }
210 }
211