/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.tv.tuner.setup; import android.app.Fragment; import android.app.FragmentManager; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.media.tv.TvContract; import android.os.Bundle; import android.support.v4.app.NotificationCompat; import android.util.Log; import android.view.KeyEvent; import android.widget.Toast; import com.android.tv.TvApplication; import com.android.tv.common.TvCommonConstants; import com.android.tv.common.TvCommonUtils; import com.android.tv.common.ui.setup.SetupActivity; import com.android.tv.common.ui.setup.SetupFragment; import com.android.tv.common.ui.setup.SetupMultiPaneFragment; import com.android.tv.tuner.R; import com.android.tv.tuner.TunerHal; import com.android.tv.tuner.TunerPreferences; import com.android.tv.tuner.tvinput.TunerTvInputService; import com.android.tv.tuner.util.TunerInputInfoUtils; /** * An activity that serves tuner setup process. */ public class TunerSetupActivity extends SetupActivity { private final String TAG = "TunerSetupActivity"; // For the recommendation card private static final String TV_ACTIVITY_CLASS_NAME = "com.android.tv.TvActivity"; private static final String NOTIFY_TAG = "TunerSetup"; private static final int NOTIFY_ID = 1000; private static final String TAG_DRAWABLE = "drawable"; private static final String TAG_ICON = "ic_launcher_s"; private static final int CHANNEL_MAP_SCAN_FILE[] = { R.raw.ut_us_atsc_center_frequencies_8vsb, R.raw.ut_us_cable_standard_center_frequencies_qam256, R.raw.ut_us_all, R.raw.ut_kr_atsc_center_frequencies_8vsb, R.raw.ut_kr_cable_standard_center_frequencies_qam256, R.raw.ut_kr_all, R.raw.ut_kr_dev_cj_cable_center_frequencies_qam256}; private ScanFragment mLastScanFragment; @Override protected void onCreate(Bundle savedInstanceState) { TvApplication.setCurrentRunningProcess(this, false); super.onCreate(savedInstanceState); // TODO: check {@link shouldShowRequestPermissionRationale}. if (checkSelfPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { // No need to check the request result. requestPermissions(new String[] {android.Manifest.permission.ACCESS_COARSE_LOCATION}, 0); } } @Override protected Fragment onCreateInitialFragment() { SetupFragment fragment = new WelcomeFragment(); fragment.setShortDistance(SetupFragment.FRAGMENT_EXIT_TRANSITION | SetupFragment.FRAGMENT_REENTER_TRANSITION); return fragment; } @Override protected boolean executeAction(String category, int actionId, Bundle params) { switch (category) { case WelcomeFragment.ACTION_CATEGORY: switch (actionId) { case SetupMultiPaneFragment.ACTION_DONE: // If the scan was performed, then the result should be OK. setResult(mLastScanFragment == null ? RESULT_CANCELED : RESULT_OK); finish(); break; default: { SetupFragment fragment = new ConnectionTypeFragment(); fragment.setShortDistance(SetupFragment.FRAGMENT_ENTER_TRANSITION | SetupFragment.FRAGMENT_RETURN_TRANSITION); showFragment(fragment, true); break; } } return true; case ConnectionTypeFragment.ACTION_CATEGORY: TunerHal hal = TunerHal.createInstance(getApplicationContext()); if (hal == null) { finish(); Toast.makeText(getApplicationContext(), R.string.ut_channel_scan_tuner_unavailable,Toast.LENGTH_LONG).show(); return true; } try { hal.close(); } catch (Exception e) { Log.e(TAG, "Tuner hal close failed", e); return true; } mLastScanFragment = new ScanFragment(); Bundle args = new Bundle(); args.putInt(ScanFragment.EXTRA_FOR_CHANNEL_SCAN_FILE, CHANNEL_MAP_SCAN_FILE[actionId]); mLastScanFragment.setArguments(args); showFragment(mLastScanFragment, true); return true; case ScanFragment.ACTION_CATEGORY: switch (actionId) { case ScanFragment.ACTION_CANCEL: getFragmentManager().popBackStack(); return true; case ScanFragment.ACTION_FINISH: SetupFragment fragment = new ScanResultFragment(); fragment.setShortDistance(SetupFragment.FRAGMENT_EXIT_TRANSITION | SetupFragment.FRAGMENT_REENTER_TRANSITION); showFragment(fragment, true); return true; } break; case ScanResultFragment.ACTION_CATEGORY: switch (actionId) { case SetupMultiPaneFragment.ACTION_DONE: setResult(RESULT_OK); finish(); break; default: SetupFragment fragment = new ConnectionTypeFragment(); fragment.setShortDistance(SetupFragment.FRAGMENT_ENTER_TRANSITION | SetupFragment.FRAGMENT_RETURN_TRANSITION); showFragment(fragment, true); break; } return true; } return false; } @Override public boolean onKeyUp(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { FragmentManager manager = getFragmentManager(); int count = manager.getBackStackEntryCount(); if (count > 0) { String lastTag = manager.getBackStackEntryAt(count - 1).getName(); if (ScanResultFragment.class.getCanonicalName().equals(lastTag) && count >= 2) { // Pops fragment including ScanFragment. manager.popBackStack(manager.getBackStackEntryAt(count - 2).getName(), FragmentManager.POP_BACK_STACK_INCLUSIVE); return true; } else if (ScanFragment.class.getCanonicalName().equals(lastTag)) { mLastScanFragment.finishScan(true); return true; } } } return super.onKeyUp(keyCode, event); } /** * A callback to be invoked when the TvInputService is enabled or disabled. * * @param context a {@link Context} instance * @param enabled {@code true} for the {@link TunerTvInputService} to be enabled; * otherwise {@code false} */ public static void onTvInputEnabled(Context context, boolean enabled) { // Send a recommendation card for tuner setup if there's no channels and the tuner TV input // setup has been not done. boolean channelScanDoneOnPreference = TunerPreferences.isScanDone(context); int channelCountOnPreference = TunerPreferences.getScannedChannelCount(context); if (enabled && !channelScanDoneOnPreference && channelCountOnPreference == 0) { TunerPreferences.setShouldShowSetupActivity(context, true); sendRecommendationCard(context); } else { TunerPreferences.setShouldShowSetupActivity(context, false); cancelRecommendationCard(context); } } /** * Returns a {@link Intent} to launch the tuner TV input service. * * @param context a {@link Context} instance */ public static Intent createSetupActivity(Context context) { String inputId = TvContract.buildInputId(new ComponentName(context.getPackageName(), TunerTvInputService.class.getName())); // Make an intent to launch the setup activity of USB tuner TV input. Intent intent = TvCommonUtils.createSetupIntent( new Intent(context, TunerSetupActivity.class), inputId); intent.putExtra(TvCommonConstants.EXTRA_INPUT_ID, inputId); Intent tvActivityIntent = new Intent(); tvActivityIntent.setComponent(new ComponentName(context, TV_ACTIVITY_CLASS_NAME)); intent.putExtra(TvCommonConstants.EXTRA_ACTIVITY_AFTER_COMPLETION, tvActivityIntent); return intent; } /** * Returns a {@link PendingIntent} to launch the tuner TV input service. * * @param context a {@link Context} instance */ private static PendingIntent createPendingIntentForSetupActivity(Context context) { return PendingIntent.getActivity(context, 0, createSetupActivity(context), PendingIntent.FLAG_UPDATE_CURRENT); } /** * Sends the recommendation card to start the tuner TV input setup activity. * * @param context a {@link Context} instance */ private static void sendRecommendationCard(Context context) { Resources resources = context.getResources(); String focusedTitle = resources.getString( R.string.ut_setup_recommendation_card_focused_title); String title; if (TunerInputInfoUtils.isBuiltInTuner(context)) { title = resources.getString(R.string.bt_setup_recommendation_card_title); } else { title = resources.getString(R.string.ut_setup_recommendation_card_title); } Bitmap largeIcon = BitmapFactory.decodeResource(resources, R.drawable.recommendation_antenna); // Build and send the notification. Notification notification = new NotificationCompat.BigPictureStyle( new NotificationCompat.Builder(context) .setAutoCancel(false) .setContentTitle(focusedTitle) .setContentText(title) .setContentInfo(title) .setCategory(Notification.CATEGORY_RECOMMENDATION) .setLargeIcon(largeIcon) .setSmallIcon(resources.getIdentifier( TAG_ICON, TAG_DRAWABLE, context.getPackageName())) .setContentIntent(createPendingIntentForSetupActivity(context))) .build(); NotificationManager notificationManager = (NotificationManager) context .getSystemService(Context.NOTIFICATION_SERVICE); notificationManager.notify(NOTIFY_TAG, NOTIFY_ID, notification); } /** * Cancels the previously shown recommendation card. * * @param context a {@link Context} instance */ public static void cancelRecommendationCard(Context context) { NotificationManager notificationManager = (NotificationManager) context .getSystemService(Context.NOTIFICATION_SERVICE); notificationManager.cancel(NOTIFY_TAG, NOTIFY_ID); } }