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.onboarding;
18
19import android.app.Fragment;
20import android.content.ActivityNotFoundException;
21import android.content.ComponentName;
22import android.content.Context;
23import android.content.Intent;
24import android.content.pm.PackageManager;
25import android.media.tv.TvInputInfo;
26import android.os.Bundle;
27import android.support.annotation.NonNull;
28import android.widget.Toast;
29import com.android.tv.R;
30import com.android.tv.SetupPassthroughActivity;
31import com.android.tv.TvSingletons;
32import com.android.tv.common.ui.setup.SetupActivity;
33import com.android.tv.common.ui.setup.SetupMultiPaneFragment;
34import com.android.tv.common.util.CommonUtils;
35import com.android.tv.common.util.PermissionUtils;
36import com.android.tv.data.ChannelDataManager;
37import com.android.tv.util.OnboardingUtils;
38import com.android.tv.util.SetupUtils;
39import com.android.tv.util.TvInputManagerHelper;
40
41public class OnboardingActivity extends SetupActivity {
42    private static final String KEY_INTENT_AFTER_COMPLETION = "key_intent_after_completion";
43
44    private static final int PERMISSIONS_REQUEST_READ_TV_LISTINGS = 1;
45
46    private static final int SHOW_RIPPLE_DURATION_MS = 266;
47
48    private static final int REQUEST_CODE_START_SETUP_ACTIVITY = 1;
49
50    private ChannelDataManager mChannelDataManager;
51    private TvInputManagerHelper mInputManager;
52    private SetupUtils mSetupUtils;
53    private final ChannelDataManager.Listener mChannelListener =
54            new ChannelDataManager.Listener() {
55                @Override
56                public void onLoadFinished() {
57                    mChannelDataManager.removeListener(this);
58                    mSetupUtils.markNewChannelsBrowsable();
59                }
60
61                @Override
62                public void onChannelListUpdated() {}
63
64                @Override
65                public void onChannelBrowsableChanged() {}
66            };
67
68    /**
69     * Returns an intent to start {@link OnboardingActivity}.
70     *
71     * @param context context to create an intent. Should not be {@code null}.
72     * @param intentAfterCompletion intent which will be used to start a new activity when this
73     *     activity finishes. Should not be {@code null}.
74     */
75    public static Intent buildIntent(
76            @NonNull Context context, @NonNull Intent intentAfterCompletion) {
77        return new Intent(context, OnboardingActivity.class)
78                .putExtra(OnboardingActivity.KEY_INTENT_AFTER_COMPLETION, intentAfterCompletion);
79    }
80
81    @Override
82    protected void onCreate(Bundle savedInstanceState) {
83        super.onCreate(savedInstanceState);
84        TvSingletons singletons = TvSingletons.getSingletons(this);
85        mInputManager = singletons.getTvInputManagerHelper();
86        mSetupUtils = singletons.getSetupUtils();
87        if (PermissionUtils.hasAccessAllEpg(this) || PermissionUtils.hasReadTvListings(this)) {
88            mChannelDataManager = singletons.getChannelDataManager();
89            // Make the channels of the new inputs which have been setup outside Live TV
90            // browsable.
91            if (mChannelDataManager.isDbLoadFinished()) {
92                mSetupUtils.markNewChannelsBrowsable();
93            } else {
94                mChannelDataManager.addListener(mChannelListener);
95            }
96        } else {
97            requestPermissions(
98                    new String[] {PermissionUtils.PERMISSION_READ_TV_LISTINGS},
99                    PERMISSIONS_REQUEST_READ_TV_LISTINGS);
100        }
101    }
102
103    @Override
104    protected void onDestroy() {
105        if (mChannelDataManager != null) {
106            mChannelDataManager.removeListener(mChannelListener);
107        }
108        super.onDestroy();
109    }
110
111    @Override
112    protected Fragment onCreateInitialFragment() {
113        if (PermissionUtils.hasAccessAllEpg(this) || PermissionUtils.hasReadTvListings(this)) {
114            return OnboardingUtils.isFirstRunWithCurrentVersion(this)
115                    ? new WelcomeFragment()
116                    : new SetupSourcesFragment();
117        }
118        return null;
119    }
120
121    @Override
122    public void onRequestPermissionsResult(
123            int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
124        if (requestCode == PERMISSIONS_REQUEST_READ_TV_LISTINGS) {
125            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
126                finish();
127                Intent intentForNextActivity =
128                        getIntent().getParcelableExtra(KEY_INTENT_AFTER_COMPLETION);
129                startActivity(buildIntent(this, intentForNextActivity));
130            } else {
131                Toast.makeText(
132                                this,
133                                R.string.msg_read_tv_listing_permission_denied,
134                                Toast.LENGTH_LONG)
135                        .show();
136                finish();
137            }
138        }
139    }
140
141    private void finishActivity() {
142        Intent intentForNextActivity = getIntent().getParcelableExtra(KEY_INTENT_AFTER_COMPLETION);
143        if (intentForNextActivity != null) {
144            startActivity(intentForNextActivity);
145        }
146        finish();
147    }
148
149    private void showMerchantCollection() {
150        executeActionWithDelay(
151                new Runnable() {
152                    @Override
153                    public void run() {
154                        startActivity(OnboardingUtils.ONLINE_STORE_INTENT);
155                    }
156                },
157                SHOW_RIPPLE_DURATION_MS);
158    }
159
160    @Override
161    protected boolean executeAction(String category, int actionId, Bundle params) {
162        switch (category) {
163            case WelcomeFragment.ACTION_CATEGORY:
164                switch (actionId) {
165                    case WelcomeFragment.ACTION_NEXT:
166                        OnboardingUtils.setFirstRunWithCurrentVersionCompleted(
167                                OnboardingActivity.this);
168                        showFragment(new SetupSourcesFragment(), false);
169                        return true;
170                }
171                break;
172            case SetupSourcesFragment.ACTION_CATEGORY:
173                switch (actionId) {
174                    case SetupSourcesFragment.ACTION_ONLINE_STORE:
175                        showMerchantCollection();
176                        return true;
177                    case SetupSourcesFragment.ACTION_SETUP_INPUT:
178                        {
179                            String inputId =
180                                    params.getString(
181                                            SetupSourcesFragment.ACTION_PARAM_KEY_INPUT_ID);
182                            TvInputInfo input = mInputManager.getTvInputInfo(inputId);
183                            Intent intent = CommonUtils.createSetupIntent(input);
184                            if (intent == null) {
185                                Toast.makeText(
186                                                this,
187                                                R.string.msg_no_setup_activity,
188                                                Toast.LENGTH_SHORT)
189                                        .show();
190                                return true;
191                            }
192                            // Even though other app can handle the intent, the setup launched by
193                            // Live
194                            // channels should go through Live channels SetupPassthroughActivity.
195                            intent.setComponent(
196                                    new ComponentName(this, SetupPassthroughActivity.class));
197                            try {
198                                // Now we know that the user intends to set up this input. Grant
199                                // permission for writing EPG data.
200                                SetupUtils.grantEpgPermission(
201                                        this, input.getServiceInfo().packageName);
202                                startActivityForResult(intent, REQUEST_CODE_START_SETUP_ACTIVITY);
203                            } catch (ActivityNotFoundException e) {
204                                Toast.makeText(
205                                                this,
206                                                getString(
207                                                        R.string.msg_unable_to_start_setup_activity,
208                                                        input.loadLabel(this)),
209                                                Toast.LENGTH_SHORT)
210                                        .show();
211                            }
212                            return true;
213                        }
214                    case SetupMultiPaneFragment.ACTION_DONE:
215                        {
216                            ChannelDataManager manager =
217                                    TvSingletons.getSingletons(OnboardingActivity.this)
218                                            .getChannelDataManager();
219                            if (manager.getChannelCount() == 0) {
220                                finish();
221                            } else {
222                                finishActivity();
223                            }
224                            return true;
225                        }
226                }
227                break;
228        }
229        return false;
230    }
231}
232