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