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.picker.OverwriteConfirmFragment;
30 import com.android.documentsui.services.FileOperationService;
31 import com.android.documentsui.services.FileOperationService.OpType;
32 import com.android.documentsui.services.FileOperations;
33 import com.android.documentsui.services.FileOperations.Callback.Status;
34 
35 import java.util.List;
36 
37 public interface DialogController {
38 
39     // Dialogs used in FilesActivity
confirmDelete(List<DocumentInfo> docs, ConfirmationCallback callback)40     void confirmDelete(List<DocumentInfo> docs, ConfirmationCallback callback);
showFileOperationStatus(int status, int opType, int docCount)41     void showFileOperationStatus(int status, int opType, int docCount);
showNoApplicationFound()42     void showNoApplicationFound();
showViewInArchivesUnsupported()43     void showViewInArchivesUnsupported();
showDocumentsClipped(int size)44     void showDocumentsClipped(int size);
45 
46     // Dialogs used in PickActivity
confirmOverwrite(FragmentManager fm, DocumentInfo overwriteTarget)47     void confirmOverwrite(FragmentManager fm, DocumentInfo overwriteTarget);
48 
49     // Should be private, but Java doesn't like me treating an interface like a mini-package.
50     public static final class RuntimeDialogController implements DialogController {
51 
52         private final Activity mActivity;
53         private final MessageBuilder mMessages;
54 
RuntimeDialogController(Activity activity, MessageBuilder messages)55         public RuntimeDialogController(Activity activity, MessageBuilder messages) {
56             mActivity = activity;
57             mMessages = messages;
58         }
59 
60         @Override
confirmDelete(List<DocumentInfo> docs, ConfirmationCallback callback)61         public void confirmDelete(List<DocumentInfo> docs, ConfirmationCallback callback) {
62             assert(!docs.isEmpty());
63 
64             TextView message =
65                     (TextView) mActivity.getLayoutInflater().inflate(
66                             R.layout.dialog_delete_confirmation, null);
67             message.setText(mMessages.generateDeleteMessage(docs));
68 
69             // For now, we implement this dialog NOT
70             // as a fragment (which can survive rotation and have its own state),
71             // but as a simple runtime dialog. So rotating a device with an
72             // active delete dialog...results in that dialog disappearing.
73             // We can do better, but don't have cycles for it now.
74             final AlertDialog alertDialog = new AlertDialog.Builder(mActivity)
75                     .setView(message)
76                     .setPositiveButton(
77                             android.R.string.ok,
78                             new DialogInterface.OnClickListener() {
79                                 @Override
80                                 public void onClick(DialogInterface dialog, int id) {
81                                     callback.accept(ConfirmationCallback.CONFIRM);
82                                 }
83                             })
84                     .setNegativeButton(android.R.string.cancel, null)
85                     .create();
86 
87             alertDialog.setOnShowListener(
88                     (DialogInterface) -> {
89                         Button positive = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
90                         positive.setFocusable(true);
91                         positive.requestFocus();
92                     });
93             alertDialog.show();
94         }
95 
96         @Override
showFileOperationStatus( @tatus int status, @OpType int opType, int docCount)97         public void showFileOperationStatus(
98                 @Status int status, @OpType int opType, int docCount) {
99             if (status == FileOperations.Callback.STATUS_REJECTED) {
100                 Snackbars.showOperationRejected(mActivity);
101                 return;
102             }
103             if (status == FileOperations.Callback.STATUS_FAILED) {
104                 Snackbars.showOperationFailed(mActivity);
105                 return;
106             }
107 
108             if (docCount == 0) {
109                 // Nothing has been pasted, so there is no need to show a snackbar.
110                 return;
111             }
112 
113             switch (opType) {
114                 case FileOperationService.OPERATION_MOVE:
115                     Snackbars.showMove(mActivity, docCount);
116                     break;
117                 case FileOperationService.OPERATION_COPY:
118                     Snackbars.showCopy(mActivity, docCount);
119                     break;
120                 case FileOperationService.OPERATION_COMPRESS:
121                     Snackbars.showCompress(mActivity, docCount);
122                     break;
123                 case FileOperationService.OPERATION_EXTRACT:
124                     Snackbars.showExtract(mActivity, docCount);
125                     break;
126                 case FileOperationService.OPERATION_DELETE:
127                     Snackbars.showDelete(mActivity, docCount);
128                     break;
129                 default:
130                     throw new UnsupportedOperationException("Unsupported Operation: " + opType);
131             }
132         }
133 
134         @Override
showNoApplicationFound()135         public void showNoApplicationFound() {
136             Snackbars.makeSnackbar(
137                     mActivity, R.string.toast_no_application, Snackbar.LENGTH_SHORT).show();
138         }
139 
140         @Override
showViewInArchivesUnsupported()141         public void showViewInArchivesUnsupported() {
142             Snackbars.makeSnackbar(mActivity, R.string.toast_view_in_archives_unsupported,
143                     Snackbar.LENGTH_SHORT).show();
144         }
145 
146         @Override
showDocumentsClipped(int size)147         public void showDocumentsClipped(int size) {
148             Snackbars.showDocumentsClipped(mActivity, size);
149         }
150 
151         @Override
confirmOverwrite(FragmentManager fm, DocumentInfo overwriteTarget)152         public void confirmOverwrite(FragmentManager fm, DocumentInfo overwriteTarget) {
153             OverwriteConfirmFragment.show(fm, overwriteTarget);
154         }
155     }
156 
create(Activity activity, MessageBuilder messages)157     static DialogController create(Activity activity, MessageBuilder messages) {
158         return new RuntimeDialogController(activity, messages);
159     }
160 }
161