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 android.support.car.app.menu; 18 19 import android.content.Context; 20 import android.graphics.Bitmap; 21 import android.graphics.drawable.Drawable; 22 import android.os.Bundle; 23 import android.os.Handler; 24 import android.support.annotation.LayoutRes; 25 import android.support.car.Car; 26 import android.support.car.app.CarFragmentActivity; 27 import android.support.car.app.menu.compat.CarMenuConstantsComapt.MenuItemConstants; 28 import android.support.car.input.CarInputManager; 29 import android.support.v4.app.Fragment; 30 import android.util.DisplayMetrics; 31 import android.view.LayoutInflater; 32 import android.view.View; 33 import android.view.ViewGroup; 34 import android.view.WindowManager; 35 import android.widget.EditText; 36 37 /** 38 * Base class for a car app which wants to use a drawer. 39 */ 40 public abstract class CarDrawerActivity extends CarFragmentActivity { 41 private static final String TAG = "CarDrawerActivity"; 42 43 private static final String KEY_DRAWERSHOWING = 44 "android.support.car.app.CarDrawerActivity.DRAWER_SHOWING"; 45 private static final String KEY_INPUTSHOWING = 46 "android.support.car.app.CarDrawerActivity.INPUT_SHOWING"; 47 private static final String KEY_SEARCHBOXENABLED = 48 "android.support.car.app.CarDrawerActivity.SEARCH_BOX_ENABLED"; 49 50 private final Handler mHandler = new Handler(); 51 private final CarUiController mUiController; 52 53 private CarMenuCallbacks mMenuCallbacks; 54 private OnMenuClickListener mMenuClickListener; 55 private boolean mDrawerShowing; 56 private boolean mShowingSearchBox; 57 private boolean mSearchBoxEnabled; 58 private boolean mOnCreateCalled = false; 59 private View.OnClickListener mSearchBoxOnClickListener; 60 61 private CarInputManager mInputManager; 62 private EditText mSearchBoxView; 63 64 public interface OnMenuClickListener { 65 /** 66 * Called when the menu button is clicked. 67 * 68 * @return True if event was handled. This will prevent the drawer from executing its 69 * default action (opening/closing/going back). False if the event was not handled 70 * so the drawer will execute the default action. 71 */ 72 boolean onClicked(); 73 } 74 75 public CarDrawerActivity(Proxy proxy, Context context, Car car) { 76 super(proxy, context, car); 77 mUiController = createCarUiController(); 78 } 79 80 /** 81 * Create a {@link android.support.car.app.menu.CarUiController}. 82 * 83 * Derived class can override this function to return a customized ui controller. 84 */ 85 protected CarUiController createCarUiController() { 86 return CarUiController.createCarUiController(this); 87 } 88 89 @Override 90 public void setContentView(View view) { 91 ViewGroup parent = (ViewGroup) findViewById(mUiController.getFragmentContainerId()); 92 parent.addView(view); 93 } 94 95 @Override 96 public void setContentView(@LayoutRes int resourceId) { 97 ViewGroup parent = (ViewGroup) findViewById(mUiController.getFragmentContainerId()); 98 LayoutInflater inflater = getLayoutInflater(); 99 inflater.inflate(resourceId, parent, true); 100 } 101 102 @Override 103 public View findViewById(@LayoutRes int id) { 104 return super.findViewById(mUiController.getFragmentContainerId()).findViewById(id); 105 } 106 107 @Override 108 protected void onCreate(Bundle savedInstanceState) { 109 super.onCreate(savedInstanceState); 110 super.setContentView(mUiController.getContentView()); 111 mInputManager = getInputManager(); 112 mHandler.post(new Runnable() { 113 @Override 114 public void run() { 115 if (mMenuCallbacks != null) { 116 mMenuCallbacks.registerOnChildrenChangedListener(mMenuListener); 117 } 118 mOnCreateCalled = true; 119 } 120 }); 121 } 122 123 @Override 124 protected void onDestroy() { 125 super.onDestroy(); 126 mHandler.post(new Runnable() { 127 @Override 128 public void run() { 129 if (mMenuCallbacks != null) { 130 mMenuCallbacks.unregisterOnChildrenChangedListener(mMenuListener); 131 mMenuCallbacks = null; 132 } 133 } 134 }); 135 } 136 137 @Override 138 protected void onRestoreInstanceState(Bundle savedInstanceState) { 139 super.onRestoreInstanceState(savedInstanceState); 140 mDrawerShowing = savedInstanceState.getBoolean(KEY_DRAWERSHOWING); 141 mUiController.onRestoreInstanceState(savedInstanceState); 142 } 143 144 @Override 145 protected void onSaveInstanceState(Bundle outState) { 146 super.onSaveInstanceState(outState); 147 outState.putBoolean(KEY_DRAWERSHOWING, mDrawerShowing); 148 mUiController.onSaveInstanceState(outState); 149 } 150 151 @Override 152 protected void onStart() { 153 super.onStart(); 154 getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN); 155 mUiController.onStart(); 156 } 157 158 @Override 159 protected void onResume() { 160 super.onResume(); 161 mUiController.onResume(); 162 } 163 164 @Override 165 protected void onPause() { 166 super.onPause(); 167 mUiController.onPause(); 168 } 169 170 @Override 171 protected void onStop() { 172 super.onStop(); 173 mUiController.onStop(); 174 } 175 176 /** 177 * Set the fragment in the main fragment container. 178 */ 179 public void setContentFragment(Fragment fragment) { 180 super.setContentFragment(fragment, mUiController.getFragmentContainerId()); 181 } 182 183 /** 184 * Return the main fragment container id for the app. 185 */ 186 public int getFragmentContainerId() { 187 return mUiController.getFragmentContainerId(); 188 } 189 190 /** 191 * Set the callbacks for car menu interactions. 192 */ 193 public void setCarMenuCallbacks(final CarMenuCallbacks callbacks) { 194 if (mOnCreateCalled) { 195 throw new IllegalStateException( 196 "Cannot call setCarMenuCallbacks after onCreate has been called."); 197 } 198 mMenuCallbacks = callbacks; 199 mUiController.registerCarMenuCallbacks(callbacks); 200 } 201 202 /** 203 * Listener that listens for when the menu button is pressed. 204 * 205 * @param listener {@link OnMenuClickListener} that will listen for menu button clicks. 206 */ 207 public void setOnMenuClickedListener(OnMenuClickListener listener) { 208 mMenuClickListener = listener; 209 } 210 211 /** 212 * Restore the menu button drawable 213 */ 214 public void restoreMenuButtonDrawable() { 215 mUiController.restoreMenuButtonDrawable(); 216 } 217 218 /** 219 * Sets the menu button bitmap 220 * 221 * @param bitmap Bitmap to the menu button to. 222 */ 223 public void setMenuButtonBitmap(Bitmap bitmap) { 224 mUiController.setMenuButtonBitmap(bitmap); 225 } 226 227 /** 228 * Set the title of the menu. 229 */ 230 public void setTitle(CharSequence title) { 231 mUiController.setTitle(title); 232 } 233 234 /** 235 * Set the System UI to be light. 236 */ 237 public void setLightMode() { 238 mUiController.setLightMode(); 239 } 240 241 /** 242 * Set the System UI to be dark. 243 */ 244 public void setDarkMode() { 245 mUiController.setDarkMode(); 246 } 247 248 /** 249 * Set the System UI to be dark during day mode and light during night mode. 250 */ 251 public void setAutoLightDarkMode() { 252 mUiController.setAutoLightDarkMode(); 253 } 254 255 /** 256 * Sets the application background to the given {@link android.graphics.Bitmap}. 257 * 258 * @param bitmap to use as background. 259 */ 260 public void setBackground(Bitmap bitmap) { 261 mUiController.setBackground(bitmap); 262 } 263 264 /** 265 * Sets the color of the scrim to the right of the car menu drawer. 266 */ 267 public void setScrimColor(int color) { 268 mUiController.setScrimColor(color); 269 } 270 271 /** 272 * Show the menu associated with the given id in the drawer. 273 * 274 * @param id Id of the menu to link to. 275 * @param title Title that should be displayed. 276 */ 277 public void showMenu(String id, String title) { 278 mUiController.showMenu(id, title); 279 } 280 281 public boolean onMenuClicked() { 282 if (mMenuClickListener != null) { 283 return mMenuClickListener.onClicked(); 284 } 285 return false; 286 } 287 288 public void restoreSearchBox() { 289 if (isSearchBoxEnabled()) { 290 mUiController.showSearchBox(mSearchBoxOnClickListener); 291 mShowingSearchBox = true; 292 } 293 } 294 295 private final CarMenuCallbacks.OnChildrenChangedListener mMenuListener = 296 new CarMenuCallbacks.OnChildrenChangedListener() { 297 @Override 298 public void onChildrenChanged(String parentId) { 299 if (mOnCreateCalled) { 300 mUiController.onChildrenChanged(parentId); 301 } 302 } 303 304 @Override 305 public void onChildChanged(String parentId, Bundle item, 306 Drawable leftIcon, Drawable rightIcon) { 307 DisplayMetrics metrics = getResources().getDisplayMetrics(); 308 if (leftIcon != null) { 309 item.putParcelable(MenuItemConstants.KEY_LEFTICON, 310 Utils.snapshot(metrics, leftIcon)); 311 } 312 313 if (rightIcon != null) { 314 item.putParcelable(MenuItemConstants.KEY_RIGHTICON, 315 Utils.snapshot(metrics, rightIcon)); 316 } 317 if (mOnCreateCalled) { 318 mUiController.onChildChanged(parentId, item); 319 } 320 } 321 }; 322 323 public void closeDrawer() { 324 mUiController.closeDrawer(); 325 } 326 327 public void openDrawer() { 328 mUiController.openDrawer(); 329 } 330 331 public boolean isDrawerShowing() { 332 return mDrawerShowing; 333 } 334 335 public void setDrawerShowing(boolean showing) { 336 mDrawerShowing = showing; 337 } 338 339 public boolean isSearchBoxEnabled() { 340 return mSearchBoxEnabled; 341 } 342 343 public boolean isShowingSearchBox() { 344 return mShowingSearchBox; 345 } 346 347 /** 348 * Shows a small clickable {@link android.widget.EditText}. 349 * 350 * {@link View} will be {@code null} in {@link View.OnClickListener#onClick(View)}. 351 * 352 * @param listener {@link View.OnClickListener} that is called when user selects the 353 * {@link android.widget.EditText}. 354 */ 355 public void showSearchBox(View.OnClickListener listener) { 356 if (!isDrawerShowing()) { 357 mUiController.showSearchBox(listener); 358 mShowingSearchBox = true; 359 } 360 mSearchBoxEnabled = true; 361 mSearchBoxOnClickListener = listener; 362 } 363 364 public void showSearchBox() { 365 showSearchBox(mSearchBoxOnClickListener); 366 } 367 368 public void hideSearchBox() { 369 if (isShowingSearchBox()) { 370 stopInput(); 371 } 372 mSearchBoxEnabled = false; 373 } 374 375 public void setSearchBoxEditListener(SearchBoxEditListener listener) { 376 mUiController.setSearchBoxEditListener(listener); 377 } 378 379 public void stopInput() { 380 // STOPSHIP: sometimes focus is lost and we are not able to hide the keyboard. 381 // properly fix this before we ship. 382 if (mSearchBoxView != null) { 383 mSearchBoxView.requestFocusFromTouch(); 384 } 385 mUiController.stopInput(); 386 mInputManager.stopInput(); 387 mShowingSearchBox = false; 388 } 389 390 /** 391 * Start input on the search box that is provided by a car ui provider. 392 * TODO: Migrate to use the new input/search api once it becomes stable (b/27108311). 393 * @param hint Search hint 394 */ 395 public void startInput(String hint) { 396 startInput(hint, mSearchBoxOnClickListener); 397 } 398 399 /** 400 * Start input on the search box that is provided by a car ui provider. 401 * TODO: Migrate to use the new input/search api once it becomes stable (b/27108311). 402 * @param hint Search hint 403 * @param onClickListener Listener for the search box clicks. 404 */ 405 public void startInput(final String hint, final View.OnClickListener onClickListener) { 406 mInputManager = getInputManager(); 407 EditText inputView = mUiController.startInput(hint, onClickListener); 408 getInputManager().startInput(inputView); 409 mSearchBoxView = inputView; 410 mShowingSearchBox = true; 411 } 412 413 public void setSearchBoxColors(int backgroundColor, int searchLogoColor, int textColor, 414 int hintTextColor) { 415 mUiController.setSearchBoxColors(backgroundColor, searchLogoColor, 416 textColor, hintTextColor); 417 } 418 419 public void setSearchBoxEndView(View endView) { 420 mUiController.setSearchBoxEndView(endView); 421 } 422 423 public void showToast(String text, int duration) { 424 mUiController.showToast(text, duration); 425 } 426 } 427