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