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