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