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;
18 
19 import android.content.Context;
20 import android.content.Intent;
21 import android.content.res.Configuration;
22 import android.content.res.Resources;
23 import android.os.Bundle;
24 import android.os.Handler;
25 import android.support.annotation.LayoutRes;
26 import android.support.car.Car;
27 import android.support.car.input.CarInputManager;
28 import android.util.AttributeSet;
29 import android.util.Log;
30 import android.view.LayoutInflater;
31 import android.view.Menu;
32 import android.view.MenuInflater;
33 import android.view.View;
34 import android.view.Window;
35 
36 /**
37  * Abstraction for car UI. Car applications should implement this for car UI.
38  * The API looks like {@link android.app.Activity}, and behaves like one, but it is neither
39  * android {@link android.app.Activity} nor {@link android.app.Context}.
40  * Applications should use {@link #getContext()} to get necessary {@link android.app.Context}.
41  */
42 public abstract class CarActivity {
43     private static final String TAG = "CarActivity";
44     public interface RequestPermissionsRequestCodeValidator {
validateRequestPermissionsRequestCode(int requestCode)45         public void validateRequestPermissionsRequestCode(int requestCode);
46     }
47     /**
48      * Interface to connect {@link CarActivity} to {@link android.app.Activity} or other app model.
49      * This interface provides utility for {@link CarActivity} to do things like manipulating view,
50      * handling menu, and etc.
51      */
52     public abstract static class Proxy {
setIntent(Intent i)53         abstract public void setIntent(Intent i);
setContentView(View view)54         abstract public void setContentView(View view);
setContentView(int layoutResID)55         abstract public void setContentView(int layoutResID);
getResources()56         abstract public Resources getResources();
findViewById(int id)57         abstract public View findViewById(int id);
getLayoutInflater()58         abstract public LayoutInflater getLayoutInflater();
getIntent()59         abstract public Intent getIntent();
finish()60         abstract public void finish();
getCarInputManager()61         abstract public CarInputManager getCarInputManager();
isFinishing()62         abstract public boolean isFinishing();
getMenuInflater()63         abstract public MenuInflater getMenuInflater();
finishAfterTransition()64         abstract public void finishAfterTransition();
getWindow()65         abstract public Window getWindow();
setResult(int resultCode)66         abstract public void setResult(int resultCode);
setResult(int resultCode, Intent data)67         abstract public void setResult(int resultCode, Intent data);
68 
requestPermissions(String[] permissions, int requestCode)69         public void requestPermissions(String[] permissions, int requestCode) {
70             Log.w(TAG, "No support for requestPermissions");
71         }
shouldShowRequestPermissionRationale(String permission)72         public boolean shouldShowRequestPermissionRationale(String permission) {
73             Log.w(TAG, "No support for shouldShowRequestPermissionRationale");
74             return false;
75         }
startActivityForResult(Intent intent, int requestCode)76         public void startActivityForResult(Intent intent, int requestCode) {
77             Log.w(TAG, "No support for startActivityForResult");
78         };
79     }
80 
81     /** @hide */
82     public static final int CMD_ON_CREATE = 0;
83     /** @hide */
84     public static final int CMD_ON_START = 1;
85     /** @hide */
86     public static final int CMD_ON_RESTART = 2;
87     /** @hide */
88     public static final int CMD_ON_RESUME = 3;
89     /** @hide */
90     public static final int CMD_ON_PAUSE = 4;
91     /** @hide */
92     public static final int CMD_ON_STOP = 5;
93     /** @hide */
94     public static final int CMD_ON_DESTROY = 6;
95     /** @hide */
96     public static final int CMD_ON_BACK_PRESSED = 7;
97     /** @hide */
98     public static final int CMD_ON_SAVE_INSTANCE_STATE = 8;
99     /** @hide */
100     public static final int CMD_ON_RESTORE_INSTANCE_STATE = 9;
101     /** @hide */
102     public static final int CMD_ON_CONFIG_CHANGED = 10;
103     /** @hide */
104     public static final int CMD_ON_REQUEST_PERMISSIONS_RESULT = 11;
105     /** @hide */
106     public static final int CMD_ON_NEW_INTENT = 12;
107     /** @hide */
108     public static final int CMD_ON_ACTIVITY_RESULT = 13;
109     /** @hide */
110     public static final int CMD_ON_POST_RESUME = 14;
111     /** @hide */
112     public static final int CMD_ON_LOW_MEMORY = 15;
113 
114     private final Proxy mProxy;
115     private final Context mContext;
116     private final Car mCar;
117     private final Handler mHandler = new Handler();
118 
CarActivity(Proxy proxy, Context context, Car car)119     public CarActivity(Proxy proxy, Context context, Car car) {
120         mProxy = proxy;
121         mContext = context;
122         mCar = car;
123     }
124 
getContext()125     public Context getContext() {
126         return mContext;
127     }
128 
getCar()129     public Car getCar() {
130         if (mCar == null) {
131             throw new IllegalStateException("The default Car is not available. You can either " +
132                     "create a Car by yourself or indicate the need of the default Car in the " +
133                     "CarProxyActivity's constructor.");
134         }
135         return mCar;
136     }
137 
getResources()138     public Resources getResources() {
139         return mProxy.getResources();
140     }
141 
setContentView(View view)142     public void setContentView(View view) {
143         mProxy.setContentView(view);
144     }
145 
setContentView(@ayoutRes int resourceId)146     public void setContentView(@LayoutRes int resourceId) {
147         mProxy.setContentView(resourceId);
148     }
149 
getLayoutInflater()150     public LayoutInflater getLayoutInflater() {
151         return mProxy.getLayoutInflater();
152     }
153 
getIntent()154     public Intent getIntent() {
155         return mProxy.getIntent();
156     }
157 
setResult(int resultCode)158     public void setResult(int resultCode){
159         mProxy.setResult(resultCode);
160     }
161 
setResult(int resultCode, Intent data)162     public void setResult(int resultCode, Intent data) {
163         mProxy.setResult(resultCode, data);
164     }
165 
getInputManager()166     public CarInputManager getInputManager() {
167         return mProxy.getCarInputManager();
168     }
169 
findViewById(int id)170     public View findViewById(int id) {
171         return mProxy.findViewById(id);
172     }
173 
finish()174     public void finish() {
175         mProxy.finish();
176     }
177 
isFinishing()178     public boolean isFinishing() {
179         return mProxy.isFinishing();
180     }
181 
shouldShowRequestPermissionRationale(String permission)182     public boolean shouldShowRequestPermissionRationale(String permission) {
183         return mProxy.shouldShowRequestPermissionRationale(permission);
184     }
185 
requestPermissions(String[] permissions, int requestCode)186     public void requestPermissions(String[] permissions, int requestCode) {
187         if (this instanceof RequestPermissionsRequestCodeValidator) {
188             ((RequestPermissionsRequestCodeValidator) this)
189                     .validateRequestPermissionsRequestCode(requestCode);
190         }
191         mProxy.requestPermissions(permissions, requestCode);
192     }
193 
setIntent(Intent i)194     public void setIntent(Intent i) {
195         mProxy.setIntent(i);
196     }
197 
finishAfterTransition()198     public void finishAfterTransition() {
199         mProxy.finishAfterTransition();
200     }
201 
getMenuInflater()202     public MenuInflater getMenuInflater() {
203         return mProxy.getMenuInflater();
204     }
205 
getWindow()206     public Window getWindow() {
207         return mProxy.getWindow();
208     }
209 
getLastNonConfigurationInstance()210     public Object getLastNonConfigurationInstance() {
211         return null;
212     }
213 
startActivityForResult(Intent intent, int requestCode)214     public void startActivityForResult(Intent intent, int requestCode) {
215         mProxy.startActivityForResult(intent, requestCode);
216     }
217 
runOnUiThread(Runnable runnable)218     public void runOnUiThread(Runnable runnable) {
219         if (Thread.currentThread() == mHandler.getLooper().getThread()) {
220             runnable.run();
221         } else {
222             mHandler.post(runnable);
223         }
224     }
225 
226     /** @hide */
dispatchCmd(int cmd, Object... args)227     public void dispatchCmd(int cmd, Object... args) {
228 
229         switch (cmd) {
230             case CMD_ON_CREATE:
231                 assertArgsLength(1, args);
232                 onCreate((Bundle) args[0]);
233                 break;
234             case CMD_ON_START:
235                 onStart();
236                 break;
237             case CMD_ON_RESTART:
238                 onRestart();
239                 break;
240             case CMD_ON_RESUME:
241                 onResume();
242                 break;
243             case CMD_ON_POST_RESUME:
244                 onPostResume();
245                 break;
246             case CMD_ON_PAUSE:
247                 onPause();
248                 break;
249             case CMD_ON_STOP:
250                 onStop();
251                 break;
252             case CMD_ON_DESTROY:
253                 onDestroy();
254                 break;
255             case CMD_ON_BACK_PRESSED:
256                 onBackPressed();
257                 break;
258             case CMD_ON_SAVE_INSTANCE_STATE:
259                 assertArgsLength(1, args);
260                 onSaveInstanceState((Bundle) args[0]);
261                 break;
262             case CMD_ON_RESTORE_INSTANCE_STATE:
263                 assertArgsLength(1, args);
264                 onRestoreInstanceState((Bundle) args[0]);
265                 break;
266             case CMD_ON_REQUEST_PERMISSIONS_RESULT:
267                 assertArgsLength(3, args);
268                 onRequestPermissionsResult(((Integer) args[0]).intValue(),
269                         (String[]) args[1], convertArray((Integer[]) args[2]));
270                 break;
271             case CMD_ON_CONFIG_CHANGED:
272                 assertArgsLength(1, args);
273                 onConfigurationChanged((Configuration) args[0]);
274                 break;
275             case CMD_ON_NEW_INTENT:
276                 assertArgsLength(1, args);
277                 onNewIntent((Intent) args[0]);
278                 break;
279             case CMD_ON_ACTIVITY_RESULT:
280                 assertArgsLength(3, args);
281                 onActivityResult(((Integer) args[0]).intValue(), ((Integer) args[1]).intValue(),
282                         (Intent) args[2]);
283                 break;
284             case CMD_ON_LOW_MEMORY:
285                 onLowMemory();
286                 break;
287             default:
288                 throw new RuntimeException("Unknown dispatch cmd for CarActivity, " + cmd);
289         }
290 
291     }
292 
onCreate(Bundle savedInstanceState)293     protected void onCreate(Bundle savedInstanceState) {
294     }
295 
onStart()296     protected void onStart() {
297     }
298 
onRestart()299     protected void onRestart() {
300     }
301 
onResume()302     protected void onResume() {
303     }
304 
onPostResume()305     protected void onPostResume() {
306     }
307 
onPause()308     protected void onPause() {
309     }
310 
onStop()311     protected void onStop() {
312     }
313 
onDestroy()314     protected void onDestroy() {
315     }
316 
onRestoreInstanceState(Bundle savedInstanceState)317     protected void onRestoreInstanceState(Bundle savedInstanceState) {
318     }
319 
onSaveInstanceState(Bundle outState)320     protected void onSaveInstanceState(Bundle outState) {
321     }
322 
onBackPressed()323     protected void onBackPressed() {
324     }
325 
onConfigurationChanged(Configuration newConfig)326     protected void onConfigurationChanged(Configuration newConfig) {
327     }
328 
onNewIntent(Intent intent)329     protected void onNewIntent(Intent intent) {
330     }
331 
onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)332     public void onRequestPermissionsResult(int requestCode, String[] permissions,
333             int[] grantResults) {
334     }
335 
onActivityResult(int requestCode, int resultCode, Intent data)336     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
337     }
338 
onRetainNonConfigurationInstance()339     public Object onRetainNonConfigurationInstance() {
340         return null;
341     }
342 
343     // TODO: hook up panel menu if it's needed in any apps.
onCreatePanelMenu(int featureId, Menu menu)344     public boolean onCreatePanelMenu(int featureId, Menu menu) {
345         return false; // default menu will not be displayed.
346     }
347 
onCreateView(View parent, String name, Context context, AttributeSet attrs)348     public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
349         // CarFragmentActivity can override this to dispatch onCreateView to fragments
350         return null;
351     }
352 
onLowMemory()353     public void onLowMemory() {
354     }
355 
assertArgsLength(int length, Object... args)356     private void assertArgsLength(int length, Object... args) {
357         if (args == null || args.length != length) {
358             throw new IllegalArgumentException(
359                     String.format("Wrong number of parameters. Expected: %d Actual: %d",
360                             length, args == null ? 0 : args.length));
361         }
362     }
363 
convertArray(Integer[] array)364     private static int[] convertArray(Integer[] array) {
365         int[] results = new int[array.length];
366         for(int i = 0; i < results.length; i++) {
367             results[i] = array[i].intValue();
368         }
369         return results;
370     }
371 }
372