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