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; 18 19import android.app.Activity; 20import android.content.Context; 21import android.content.Intent; 22import android.content.res.Configuration; 23import android.content.res.Resources; 24import android.os.Bundle; 25import android.os.Handler; 26import android.support.annotation.LayoutRes; 27import android.support.car.Car; 28import android.support.car.app.menu.CarDrawerActivity; 29import android.support.car.input.CarInputManager; 30import android.util.AttributeSet; 31import android.util.Log; 32import android.view.LayoutInflater; 33import android.view.Menu; 34import android.view.MenuInflater; 35import android.view.View; 36import android.view.Window; 37 38/** 39 * A car specific activity class. It allows an application to run on both "projected" and 40 * "native" platforms. For the phone-only mode we only support media and messaging apps at this 41 * time. Please see our guides for writing 42 * <a href="https://developer.android.com/training/auto/index.html#media">media</a> and 43 * <a href="https://developer.android.com/training/auto/index.html#messaging">messaging</a> apps. 44 * <ul> 45 * <li> 46 * For "native" systems you'll additionally need to implement a 47 * {@link CarProxyActivity} and add it to your application's manifest (see 48 * {@link CarProxyActivity}) for details). 49 * </li> 50 * <li> 51 * For "projected" systems you'll need to implement a 52 * {@link com.google.android.apps.auto.sdk.activity.CarProxyProjectionActivityService} 53 * </li> 54 * </ul> 55 * 56 * Applications wishing to write Android Auto applications will need to extend this class or one of 57 * it's sub classes. You'll most likely want to use {@link CarFragmentActivity} or 58 * {@link CarDrawerActivity} instead or this class as this one does not support fragments. 59 * <p/> 60 * This class has the look and feel of {@link Activity} however, it does not extend {@link Activity} 61 * or {@link Context}. Applications should use {@link #getContext()} to access the {@link Context}. 62 * 63 * @hide 64 */ 65public abstract class CarActivity { 66 private static final String TAG = "CarActivity"; 67 public interface RequestPermissionsRequestCodeValidator { 68 public void validateRequestPermissionsRequestCode(int requestCode); 69 } 70 /** 71 * Interface to connect {@link CarActivity} to {@link android.app.Activity} or other app model. 72 * This interface provides utility for {@link CarActivity} to do things like manipulating view, 73 * handling menu, and etc. 74 */ 75 public abstract static class Proxy { 76 abstract public void setIntent(Intent i); 77 abstract public void setContentView(View view); 78 abstract public void setContentView(int layoutResID); 79 abstract public Resources getResources(); 80 abstract public View findViewById(int id); 81 abstract public LayoutInflater getLayoutInflater(); 82 abstract public Intent getIntent(); 83 abstract public void finish(); 84 abstract public CarInputManager getCarInputManager(); 85 abstract public boolean isFinishing(); 86 abstract public MenuInflater getMenuInflater(); 87 abstract public void finishAfterTransition(); 88 abstract public Window getWindow(); 89 abstract public void setResult(int resultCode); 90 abstract public void setResult(int resultCode, Intent data); 91 92 public void requestPermissions(String[] permissions, int requestCode) { 93 Log.w(TAG, "No support for requestPermissions"); 94 } 95 public boolean shouldShowRequestPermissionRationale(String permission) { 96 Log.w(TAG, "No support for shouldShowRequestPermissionRationale"); 97 return false; 98 } 99 public void startActivityForResult(Intent intent, int requestCode) { 100 Log.w(TAG, "No support for startActivityForResult"); 101 }; 102 } 103 104 /** @hide */ 105 public static final int CMD_ON_CREATE = 0; 106 /** @hide */ 107 public static final int CMD_ON_START = 1; 108 /** @hide */ 109 public static final int CMD_ON_RESTART = 2; 110 /** @hide */ 111 public static final int CMD_ON_RESUME = 3; 112 /** @hide */ 113 public static final int CMD_ON_PAUSE = 4; 114 /** @hide */ 115 public static final int CMD_ON_STOP = 5; 116 /** @hide */ 117 public static final int CMD_ON_DESTROY = 6; 118 /** @hide */ 119 public static final int CMD_ON_BACK_PRESSED = 7; 120 /** @hide */ 121 public static final int CMD_ON_SAVE_INSTANCE_STATE = 8; 122 /** @hide */ 123 public static final int CMD_ON_RESTORE_INSTANCE_STATE = 9; 124 /** @hide */ 125 public static final int CMD_ON_CONFIG_CHANGED = 10; 126 /** @hide */ 127 public static final int CMD_ON_REQUEST_PERMISSIONS_RESULT = 11; 128 /** @hide */ 129 public static final int CMD_ON_NEW_INTENT = 12; 130 /** @hide */ 131 public static final int CMD_ON_ACTIVITY_RESULT = 13; 132 /** @hide */ 133 public static final int CMD_ON_POST_RESUME = 14; 134 /** @hide */ 135 public static final int CMD_ON_LOW_MEMORY = 15; 136 137 private final Proxy mProxy; 138 private final Context mContext; 139 private final Car mCar; 140 private final Handler mHandler = new Handler(); 141 142 public CarActivity(Proxy proxy, Context context, Car car) { 143 mProxy = proxy; 144 mContext = context; 145 mCar = car; 146 } 147 148 /** 149 * Returns a standard app {@link Context} object since this class does not extend {@link 150 * Context} link {@link Activity} does. 151 */ 152 public Context getContext() { 153 return mContext; 154 } 155 156 /** 157 * Returns an instance of the {@link Car} object. This is the main entry point to interact 158 * with the car and it's data. 159 * 160 * <p/> 161 * Note: For "native" platform uses cases you'll need to construct the CarProxy activity with 162 * the createCar boolean set to true if you want to use the getCar() method. 163 * <pre> 164 * {@code 165 * 166 * class FooProxyActivity extends CarProxyActivity { 167 * public FooProxyActivity() { 168 * super(FooActivity.class, true); 169 * } 170 * } 171 * } 172 * </pre> 173 * 174 * "Projected" use cases will create a Car instance by default. 175 * 176 * @throws IllegalStateException if the Car object is not available. 177 */ 178 public Car getCar() { 179 if (mCar == null) { 180 throw new IllegalStateException("The default Car is not available. You can either " + 181 "create a Car by yourself or indicate the need of the default Car in the " + 182 "CarProxyActivity's constructor."); 183 } 184 return mCar; 185 } 186 187 /** 188 * Returns the input manager for car activities. 189 */ 190 public CarInputManager getInputManager() { 191 return mProxy.getCarInputManager(); 192 } 193 194 /** 195 * See {@link Activity#getResources()}. 196 */ 197 public Resources getResources() { 198 return mProxy.getResources(); 199 } 200 201 /** 202 * See {@link Activity#setContentView(View)}. 203 */ 204 public void setContentView(View view) { 205 mProxy.setContentView(view); 206 } 207 208 /** 209 * See {@link Activity#setContentView(int)}. 210 */ 211 public void setContentView(@LayoutRes int resourceId) { 212 mProxy.setContentView(resourceId); 213 } 214 215 /** 216 * See {@link Activity#getLayoutInflater()}. 217 */ 218 public LayoutInflater getLayoutInflater() { 219 return mProxy.getLayoutInflater(); 220 } 221 222 /** 223 * See {@link Activity#getIntent()} 224 */ 225 public Intent getIntent() { 226 return mProxy.getIntent(); 227 } 228 229 /** 230 * See {@link Activity#setResult(int)} } 231 */ 232 public void setResult(int resultCode){ 233 mProxy.setResult(resultCode); 234 } 235 236 237 /** 238 * See {@link Activity#setResult(int, Intent)} } 239 */ 240 public void setResult(int resultCode, Intent data) { 241 mProxy.setResult(resultCode, data); 242 } 243 244 /** 245 * See {@link Activity#findViewById(int)} } 246 */ 247 public View findViewById(int id) { 248 return mProxy.findViewById(id); 249 } 250 251 /** 252 * See {@link Activity#finish()} 253 */ 254 public void finish() { 255 mProxy.finish(); 256 } 257 258 /** 259 * See {@link Activity#isFinishing()} 260 */ 261 public boolean isFinishing() { 262 return mProxy.isFinishing(); 263 } 264 265 /** 266 * See {@link Activity#shouldShowRequestPermissionRationale(String)} 267 */ 268 public boolean shouldShowRequestPermissionRationale(String permission) { 269 return mProxy.shouldShowRequestPermissionRationale(permission); 270 } 271 272 /** 273 * See {@link Activity#requestPermissions(String[], int)} 274 */ 275 public void requestPermissions(String[] permissions, int requestCode) { 276 if (this instanceof RequestPermissionsRequestCodeValidator) { 277 ((RequestPermissionsRequestCodeValidator) this) 278 .validateRequestPermissionsRequestCode(requestCode); 279 } 280 mProxy.requestPermissions(permissions, requestCode); 281 } 282 283 /** 284 * See {@link Activity#setIntent(Intent)} 285 */ 286 public void setIntent(Intent i) { 287 mProxy.setIntent(i); 288 } 289 290 /** 291 * See {@link Activity#finishAfterTransition()} 292 */ 293 public void finishAfterTransition() { 294 mProxy.finishAfterTransition(); 295 } 296 297 /** 298 * See {@link Activity#getMenuInflater()} 299 */ 300 public MenuInflater getMenuInflater() { 301 return mProxy.getMenuInflater(); 302 } 303 304 /** 305 * See {@link Activity#getWindow()} 306 */ 307 public Window getWindow() { 308 return mProxy.getWindow(); 309 } 310 311 /** 312 * See {@link Activity#getLastNonConfigurationInstance()} 313 */ 314 public Object getLastNonConfigurationInstance() { 315 return null; 316 } 317 318 /** 319 * See {@link Activity#startActivityForResult(Intent, int)} 320 */ 321 public void startActivityForResult(Intent intent, int requestCode) { 322 mProxy.startActivityForResult(intent, requestCode); 323 } 324 325 /** 326 * See {@link Activity#runOnUiThread(Runnable)} 327 */ 328 public void runOnUiThread(Runnable runnable) { 329 if (Thread.currentThread() == mHandler.getLooper().getThread()) { 330 runnable.run(); 331 } else { 332 mHandler.post(runnable); 333 } 334 } 335 336 /** @hide */ 337 public void dispatchCmd(int cmd, Object... args) { 338 339 switch (cmd) { 340 case CMD_ON_CREATE: 341 assertArgsLength(1, args); 342 onCreate((Bundle) args[0]); 343 break; 344 case CMD_ON_START: 345 onStart(); 346 break; 347 case CMD_ON_RESTART: 348 onRestart(); 349 break; 350 case CMD_ON_RESUME: 351 onResume(); 352 break; 353 case CMD_ON_POST_RESUME: 354 onPostResume(); 355 break; 356 case CMD_ON_PAUSE: 357 onPause(); 358 break; 359 case CMD_ON_STOP: 360 onStop(); 361 break; 362 case CMD_ON_DESTROY: 363 onDestroy(); 364 break; 365 case CMD_ON_BACK_PRESSED: 366 onBackPressed(); 367 break; 368 case CMD_ON_SAVE_INSTANCE_STATE: 369 assertArgsLength(1, args); 370 onSaveInstanceState((Bundle) args[0]); 371 break; 372 case CMD_ON_RESTORE_INSTANCE_STATE: 373 assertArgsLength(1, args); 374 onRestoreInstanceState((Bundle) args[0]); 375 break; 376 case CMD_ON_REQUEST_PERMISSIONS_RESULT: 377 assertArgsLength(3, args); 378 onRequestPermissionsResult(((Integer) args[0]).intValue(), 379 (String[]) args[1], convertArray((Integer[]) args[2])); 380 break; 381 case CMD_ON_CONFIG_CHANGED: 382 assertArgsLength(1, args); 383 onConfigurationChanged((Configuration) args[0]); 384 break; 385 case CMD_ON_NEW_INTENT: 386 assertArgsLength(1, args); 387 onNewIntent((Intent) args[0]); 388 break; 389 case CMD_ON_ACTIVITY_RESULT: 390 assertArgsLength(3, args); 391 onActivityResult(((Integer) args[0]).intValue(), ((Integer) args[1]).intValue(), 392 (Intent) args[2]); 393 break; 394 case CMD_ON_LOW_MEMORY: 395 onLowMemory(); 396 break; 397 default: 398 throw new RuntimeException("Unknown dispatch cmd for CarActivity, " + cmd); 399 } 400 401 } 402 403 /** 404 * See {@link Activity#onCreate(Bundle)} 405 */ 406 protected void onCreate(Bundle savedInstanceState) { 407 } 408 409 /** 410 * See {@link Activity#onStart()} 411 */ 412 protected void onStart() { 413 } 414 415 /** 416 * See {@link Activity#onRestart()} 417 */ 418 protected void onRestart() { 419 } 420 421 /** 422 * See {@link Activity#onResume()} 423 */ 424 protected void onResume() { 425 } 426 427 /** 428 * See {@link Activity#onPostResume()} 429 */ 430 protected void onPostResume() { 431 } 432 433 /** 434 * See {@link Activity#onPause()} 435 */ 436 protected void onPause() { 437 } 438 439 /** 440 * See {@link Activity#onStop()} 441 */ 442 protected void onStop() { 443 } 444 445 /** 446 * See {@link Activity#onDestroy()} 447 */ 448 protected void onDestroy() { 449 } 450 451 /** 452 * See {@link Activity#onRestoreInstanceState(Bundle)} 453 */ 454 protected void onRestoreInstanceState(Bundle savedInstanceState) { 455 } 456 457 /** 458 * See {@link Activity#onSaveInstanceState(Bundle)} 459 */ 460 protected void onSaveInstanceState(Bundle outState) { 461 } 462 463 /** 464 * See {@link Activity#onBackPressed()} 465 */ 466 protected void onBackPressed() { 467 } 468 469 /** 470 * See {@link Activity#onConfigurationChanged(Configuration)} 471 */ 472 protected void onConfigurationChanged(Configuration newConfig) { 473 } 474 475 /** 476 * See {@link Activity#onNewIntent(Intent)} 477 */ 478 protected void onNewIntent(Intent intent) { 479 } 480 481 /** 482 * See {@link Activity#onRequestPermissionsResult(int, String[], int[])} 483 */ 484 public void onRequestPermissionsResult(int requestCode, String[] permissions, 485 int[] grantResults) { 486 } 487 488 /** 489 * See {@link Activity#onActivityResult(int, int, Intent)} 490 */ 491 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 492 } 493 494 /** 495 * See {@link Activity#onRetainNonConfigurationInstance()} 496 */ 497 public Object onRetainNonConfigurationInstance() { 498 return null; 499 } 500 501 // TODO: hook up panel menu if it's needed in any apps. 502 /** 503 * Currently always returns false. 504 * See {@link Activity#onCreatePanelMenu(int, Menu)} 505 */ 506 public boolean onCreatePanelMenu(int featureId, Menu menu) { 507 return false; // default menu will not be displayed. 508 } 509 510 /** 511 * See {@link Activity#onCreateView(View, String, Context, AttributeSet)} 512 */ 513 public View onCreateView(View parent, String name, Context context, AttributeSet attrs) { 514 // CarFragmentActivity can override this to dispatch onCreateView to fragments 515 return null; 516 } 517 518 /** 519 * See {@link Activity#onLowMemory()} 520 */ 521 public void onLowMemory() { 522 } 523 524 private void assertArgsLength(int length, Object... args) { 525 if (args == null || args.length != length) { 526 throw new IllegalArgumentException( 527 String.format("Wrong number of parameters. Expected: %d Actual: %d", 528 length, args == null ? 0 : args.length)); 529 } 530 } 531 532 private static int[] convertArray(Integer[] array) { 533 int[] results = new int[array.length]; 534 for(int i = 0; i < results.length; i++) { 535 results[i] = array[i].intValue(); 536 } 537 return results; 538 } 539} 540