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