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