1c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay/* 2c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay * Copyright (C) 2016 The Android Open Source Project 3c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay * 4c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay * Licensed under the Apache License, Version 2.0 (the "License"); 5c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay * you may not use this file except in compliance with the License. 6c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay * You may obtain a copy of the License at 7c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay * 8c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay * http://www.apache.org/licenses/LICENSE-2.0 9c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay * 10c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay * Unless required by applicable law or agreed to in writing, software 11c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay * distributed under the License is distributed on an "AS IS" BASIS, 12c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay * See the License for the specific language governing permissions and 14c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay * limitations under the License. 15c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay */ 16c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKaypackage com.android.documentsui.ui; 17c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay 18c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKayimport android.app.Activity; 19c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKayimport android.app.AlertDialog; 2023ac60cd2b1beb59b6cd760b91c7e1b1f7e8fe49Garfield Tanimport android.app.FragmentManager; 21c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKayimport android.content.DialogInterface; 22eed2f4ec78227b5e173ff667a5517637a91b9a30Steve McKayimport android.support.design.widget.Snackbar; 236f6d578302425aca2eb5ec2bd278be9a77065172Ben Linimport android.widget.Button; 24c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKayimport android.widget.TextView; 25c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay 26c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKayimport com.android.documentsui.R; 27c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKayimport com.android.documentsui.base.ConfirmationCallback; 28c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKayimport com.android.documentsui.base.DocumentInfo; 290d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewskiimport com.android.documentsui.base.Features; 3023ac60cd2b1beb59b6cd760b91c7e1b1f7e8fe49Garfield Tanimport com.android.documentsui.picker.OverwriteConfirmFragment; 310d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewskiimport com.android.documentsui.services.FileOperation; 32c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKayimport com.android.documentsui.services.FileOperationService.OpType; 330d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewskiimport com.android.documentsui.services.FileOperationService; 34c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKayimport com.android.documentsui.services.FileOperations.Callback.Status; 350d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewskiimport com.android.documentsui.services.FileOperations; 36c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay 37c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKayimport java.util.List; 380d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewskiimport javax.annotation.Nullable; 39c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay 40c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKaypublic interface DialogController { 41c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay 4223ac60cd2b1beb59b6cd760b91c7e1b1f7e8fe49Garfield Tan // Dialogs used in FilesActivity 43c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay void confirmDelete(List<DocumentInfo> docs, ConfirmationCallback callback); 4468689622255bc831ad0f832de4c8f82c319fdb29Ben Lin void showFileOperationStatus(int status, int opType, int docCount); 450d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski 460d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski /** 470d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski * There can be only one progress dialog active at the time. Each call to this 480d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski * method will discard any previously created progress dialogs. 490d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski */ 500d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski void showProgressDialog(String jobId, FileOperation operation); 510d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski 52eed2f4ec78227b5e173ff667a5517637a91b9a30Steve McKay void showNoApplicationFound(); 53c1a32aea59f47b06f3157a7f87ef5b9bf45f7627Ben Lin void showOperationUnsupported(); 54179f0695a8d4a0d87d28303f8d6e5e9c7194d981Tomasz Mikolajewski void showViewInArchivesUnsupported(); 55d947f0192142c7db40d7dfaa8d0c6caaa1cf7c36Ben Lin void showDocumentsClipped(int size); 56c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay 5723ac60cd2b1beb59b6cd760b91c7e1b1f7e8fe49Garfield Tan // Dialogs used in PickActivity 5823ac60cd2b1beb59b6cd760b91c7e1b1f7e8fe49Garfield Tan void confirmOverwrite(FragmentManager fm, DocumentInfo overwriteTarget); 5923ac60cd2b1beb59b6cd760b91c7e1b1f7e8fe49Garfield Tan 60c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay // Should be private, but Java doesn't like me treating an interface like a mini-package. 61c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay public static final class RuntimeDialogController implements DialogController { 62c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay 63c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay private final Activity mActivity; 64c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay private final MessageBuilder mMessages; 650d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski private final Features mFeatures; 660d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski private OperationProgressDialog mCurrentProgressDialog = null; 67c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay 680d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski public RuntimeDialogController(Features features, Activity activity, MessageBuilder messages) { 690d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski mFeatures = features; 70c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay mActivity = activity; 715b0a2c187a9e446b683687817d22cbe443585223Steve McKay mMessages = messages; 72c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay } 73c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay 74c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay @Override 75c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay public void confirmDelete(List<DocumentInfo> docs, ConfirmationCallback callback) { 76c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay assert(!docs.isEmpty()); 77c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay 78c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay TextView message = 79c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay (TextView) mActivity.getLayoutInflater().inflate( 80c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay R.layout.dialog_delete_confirmation, null); 81c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay message.setText(mMessages.generateDeleteMessage(docs)); 82c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay 83c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay // For now, we implement this dialog NOT 84c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay // as a fragment (which can survive rotation and have its own state), 85c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay // but as a simple runtime dialog. So rotating a device with an 86c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay // active delete dialog...results in that dialog disappearing. 87c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay // We can do better, but don't have cycles for it now. 886f6d578302425aca2eb5ec2bd278be9a77065172Ben Lin final AlertDialog alertDialog = new AlertDialog.Builder(mActivity) 896f6d578302425aca2eb5ec2bd278be9a77065172Ben Lin .setView(message) 906f6d578302425aca2eb5ec2bd278be9a77065172Ben Lin .setPositiveButton( 916f6d578302425aca2eb5ec2bd278be9a77065172Ben Lin android.R.string.ok, 926f6d578302425aca2eb5ec2bd278be9a77065172Ben Lin new DialogInterface.OnClickListener() { 936f6d578302425aca2eb5ec2bd278be9a77065172Ben Lin @Override 946f6d578302425aca2eb5ec2bd278be9a77065172Ben Lin public void onClick(DialogInterface dialog, int id) { 956f6d578302425aca2eb5ec2bd278be9a77065172Ben Lin callback.accept(ConfirmationCallback.CONFIRM); 966f6d578302425aca2eb5ec2bd278be9a77065172Ben Lin } 976f6d578302425aca2eb5ec2bd278be9a77065172Ben Lin }) 986f6d578302425aca2eb5ec2bd278be9a77065172Ben Lin .setNegativeButton(android.R.string.cancel, null) 996f6d578302425aca2eb5ec2bd278be9a77065172Ben Lin .create(); 1006f6d578302425aca2eb5ec2bd278be9a77065172Ben Lin 1016f6d578302425aca2eb5ec2bd278be9a77065172Ben Lin alertDialog.setOnShowListener( 1026f6d578302425aca2eb5ec2bd278be9a77065172Ben Lin (DialogInterface) -> { 1036f6d578302425aca2eb5ec2bd278be9a77065172Ben Lin Button positive = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE); 1046f6d578302425aca2eb5ec2bd278be9a77065172Ben Lin positive.setFocusable(true); 1056f6d578302425aca2eb5ec2bd278be9a77065172Ben Lin positive.requestFocus(); 1066f6d578302425aca2eb5ec2bd278be9a77065172Ben Lin }); 1076f6d578302425aca2eb5ec2bd278be9a77065172Ben Lin alertDialog.show(); 108c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay } 109c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay 110c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay @Override 1110d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski public void showFileOperationStatus(@Status int status, @OpType int opType, 1120d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski int docCount) { 113c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay if (status == FileOperations.Callback.STATUS_REJECTED) { 114c1a32aea59f47b06f3157a7f87ef5b9bf45f7627Ben Lin showOperationUnsupported(); 115f657025bfc915df39e06e8e21067af2fd045759dJon Mann return; 116f657025bfc915df39e06e8e21067af2fd045759dJon Mann } 117f657025bfc915df39e06e8e21067af2fd045759dJon Mann if (status == FileOperations.Callback.STATUS_FAILED) { 11868689622255bc831ad0f832de4c8f82c319fdb29Ben Lin Snackbars.showOperationFailed(mActivity); 119c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay return; 120c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay } 121c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay 122c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay if (docCount == 0) { 123c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay // Nothing has been pasted, so there is no need to show a snackbar. 124c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay return; 125c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay } 126c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay 1270d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski if (shouldShowProgressDialogForOperation(opType)) { 1280d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski // The operation has a progress dialog created, so do not show a snackbar 1290d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski // for operation start, as it would duplicate the UI. 1300d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski return; 1310d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski } 1320d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski 133c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay switch (opType) { 134c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay case FileOperationService.OPERATION_MOVE: 135c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay Snackbars.showMove(mActivity, docCount); 136c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay break; 137c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay case FileOperationService.OPERATION_COPY: 138c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay Snackbars.showCopy(mActivity, docCount); 139c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay break; 140bc7eb5fc39b5555eedc7d7b3591687343ecda18dTomasz Mikolajewski case FileOperationService.OPERATION_COMPRESS: 141bc7eb5fc39b5555eedc7d7b3591687343ecda18dTomasz Mikolajewski Snackbars.showCompress(mActivity, docCount); 142bc7eb5fc39b5555eedc7d7b3591687343ecda18dTomasz Mikolajewski break; 143bc7eb5fc39b5555eedc7d7b3591687343ecda18dTomasz Mikolajewski case FileOperationService.OPERATION_EXTRACT: 144bc7eb5fc39b5555eedc7d7b3591687343ecda18dTomasz Mikolajewski Snackbars.showExtract(mActivity, docCount); 145bc7eb5fc39b5555eedc7d7b3591687343ecda18dTomasz Mikolajewski break; 146c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay case FileOperationService.OPERATION_DELETE: 14768689622255bc831ad0f832de4c8f82c319fdb29Ben Lin Snackbars.showDelete(mActivity, docCount); 148c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay break; 149c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay default: 150c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay throw new UnsupportedOperationException("Unsupported Operation: " + opType); 151c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay } 152eed2f4ec78227b5e173ff667a5517637a91b9a30Steve McKay } 153eed2f4ec78227b5e173ff667a5517637a91b9a30Steve McKay 1540d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski private boolean shouldShowProgressDialogForOperation(@OpType int opType) { 1550d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski // TODO: Hook up progress dialog to the delete operation. 1560d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski if (opType == FileOperationService.OPERATION_DELETE) { 1570d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski return false; 1580d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski } 1590d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski 1600d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski return mFeatures.isJobProgressDialogEnabled(); 1610d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski } 1620d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski 1630d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski @Override 1640d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski public void showProgressDialog(String jobId, FileOperation operation) { 1650d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski assert(operation.getOpType() != FileOperationService.OPERATION_UNKNOWN); 1660d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski 1670d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski if (!shouldShowProgressDialogForOperation(operation.getOpType())) { 1680d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski return; 1690d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski } 1700d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski 1710d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski if (mCurrentProgressDialog != null) { 1720d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski mCurrentProgressDialog.dismiss(); 1730d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski } 1740d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski 1750d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski mCurrentProgressDialog = OperationProgressDialog.create(mActivity, jobId, operation); 1760d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski mCurrentProgressDialog.show(); 1770d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski } 1780d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski 179eed2f4ec78227b5e173ff667a5517637a91b9a30Steve McKay @Override 180eed2f4ec78227b5e173ff667a5517637a91b9a30Steve McKay public void showNoApplicationFound() { 181eed2f4ec78227b5e173ff667a5517637a91b9a30Steve McKay Snackbars.makeSnackbar( 182eed2f4ec78227b5e173ff667a5517637a91b9a30Steve McKay mActivity, R.string.toast_no_application, Snackbar.LENGTH_SHORT).show(); 183eed2f4ec78227b5e173ff667a5517637a91b9a30Steve McKay } 184d947f0192142c7db40d7dfaa8d0c6caaa1cf7c36Ben Lin 185d947f0192142c7db40d7dfaa8d0c6caaa1cf7c36Ben Lin @Override 186c1a32aea59f47b06f3157a7f87ef5b9bf45f7627Ben Lin public void showOperationUnsupported() { 187c1a32aea59f47b06f3157a7f87ef5b9bf45f7627Ben Lin Snackbars.showOperationRejected(mActivity); 188c1a32aea59f47b06f3157a7f87ef5b9bf45f7627Ben Lin } 189c1a32aea59f47b06f3157a7f87ef5b9bf45f7627Ben Lin 190c1a32aea59f47b06f3157a7f87ef5b9bf45f7627Ben Lin @Override 191179f0695a8d4a0d87d28303f8d6e5e9c7194d981Tomasz Mikolajewski public void showViewInArchivesUnsupported() { 192179f0695a8d4a0d87d28303f8d6e5e9c7194d981Tomasz Mikolajewski Snackbars.makeSnackbar(mActivity, R.string.toast_view_in_archives_unsupported, 193179f0695a8d4a0d87d28303f8d6e5e9c7194d981Tomasz Mikolajewski Snackbar.LENGTH_SHORT).show(); 194179f0695a8d4a0d87d28303f8d6e5e9c7194d981Tomasz Mikolajewski } 195179f0695a8d4a0d87d28303f8d6e5e9c7194d981Tomasz Mikolajewski 196179f0695a8d4a0d87d28303f8d6e5e9c7194d981Tomasz Mikolajewski @Override 197d947f0192142c7db40d7dfaa8d0c6caaa1cf7c36Ben Lin public void showDocumentsClipped(int size) { 198d947f0192142c7db40d7dfaa8d0c6caaa1cf7c36Ben Lin Snackbars.showDocumentsClipped(mActivity, size); 199d947f0192142c7db40d7dfaa8d0c6caaa1cf7c36Ben Lin } 20023ac60cd2b1beb59b6cd760b91c7e1b1f7e8fe49Garfield Tan 20123ac60cd2b1beb59b6cd760b91c7e1b1f7e8fe49Garfield Tan @Override 20223ac60cd2b1beb59b6cd760b91c7e1b1f7e8fe49Garfield Tan public void confirmOverwrite(FragmentManager fm, DocumentInfo overwriteTarget) { 20323ac60cd2b1beb59b6cd760b91c7e1b1f7e8fe49Garfield Tan OverwriteConfirmFragment.show(fm, overwriteTarget); 20423ac60cd2b1beb59b6cd760b91c7e1b1f7e8fe49Garfield Tan } 205c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay } 206c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay 2070d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski static DialogController create(Features features, Activity activity, MessageBuilder messages) { 2080d83d32b66332034b17b327352693b125e9b3a05Tomasz Mikolajewski return new RuntimeDialogController(features, activity, messages); 209c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay } 210c8889af6757ecb348fb8fd6dadf84d67d3478cbfSteve McKay} 211