• 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 
17 package com.android.documentsui;
18 
19 import static com.android.documentsui.base.SharedMinimal.DEBUG;
20 
21 import android.annotation.IdRes;
22 import android.annotation.Nullable;
23 import android.app.Activity;
24 import android.text.TextUtils;
25 import android.util.Log;
26 import android.view.ActionMode;
27 import android.view.Menu;
28 import android.view.MenuItem;
29 import android.view.View;
30 
31 import com.android.documentsui.MenuManager.SelectionDetails;
32 import com.android.documentsui.base.ConfirmationCallback;
33 import com.android.documentsui.base.ConfirmationCallback.Result;
34 import com.android.documentsui.base.EventHandler;
35 import com.android.documentsui.base.Menus;
36 import com.android.documentsui.selection.Selection;
37 import com.android.documentsui.selection.SelectionHelper;
38 import com.android.documentsui.selection.SelectionHelper.SelectionObserver;
39 import com.android.documentsui.ui.MessageBuilder;
40 
41 /**
42  * A controller that listens to selection changes and manages life cycles of action modes.
43  */
44 public class ActionModeController extends SelectionObserver
45         implements ActionMode.Callback, ActionModeAddons {
46 
47     private static final String TAG = "ActionModeController";
48 
49     private final Activity mActivity;
50     private final SelectionHelper mSelectionMgr;
51     private final MenuManager mMenuManager;
52     private final MessageBuilder mMessages;
53 
54     private final ContentScope mScope = new ContentScope();
55     private final Selection mSelected = new Selection();
56 
57     private @Nullable ActionMode mActionMode;
58     private @Nullable Menu mMenu;
59 
ActionModeController( Activity activity, SelectionHelper selectionMgr, MenuManager menuManager, MessageBuilder messages)60     public ActionModeController(
61             Activity activity,
62             SelectionHelper selectionMgr,
63             MenuManager menuManager,
64             MessageBuilder messages) {
65 
66         mActivity = activity;
67         mSelectionMgr = selectionMgr;
68         mMenuManager = menuManager;
69         mMessages = messages;
70     }
71 
72     @Override
onSelectionChanged()73     public void onSelectionChanged() {
74         mSelectionMgr.copySelection(mSelected);
75         if (mSelected.size() > 0) {
76             if (mActionMode == null) {
77                 if (DEBUG) Log.d(TAG, "Starting action mode.");
78                 mActionMode = mActivity.startActionMode(this);
79             }
80             updateActionMenu();
81         } else {
82             if (mActionMode != null) {
83                 if (DEBUG) Log.d(TAG, "Finishing action mode.");
84                 mActionMode.finish();
85             }
86         }
87 
88         if (mActionMode != null) {
89             assert(!mSelected.isEmpty());
90             final String title = mMessages.getQuantityString(
91                     R.plurals.elements_selected, mSelected.size());
92             mActionMode.setTitle(title);
93             mActivity.getWindow().setTitle(title);
94         }
95     }
96 
97     @Override
onSelectionRestored()98     public void onSelectionRestored() {
99         onSelectionChanged();
100     }
101 
102     // Called when the user exits the action mode
103     @Override
onDestroyActionMode(ActionMode mode)104     public void onDestroyActionMode(ActionMode mode) {
105         if (mActionMode == null) {
106             if (DEBUG) Log.w(TAG, "Received call to destroy action mode on alien mode object.");
107         }
108 
109         assert(mActionMode.equals(mode));
110 
111         if (DEBUG) Log.d(TAG, "Handling action mode destroyed.");
112         mActionMode = null;
113         mMenu = null;
114 
115         mSelectionMgr.clearSelection();
116 
117         // Reset window title back to activity title, i.e. Root name
118         mActivity.getWindow().setTitle(mActivity.getTitle());
119 
120         // Re-enable TalkBack for the toolbars, as they are no longer covered by action mode.
121         mScope.accessibilityImportanceSetter.setAccessibilityImportance(
122                 View.IMPORTANT_FOR_ACCESSIBILITY_AUTO, R.id.toolbar, R.id.roots_toolbar);
123     }
124 
125     @Override
onCreateActionMode(ActionMode mode, Menu menu)126     public boolean onCreateActionMode(ActionMode mode, Menu menu) {
127         int size = mSelectionMgr.getSelection().size();
128         mode.getMenuInflater().inflate(R.menu.action_mode_menu, menu);
129         mode.setTitle(TextUtils.formatSelectedCount(size));
130 
131         if (size > 0) {
132 
133             // Hide the toolbars if action mode is enabled, so TalkBack doesn't navigate to
134             // these controls when using linear navigation.
135             mScope.accessibilityImportanceSetter.setAccessibilityImportance(
136                     View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS,
137                     R.id.toolbar,
138                     R.id.roots_toolbar);
139             return true;
140         }
141 
142         return false;
143     }
144 
145     @Override
onPrepareActionMode(ActionMode mode, Menu menu)146     public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
147         mMenu = menu;
148         updateActionMenu();
149         return true;
150     }
151 
updateActionMenu()152     private void updateActionMenu() {
153         assert(mMenu != null);
154         mMenuManager.updateActionMenu(mMenu, mScope.selectionDetails);
155         Menus.disableHiddenItems(mMenu);
156     }
157 
158     @Override
onActionItemClicked(ActionMode mode, MenuItem item)159     public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
160         return mScope.menuItemClicker.accept(item);
161     }
162 
setImportantForAccessibility( Activity activity, int accessibilityImportance, @IdRes int[] viewIds)163     private static void setImportantForAccessibility(
164             Activity activity, int accessibilityImportance, @IdRes int[] viewIds) {
165         for (final int id : viewIds) {
166             final View v = activity.findViewById(id);
167             if (v != null) {
168                 v.setImportantForAccessibility(accessibilityImportance);
169             }
170         }
171     }
172 
173     @FunctionalInterface
174     private interface AccessibilityImportanceSetter {
setAccessibilityImportance(int accessibilityImportance, @IdRes int... viewIds)175         void setAccessibilityImportance(int accessibilityImportance, @IdRes int... viewIds);
176     }
177 
178     @Override
finishActionMode()179     public void finishActionMode() {
180         if (mActionMode != null) {
181             mActionMode.finish();
182             mActionMode = null;
183         } else {
184             Log.w(TAG, "Tried to finish a null action mode.");
185         }
186     }
187 
188     @Override
finishOnConfirmed(@esult int code)189     public void finishOnConfirmed(@Result int code) {
190         if (code == ConfirmationCallback.CONFIRM) {
191             finishActionMode();
192         }
193     }
194 
reset( SelectionDetails selectionDetails, EventHandler<MenuItem> menuItemClicker)195     public ActionModeController reset(
196             SelectionDetails selectionDetails, EventHandler<MenuItem> menuItemClicker) {
197         assert(mActionMode == null);
198         assert(mMenu == null);
199 
200         mScope.menuItemClicker = menuItemClicker;
201         mScope.selectionDetails = selectionDetails;
202         mScope.accessibilityImportanceSetter =
203                 (int accessibilityImportance, @IdRes int[] viewIds) -> {
204                     setImportantForAccessibility(
205                             mActivity, accessibilityImportance, viewIds);
206                 };
207 
208         return this;
209     }
210 
211     private static final class ContentScope {
212         private EventHandler<MenuItem> menuItemClicker;
213         private SelectionDetails selectionDetails;
214         private AccessibilityImportanceSetter accessibilityImportanceSetter;
215     }
216 }
217