1 /*
2  * Copyright (C) 2015 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.app.Activity;
22 import android.util.Log;
23 import android.view.Gravity;
24 import android.view.View;
25 
26 import androidx.annotation.ColorRes;
27 import androidx.appcompat.widget.Toolbar;
28 import androidx.drawerlayout.widget.DrawerLayout;
29 import androidx.drawerlayout.widget.DrawerLayout.DrawerListener;
30 import androidx.legacy.app.ActionBarDrawerToggle;
31 
32 import com.android.documentsui.base.Display;
33 import com.android.documentsui.base.Providers;
34 
35 /**
36  * A facade over the various pieces comprising "roots fragment in a Drawer".
37  *
38  * @see DrawerController#create(DrawerLayout)
39  */
40 public abstract class DrawerController implements DrawerListener {
41     public static final String TAG = "DrawerController";
42 
update()43     public abstract void update();
setOpen(boolean open)44     public abstract void setOpen(boolean open);
setLocked(boolean locked)45     public abstract void setLocked(boolean locked);
isPresent()46     public abstract boolean isPresent();
isOpen()47     public abstract boolean isOpen();
setTitle(String title)48     abstract void setTitle(String title);
49 
50     /**
51      * Returns a controller suitable for {@code Layout}.
52      */
create(BaseActivity activity, ActivityConfig activityConfig)53     public static DrawerController create(BaseActivity activity, ActivityConfig activityConfig) {
54 
55         DrawerLayout layout = (DrawerLayout) activity.findViewById(R.id.drawer_layout);
56 
57         if (layout == null) {
58             return new DummyDrawerController();
59         }
60 
61         View drawer = activity.findViewById(R.id.drawer_roots);
62         Toolbar toolbar = (Toolbar) activity.findViewById(R.id.roots_toolbar);
63         drawer.getLayoutParams().width = calculateDrawerWidth(activity);
64 
65         ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
66                 activity,
67                 layout,
68                 R.drawable.ic_hamburger,
69                 R.string.drawer_open,
70                 R.string.drawer_close);
71 
72         return new RuntimeDrawerController(layout, drawer, toggle, toolbar, activityConfig,
73                 activity);
74     }
75 
76     /**
77      * Returns a controller suitable for {@code Layout}.
78      */
createDummy()79     static DrawerController createDummy() {
80         return new DummyDrawerController();
81     }
82 
calculateDrawerWidth(Activity activity)83     private static int calculateDrawerWidth(Activity activity) {
84         // Material design specification for navigation drawer:
85         // https://www.google.com/design/spec/patterns/navigation-drawer.html
86         float width = Display.screenWidth(activity) - Display.actionBarHeight(activity);
87         float maxWidth = activity.getResources().getDimension(R.dimen.max_drawer_width);
88         int finalWidth = (int) ((width > maxWidth ? maxWidth : width));
89 
90         if (DEBUG)
91             Log.d(TAG, "Calculated drawer width:" + (finalWidth / Display.density(activity)));
92 
93         return finalWidth;
94     }
95 
96     /**
97      * Runtime controller that manages a real drawer.
98      */
99     private static final class RuntimeDrawerController extends DrawerController
100             implements ItemDragListener.DragHost {
101         private static final int SPRING_TIMEOUT = 750;
102         private final ActionBarDrawerToggle mToggle;
103         private DrawerLayout mLayout;
104         private View mDrawer;
105         private Toolbar mToolbar;
106         private AbstractActionHandler.CommonAddons mCommonAddons;
107 
RuntimeDrawerController( DrawerLayout layout, View drawer, ActionBarDrawerToggle toggle, Toolbar drawerToolbar, ActivityConfig activityConfig, AbstractActionHandler.CommonAddons commonAddons)108         public RuntimeDrawerController(
109                 DrawerLayout layout,
110                 View drawer,
111                 ActionBarDrawerToggle toggle,
112                 Toolbar drawerToolbar,
113                 ActivityConfig activityConfig,
114                 AbstractActionHandler.CommonAddons commonAddons) {
115             mToolbar = drawerToolbar;
116             assert(layout != null);
117 
118             mLayout = layout;
119             mDrawer = drawer;
120             mToggle = toggle;
121             mCommonAddons = commonAddons;
122 
123             mLayout.setDrawerListener(this);
124 
125             if (activityConfig.dragAndDropEnabled()) {
126                 View edge = layout.findViewById(R.id.drawer_edge);
127                 edge.setOnDragListener(new ItemDragListener<>(this, SPRING_TIMEOUT));
128             }
129         }
130 
131         @Override
runOnUiThread(Runnable runnable)132         public void runOnUiThread(Runnable runnable) {
133             mDrawer.post(runnable);
134         }
135 
136         @Override
setDropTargetHighlight(View v, boolean highlight)137         public void setDropTargetHighlight(View v, boolean highlight) {
138             assert (v.getId() == R.id.drawer_edge);
139 
140             @ColorRes int id = highlight ? R.color.secondary :
141                 android.R.color.transparent;
142             v.setBackgroundColor(id);
143         }
144 
145         @Override
onDragEntered(View v)146         public void onDragEntered(View v) {
147             // do nothing; let drawer only open for onViewHovered
148         }
149 
150         @Override
onDragExited(View v)151         public void onDragExited(View v) {
152             // do nothing
153         }
154 
155         @Override
onViewHovered(View v)156         public void onViewHovered(View v) {
157             assert (v.getId() == R.id.drawer_edge);
158 
159             setOpen(true);
160         }
161 
162         @Override
onDragEnded()163         public void onDragEnded() {
164             // do nothing
165         }
166 
167         @Override
setOpen(boolean open)168         public void setOpen(boolean open) {
169             View list = mDrawer.findViewById(R.id.roots_list);
170             if (open) {
171                 mLayout.openDrawer(mDrawer);
172                 if (list != null) {
173                     mDrawer.requestFocus();
174                 }
175             } else {
176                 mLayout.closeDrawer(mDrawer);
177                 if (list != null) {
178                     mDrawer.clearFocus();
179                 }
180             }
181         }
182 
183         @Override
setLocked(boolean locked)184         public void setLocked(boolean locked) {
185             if (locked) {
186                 mLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
187             } else {
188                 mLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
189             }
190         }
191 
192         @Override
isOpen()193         public boolean isOpen() {
194             return mLayout.isDrawerOpen(mDrawer);
195         }
196 
197         @Override
isPresent()198         public boolean isPresent() {
199             return DrawerLayout.LOCK_MODE_UNLOCKED
200                     == mLayout.getDrawerLockMode(Gravity.START);
201         }
202 
203         @Override
setTitle(String title)204         void setTitle(String title) {
205             mToolbar.setTitle(title);
206         }
207 
208         @Override
update()209         public void update() {
210             mToggle.syncState();
211         }
212 
213         @Override
onDrawerSlide(View drawerView, float slideOffset)214         public void onDrawerSlide(View drawerView, float slideOffset) {
215             mToggle.onDrawerSlide(drawerView, slideOffset);
216         }
217 
218         @Override
onDrawerOpened(View drawerView)219         public void onDrawerOpened(View drawerView) {
220             mToggle.onDrawerOpened(drawerView);
221             // Update the information for Storage's root
222             DocumentsApplication.getProvidersCache(drawerView.getContext()).updateAuthorityAsync(
223                     mCommonAddons.getSelectedUser(), Providers.AUTHORITY_STORAGE);
224         }
225 
226         @Override
onDrawerClosed(View drawerView)227         public void onDrawerClosed(View drawerView) {
228             mToggle.onDrawerClosed(drawerView);
229         }
230 
231         @Override
onDrawerStateChanged(int newState)232         public void onDrawerStateChanged(int newState) {
233             mToggle.onDrawerStateChanged(newState);
234         }
235     }
236 
237     /*
238      * Dummy controller useful with clients that don't host a real drawer.
239      */
240     private static final class DummyDrawerController extends DrawerController {
241 
242         @Override
setOpen(boolean open)243         public void setOpen(boolean open) {}
244 
245         @Override
setLocked(boolean locked)246         public void setLocked(boolean locked) {}
247 
248         @Override
isOpen()249         public boolean isOpen() {
250             return false;
251         }
252 
253         @Override
isPresent()254         public boolean isPresent() {
255             return false;
256         }
257 
258         @Override
setTitle(String title)259         void setTitle(String title) {}
260 
261         @Override
update()262         public void update() {}
263 
264         @Override
onDrawerSlide(View drawerView, float slideOffset)265         public void onDrawerSlide(View drawerView, float slideOffset) {}
266 
267         @Override
onDrawerOpened(View drawerView)268         public void onDrawerOpened(View drawerView) {}
269 
270         @Override
onDrawerClosed(View drawerView)271         public void onDrawerClosed(View drawerView) {}
272 
273         @Override
onDrawerStateChanged(int newState)274         public void onDrawerStateChanged(int newState) {}
275     }
276 }
277