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.tuner.setup; 18 19 import android.app.Fragment; 20 import android.app.FragmentManager; 21 import android.app.Notification; 22 import android.app.NotificationManager; 23 import android.app.PendingIntent; 24 import android.content.ComponentName; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.pm.PackageManager; 28 import android.content.res.Resources; 29 import android.graphics.Bitmap; 30 import android.graphics.BitmapFactory; 31 import android.media.tv.TvContract; 32 import android.os.Bundle; 33 import android.support.v4.app.NotificationCompat; 34 import android.util.Log; 35 import android.view.KeyEvent; 36 import android.widget.Toast; 37 38 import com.android.tv.TvApplication; 39 import com.android.tv.common.TvCommonConstants; 40 import com.android.tv.common.TvCommonUtils; 41 import com.android.tv.common.ui.setup.SetupActivity; 42 import com.android.tv.common.ui.setup.SetupFragment; 43 import com.android.tv.common.ui.setup.SetupMultiPaneFragment; 44 import com.android.tv.tuner.R; 45 import com.android.tv.tuner.TunerHal; 46 import com.android.tv.tuner.TunerPreferences; 47 import com.android.tv.tuner.tvinput.TunerTvInputService; 48 import com.android.tv.tuner.util.TunerInputInfoUtils; 49 50 /** 51 * An activity that serves tuner setup process. 52 */ 53 public class TunerSetupActivity extends SetupActivity { 54 private final String TAG = "TunerSetupActivity"; 55 // For the recommendation card 56 private static final String TV_ACTIVITY_CLASS_NAME = "com.android.tv.TvActivity"; 57 private static final String NOTIFY_TAG = "TunerSetup"; 58 private static final int NOTIFY_ID = 1000; 59 private static final String TAG_DRAWABLE = "drawable"; 60 private static final String TAG_ICON = "ic_launcher_s"; 61 62 private static final int CHANNEL_MAP_SCAN_FILE[] = { 63 R.raw.ut_us_atsc_center_frequencies_8vsb, 64 R.raw.ut_us_cable_standard_center_frequencies_qam256, 65 R.raw.ut_us_all, 66 R.raw.ut_kr_atsc_center_frequencies_8vsb, 67 R.raw.ut_kr_cable_standard_center_frequencies_qam256, 68 R.raw.ut_kr_all, 69 R.raw.ut_kr_dev_cj_cable_center_frequencies_qam256}; 70 71 private ScanFragment mLastScanFragment; 72 73 @Override onCreate(Bundle savedInstanceState)74 protected void onCreate(Bundle savedInstanceState) { 75 TvApplication.setCurrentRunningProcess(this, false); 76 super.onCreate(savedInstanceState); 77 // TODO: check {@link shouldShowRequestPermissionRationale}. 78 if (checkSelfPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) 79 != PackageManager.PERMISSION_GRANTED) { 80 // No need to check the request result. 81 requestPermissions(new String[] {android.Manifest.permission.ACCESS_COARSE_LOCATION}, 82 0); 83 } 84 } 85 86 @Override onCreateInitialFragment()87 protected Fragment onCreateInitialFragment() { 88 SetupFragment fragment = new WelcomeFragment(); 89 fragment.setShortDistance(SetupFragment.FRAGMENT_EXIT_TRANSITION 90 | SetupFragment.FRAGMENT_REENTER_TRANSITION); 91 return fragment; 92 } 93 94 @Override executeAction(String category, int actionId, Bundle params)95 protected boolean executeAction(String category, int actionId, Bundle params) { 96 switch (category) { 97 case WelcomeFragment.ACTION_CATEGORY: 98 switch (actionId) { 99 case SetupMultiPaneFragment.ACTION_DONE: 100 // If the scan was performed, then the result should be OK. 101 setResult(mLastScanFragment == null ? RESULT_CANCELED : RESULT_OK); 102 finish(); 103 break; 104 default: { 105 SetupFragment fragment = new ConnectionTypeFragment(); 106 fragment.setShortDistance(SetupFragment.FRAGMENT_ENTER_TRANSITION 107 | SetupFragment.FRAGMENT_RETURN_TRANSITION); 108 showFragment(fragment, true); 109 break; 110 } 111 } 112 return true; 113 case ConnectionTypeFragment.ACTION_CATEGORY: 114 TunerHal hal = TunerHal.createInstance(getApplicationContext()); 115 if (hal == null) { 116 finish(); 117 Toast.makeText(getApplicationContext(), 118 R.string.ut_channel_scan_tuner_unavailable,Toast.LENGTH_LONG).show(); 119 return true; 120 } 121 try { 122 hal.close(); 123 } catch (Exception e) { 124 Log.e(TAG, "Tuner hal close failed", e); 125 return true; 126 } 127 mLastScanFragment = new ScanFragment(); 128 Bundle args = new Bundle(); 129 args.putInt(ScanFragment.EXTRA_FOR_CHANNEL_SCAN_FILE, 130 CHANNEL_MAP_SCAN_FILE[actionId]); 131 mLastScanFragment.setArguments(args); 132 showFragment(mLastScanFragment, true); 133 return true; 134 case ScanFragment.ACTION_CATEGORY: 135 switch (actionId) { 136 case ScanFragment.ACTION_CANCEL: 137 getFragmentManager().popBackStack(); 138 return true; 139 case ScanFragment.ACTION_FINISH: 140 SetupFragment fragment = new ScanResultFragment(); 141 fragment.setShortDistance(SetupFragment.FRAGMENT_EXIT_TRANSITION 142 | SetupFragment.FRAGMENT_REENTER_TRANSITION); 143 showFragment(fragment, true); 144 return true; 145 } 146 break; 147 case ScanResultFragment.ACTION_CATEGORY: 148 switch (actionId) { 149 case SetupMultiPaneFragment.ACTION_DONE: 150 setResult(RESULT_OK); 151 finish(); 152 break; 153 default: 154 SetupFragment fragment = new ConnectionTypeFragment(); 155 fragment.setShortDistance(SetupFragment.FRAGMENT_ENTER_TRANSITION 156 | SetupFragment.FRAGMENT_RETURN_TRANSITION); 157 showFragment(fragment, true); 158 break; 159 } 160 return true; 161 } 162 return false; 163 } 164 165 @Override onKeyUp(int keyCode, KeyEvent event)166 public boolean onKeyUp(int keyCode, KeyEvent event) { 167 if (keyCode == KeyEvent.KEYCODE_BACK) { 168 FragmentManager manager = getFragmentManager(); 169 int count = manager.getBackStackEntryCount(); 170 if (count > 0) { 171 String lastTag = manager.getBackStackEntryAt(count - 1).getName(); 172 if (ScanResultFragment.class.getCanonicalName().equals(lastTag) && count >= 2) { 173 // Pops fragment including ScanFragment. 174 manager.popBackStack(manager.getBackStackEntryAt(count - 2).getName(), 175 FragmentManager.POP_BACK_STACK_INCLUSIVE); 176 return true; 177 } else if (ScanFragment.class.getCanonicalName().equals(lastTag)) { 178 mLastScanFragment.finishScan(true); 179 return true; 180 } 181 } 182 } 183 return super.onKeyUp(keyCode, event); 184 } 185 186 /** 187 * A callback to be invoked when the TvInputService is enabled or disabled. 188 * 189 * @param context a {@link Context} instance 190 * @param enabled {@code true} for the {@link TunerTvInputService} to be enabled; 191 * otherwise {@code false} 192 */ onTvInputEnabled(Context context, boolean enabled)193 public static void onTvInputEnabled(Context context, boolean enabled) { 194 // Send a recommendation card for tuner setup if there's no channels and the tuner TV input 195 // setup has been not done. 196 boolean channelScanDoneOnPreference = TunerPreferences.isScanDone(context); 197 int channelCountOnPreference = TunerPreferences.getScannedChannelCount(context); 198 if (enabled && !channelScanDoneOnPreference && channelCountOnPreference == 0) { 199 TunerPreferences.setShouldShowSetupActivity(context, true); 200 sendRecommendationCard(context); 201 } else { 202 TunerPreferences.setShouldShowSetupActivity(context, false); 203 cancelRecommendationCard(context); 204 } 205 } 206 207 /** 208 * Returns a {@link Intent} to launch the tuner TV input service. 209 * 210 * @param context a {@link Context} instance 211 */ createSetupActivity(Context context)212 public static Intent createSetupActivity(Context context) { 213 String inputId = TvContract.buildInputId(new ComponentName(context.getPackageName(), 214 TunerTvInputService.class.getName())); 215 216 // Make an intent to launch the setup activity of USB tuner TV input. 217 Intent intent = TvCommonUtils.createSetupIntent( 218 new Intent(context, TunerSetupActivity.class), inputId); 219 intent.putExtra(TvCommonConstants.EXTRA_INPUT_ID, inputId); 220 Intent tvActivityIntent = new Intent(); 221 tvActivityIntent.setComponent(new ComponentName(context, TV_ACTIVITY_CLASS_NAME)); 222 intent.putExtra(TvCommonConstants.EXTRA_ACTIVITY_AFTER_COMPLETION, tvActivityIntent); 223 return intent; 224 } 225 226 /** 227 * Returns a {@link PendingIntent} to launch the tuner TV input service. 228 * 229 * @param context a {@link Context} instance 230 */ createPendingIntentForSetupActivity(Context context)231 private static PendingIntent createPendingIntentForSetupActivity(Context context) { 232 return PendingIntent.getActivity(context, 0, createSetupActivity(context), 233 PendingIntent.FLAG_UPDATE_CURRENT); 234 } 235 236 /** 237 * Sends the recommendation card to start the tuner TV input setup activity. 238 * 239 * @param context a {@link Context} instance 240 */ sendRecommendationCard(Context context)241 private static void sendRecommendationCard(Context context) { 242 Resources resources = context.getResources(); 243 String focusedTitle = resources.getString( 244 R.string.ut_setup_recommendation_card_focused_title); 245 String title; 246 if (TunerInputInfoUtils.isBuiltInTuner(context)) { 247 title = resources.getString(R.string.bt_setup_recommendation_card_title); 248 } else { 249 title = resources.getString(R.string.ut_setup_recommendation_card_title); 250 } 251 Bitmap largeIcon = BitmapFactory.decodeResource(resources, 252 R.drawable.recommendation_antenna); 253 254 // Build and send the notification. 255 Notification notification = new NotificationCompat.BigPictureStyle( 256 new NotificationCompat.Builder(context) 257 .setAutoCancel(false) 258 .setContentTitle(focusedTitle) 259 .setContentText(title) 260 .setContentInfo(title) 261 .setCategory(Notification.CATEGORY_RECOMMENDATION) 262 .setLargeIcon(largeIcon) 263 .setSmallIcon(resources.getIdentifier( 264 TAG_ICON, TAG_DRAWABLE, context.getPackageName())) 265 .setContentIntent(createPendingIntentForSetupActivity(context))) 266 .build(); 267 NotificationManager notificationManager = (NotificationManager) context 268 .getSystemService(Context.NOTIFICATION_SERVICE); 269 notificationManager.notify(NOTIFY_TAG, NOTIFY_ID, notification); 270 } 271 272 /** 273 * Cancels the previously shown recommendation card. 274 * 275 * @param context a {@link Context} instance 276 */ cancelRecommendationCard(Context context)277 public static void cancelRecommendationCard(Context context) { 278 NotificationManager notificationManager = (NotificationManager) context 279 .getSystemService(Context.NOTIFICATION_SERVICE); 280 notificationManager.cancel(NOTIFY_TAG, NOTIFY_ID); 281 } 282 } 283