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 StubDrawerController(); 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 */ createStub()79 static DrawerController createStub() { 80 return new StubDrawerController(); 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 * Stub controller useful with clients that don't host a real drawer. 239 */ 240 private static final class StubDrawerController 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