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.tv;
18 
19 import android.app.Activity;
20 import android.content.ActivityNotFoundException;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.os.Bundle;
24 import android.util.Log;
25 
26 /**
27  * An activity to launch a new activity.
28  *
29  * <p>In the case when {@link MainActivity} starts a new activity using
30  * {@link Activity#startActivity} or {@link Activity#startActivityForResult}, Live TV app is
31  * terminated if the new activity crashes. That's because the {@link android.app.ActivityManager}
32  * terminates the activity which is just below the crashed activity in the activity stack. To avoid
33  * this, we need to locate an additional activity between these activities in the activity stack.
34  */
35 public class LauncherActivity extends Activity {
36     private static final String TAG = "LauncherActivity";
37 
38     public static final String ERROR_MESSAGE
39             = "com.android.tv.LauncherActivity.ErrorMessage";
40 
41     private static final int REQUEST_CODE_DEFAULT = 0;
42     private static final int REQUEST_START_ACTIVITY = 100;
43 
44     private static final String EXTRA_INTENT = "com.android.tv.LauncherActivity.INTENT";
45     private static final String EXTRA_REQUEST_RESULT =
46             "com.android.tv.LauncherActivity.REQUEST_RESULT";
47 
48     /**
49      * Starts an activity by calling {@link Activity#startActivity}.
50      */
startActivitySafe(Activity baseActivity, Intent intentToLaunch)51     public static void startActivitySafe(Activity baseActivity, Intent intentToLaunch) {
52         // To avoid the app termination when the new activity crashes, LauncherActivity should be
53         // started by calling startActivityForResult().
54         baseActivity.startActivityForResult(createIntent(baseActivity, intentToLaunch, false),
55                 REQUEST_CODE_DEFAULT);
56     }
57 
58     /**
59      * Starts an activity by calling {@link Activity#startActivityForResult}.
60      *
61      * <p>Note: {@code requestCode} should not be 0. The value is reserved for internal use.
62      */
startActivityForResultSafe(Activity baseActivity, Intent intentToLaunch, int requestCode)63     public static void startActivityForResultSafe(Activity baseActivity, Intent intentToLaunch,
64             int requestCode) {
65         if (requestCode == REQUEST_CODE_DEFAULT) {
66             throw new IllegalArgumentException("requestCode should not be 0.");
67         }
68         // To avoid the app termination when the new activity crashes, LauncherActivity should be
69         // started by calling startActivityForResult().
70         baseActivity.startActivityForResult(createIntent(baseActivity, intentToLaunch, true),
71                 requestCode);
72     }
73 
createIntent(Context context, Intent intentToLaunch, boolean requestResult)74     private static Intent createIntent(Context context, Intent intentToLaunch,
75             boolean requestResult) {
76         Intent intent = new Intent(context, LauncherActivity.class);
77         intent.putExtra(EXTRA_INTENT, intentToLaunch);
78         if (requestResult) {
79             intent.putExtra(EXTRA_REQUEST_RESULT, true);
80         }
81         return intent;
82     }
83 
84     @Override
onCreate(Bundle savedInstanceState)85     public void onCreate(Bundle savedInstanceState) {
86         super.onCreate(savedInstanceState);
87         // We should launch the new activity in onCreate rather than in onStart.
88         // That's because it is not guaranteed that onStart is called only once.
89         Intent intent = getIntent().getParcelableExtra(EXTRA_INTENT);
90         boolean requestResult = getIntent().getBooleanExtra(EXTRA_REQUEST_RESULT, false);
91         try {
92             if (requestResult) {
93                 startActivityForResult(intent, REQUEST_START_ACTIVITY);
94             } else {
95                 startActivity(intent);
96                 setResult(Activity.RESULT_OK);
97                 finish();
98             }
99         } catch (ActivityNotFoundException e) {
100             Log.w(TAG, "Activity not found for " + intent);
101             intent.putExtra(ERROR_MESSAGE,
102                     getResources().getString(R.string.msg_missing_app));
103             setResult(Activity.RESULT_CANCELED, intent);
104             finish();
105         }
106     }
107 
108     @Override
onActivityResult(int requestCode, int resultCode, Intent data)109     public void onActivityResult(int requestCode, int resultCode, Intent data) {
110         setResult(resultCode, data);
111         finish();
112     }
113 }
114