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