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.Shared.DEBUG;
20 
21 import android.annotation.IntDef;
22 import android.app.Activity;
23 import android.content.Context;
24 import android.support.v4.app.ActionBarDrawerToggle;
25 import android.support.v4.widget.DrawerLayout;
26 import android.support.v4.widget.DrawerLayout.DrawerListener;
27 import android.util.Log;
28 import android.view.View;
29 import android.widget.Toolbar;
30 
31 import java.lang.annotation.Retention;
32 import java.lang.annotation.RetentionPolicy;
33 
34 /**
35  * A facade over the various pieces comprising "roots fragment in a Drawer".
36  *
37  * @see DrawerController#create(DrawerLayout)
38  */
39 abstract class DrawerController implements DrawerListener {
40     public static final String TAG = "DrawerController";
41 
42     // Drawer opening triggered by tapping the navigation icon
43     public static final int OPENED_HAMBURGER = 0;
44     // Drawer opening triggered by swiping right from the edge of the screen
45     public static final int OPENED_SWIPE = 1;
46     // Mostly programmatically forced drawer opening
47     public static final int OPENED_OTHER = 2;
48 
49     @IntDef(flag = true, value = {
50             OPENED_HAMBURGER,
51             OPENED_SWIPE,
52             OPENED_OTHER
53     })
54     @Retention(RetentionPolicy.SOURCE)
55     public @interface Trigger {}
56 
57     /**
58      * Toggles the drawer and sets the OPENED_OTHER as the action that causes opening the drawer.
59      * @param open
60      */
setOpen(boolean open)61     abstract void setOpen(boolean open);
62 
63     /**
64      * Toggles the drawer.
65      * @param open
66      * @param trigger Indicates what action caused opening the drawer. It is ignored for closing.
67      */
setOpen(boolean open, @Trigger int trigger)68     abstract void setOpen(boolean open, @Trigger int trigger);
isPresent()69     abstract boolean isPresent();
isOpen()70     abstract boolean isOpen();
setTitle(String title)71     abstract void setTitle(String title);
update()72     abstract void update();
73 
74     /**
75      * Returns a controller suitable for {@code Layout}.
76      */
create(Activity activity)77     static DrawerController create(Activity activity) {
78 
79         DrawerLayout layout = (DrawerLayout) activity.findViewById(R.id.drawer_layout);
80 
81         if (layout == null) {
82             return new DummyDrawerController();
83         }
84 
85         View drawer = activity.findViewById(R.id.drawer_roots);
86         Toolbar toolbar = (Toolbar) activity.findViewById(R.id.roots_toolbar);
87 
88         drawer.getLayoutParams().width = calculateDrawerWidth(activity);
89 
90         ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
91                 activity,
92                 layout,
93                 R.drawable.ic_hamburger,
94                 R.string.drawer_open,
95                 R.string.drawer_close);
96 
97         return new RuntimeDrawerController(layout, drawer, toggle, toolbar);
98     }
99 
100     /**
101      * Returns a controller suitable for {@code Layout}.
102      */
createDummy()103     static DrawerController createDummy() {
104         return new DummyDrawerController();
105     }
106 
calculateDrawerWidth(Activity activity)107     private static int calculateDrawerWidth(Activity activity) {
108         // Material design specification for navigation drawer:
109         // https://www.google.com/design/spec/patterns/navigation-drawer.html
110         float width = Display.screenWidth(activity) - Display.actionBarHeight(activity);
111         float maxWidth = activity.getResources().getDimension(R.dimen.max_drawer_width);
112         int finalWidth = (int) ((width > maxWidth ? maxWidth : width));
113 
114         if (DEBUG)
115             Log.d(TAG, "Calculated drawer width:" + (finalWidth / Display.density(activity)));
116 
117         return finalWidth;
118     }
119 
120     /**
121      * Runtime controller that manages a real drawer.
122      */
123     private static final class RuntimeDrawerController extends DrawerController {
124         private final ActionBarDrawerToggle mToggle;
125         private DrawerLayout mLayout;
126         private View mDrawer;
127         private Toolbar mToolbar;
128         private @Trigger int mTrigger = OPENED_OTHER;
129 
RuntimeDrawerController( DrawerLayout layout, View drawer, ActionBarDrawerToggle toggle, Toolbar drawerToolbar)130         public RuntimeDrawerController(
131                 DrawerLayout layout, View drawer, ActionBarDrawerToggle toggle,
132                 Toolbar drawerToolbar) {
133             mToolbar = drawerToolbar;
134             assert(layout != null);
135 
136             mLayout = layout;
137             mDrawer = drawer;
138             mToggle = toggle;
139 
140             mLayout.setDrawerListener(this);
141         }
142 
143         @Override
setOpen(boolean open)144         void setOpen(boolean open) {
145             setOpen(open, OPENED_OTHER);
146         }
147 
148         @Override
setOpen(boolean open, @Trigger int trigger)149         void setOpen(boolean open, @Trigger int trigger) {
150             if (open) {
151                 mLayout.openDrawer(mDrawer);
152                 mTrigger = trigger;
153             } else {
154                 mLayout.closeDrawer(mDrawer);
155             }
156         }
157 
158         @Override
isOpen()159         boolean isOpen() {
160             return mLayout.isDrawerOpen(mDrawer);
161         }
162 
163         @Override
isPresent()164         boolean isPresent() {
165             return true;
166         }
167 
168         @Override
setTitle(String title)169         void setTitle(String title) {
170             mToolbar.setTitle(title);
171         }
172 
173         @Override
update()174         void update() {
175             mToggle.syncState();
176         }
177 
178         @Override
onDrawerSlide(View drawerView, float slideOffset)179         public void onDrawerSlide(View drawerView, float slideOffset) {
180             mToggle.onDrawerSlide(drawerView, slideOffset);
181         }
182 
183         @Override
onDrawerOpened(View drawerView)184         public void onDrawerOpened(View drawerView) {
185             mToggle.onDrawerOpened(drawerView);
186             Metrics.logDrawerOpened(mToolbar.getContext(), mTrigger);
187         }
188 
189         @Override
onDrawerClosed(View drawerView)190         public void onDrawerClosed(View drawerView) {
191             mToggle.onDrawerClosed(drawerView);
192             mTrigger = OPENED_OTHER;
193         }
194 
195         @Override
onDrawerStateChanged(int newState)196         public void onDrawerStateChanged(int newState) {
197             mToggle.onDrawerStateChanged(newState);
198             if (newState == DrawerLayout.STATE_DRAGGING) {
199                 mTrigger = OPENED_SWIPE;
200             }
201         }
202     }
203 
204     /*
205      * Dummy controller useful with clients that don't host a real drawer.
206      */
207     private static final class DummyDrawerController extends DrawerController {
208 
209         @Override
setOpen(boolean open)210         void setOpen(boolean open) {}
211 
212         @Override
setOpen(boolean open, @Trigger int trigger)213         void setOpen(boolean open, @Trigger int trigger) {}
214 
215         @Override
isOpen()216         boolean isOpen() {
217             return false;
218         }
219 
220         @Override
isPresent()221         boolean isPresent() {
222             return false;
223         }
224 
225         @Override
setTitle(String title)226         void setTitle(String title) {}
227 
228         @Override
update()229         void update() {}
230 
231         @Override
onDrawerSlide(View drawerView, float slideOffset)232         public void onDrawerSlide(View drawerView, float slideOffset) {}
233 
234         @Override
onDrawerOpened(View drawerView)235         public void onDrawerOpened(View drawerView) {}
236 
237         @Override
onDrawerClosed(View drawerView)238         public void onDrawerClosed(View drawerView) {}
239 
240         @Override
onDrawerStateChanged(int newState)241         public void onDrawerStateChanged(int newState) {}
242     }
243 }
244