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 com.android.tv;
18
19import android.app.Activity;
20import android.content.ActivityNotFoundException;
21import android.content.Context;
22import android.content.Intent;
23import android.os.Bundle;
24import 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 */
35public 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     */
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     */
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
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
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
109    public void onActivityResult(int requestCode, int resultCode, Intent data) {
110        setResult(resultCode, data);
111        finish();
112    }
113}
114