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