• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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.data;
18 
19 import android.annotation.TargetApi;
20 import android.content.ContentResolver;
21 import android.content.ContentUris;
22 import android.content.Context;
23 import android.database.Cursor;
24 import android.database.SQLException;
25 import android.graphics.Bitmap;
26 import android.graphics.drawable.BitmapDrawable;
27 import android.graphics.drawable.Drawable;
28 import android.media.tv.TvContract;
29 import android.net.Uri;
30 import android.os.AsyncTask;
31 import android.os.Build;
32 import android.support.annotation.IntDef;
33 import android.support.annotation.MainThread;
34 import android.support.media.tv.ChannelLogoUtils;
35 import android.support.media.tv.PreviewProgram;
36 import android.util.Log;
37 import android.util.Pair;
38 import com.android.tv.R;
39 import com.android.tv.common.util.PermissionUtils;
40 import java.lang.annotation.Retention;
41 import java.lang.annotation.RetentionPolicy;
42 import java.util.HashMap;
43 import java.util.Map;
44 import java.util.Set;
45 import java.util.concurrent.CopyOnWriteArraySet;
46 
47 /** Class to manage the preview data. */
48 @TargetApi(Build.VERSION_CODES.O)
49 @MainThread
50 public class PreviewDataManager {
51     private static final String TAG = "PreviewDataManager";
52     private static final boolean DEBUG = false;
53 
54     /** Invalid preview channel ID. */
55     public static final long INVALID_PREVIEW_CHANNEL_ID = -1;
56 
57     @IntDef({TYPE_DEFAULT_PREVIEW_CHANNEL, TYPE_RECORDED_PROGRAM_PREVIEW_CHANNEL})
58     @Retention(RetentionPolicy.SOURCE)
59     public @interface PreviewChannelType {}
60 
61     /** Type of default preview channel */
62     public static final int TYPE_DEFAULT_PREVIEW_CHANNEL = 1;
63     /** Type of recorded program channel */
64     public static final int TYPE_RECORDED_PROGRAM_PREVIEW_CHANNEL = 2;
65 
66     private final Context mContext;
67     private final ContentResolver mContentResolver;
68     private boolean mLoadFinished;
69     private PreviewData mPreviewData = new PreviewData();
70     private final Set<PreviewDataListener> mPreviewDataListeners = new CopyOnWriteArraySet<>();
71 
72     private QueryPreviewDataTask mQueryPreviewTask;
73     private final Map<Long, CreatePreviewChannelTask> mCreatePreviewChannelTasks = new HashMap<>();
74     private final Map<Long, UpdatePreviewProgramTask> mUpdatePreviewProgramTasks = new HashMap<>();
75 
76     private final int mPreviewChannelLogoWidth;
77     private final int mPreviewChannelLogoHeight;
78 
PreviewDataManager(Context context)79     public PreviewDataManager(Context context) {
80         mContext = context.getApplicationContext();
81         mContentResolver = context.getContentResolver();
82         mPreviewChannelLogoWidth =
83                 mContext.getResources().getDimensionPixelSize(R.dimen.preview_channel_logo_width);
84         mPreviewChannelLogoHeight =
85                 mContext.getResources().getDimensionPixelSize(R.dimen.preview_channel_logo_height);
86     }
87 
88     /** Starts the preview data manager. */
start()89     public void start() {
90         if (mQueryPreviewTask == null) {
91             mQueryPreviewTask = new QueryPreviewDataTask();
92             mQueryPreviewTask.execute();
93         }
94     }
95 
96     /** Stops the preview data manager. */
stop()97     public void stop() {
98         if (mQueryPreviewTask != null) {
99             mQueryPreviewTask.cancel(true);
100         }
101         for (CreatePreviewChannelTask createPreviewChannelTask :
102                 mCreatePreviewChannelTasks.values()) {
103             createPreviewChannelTask.cancel(true);
104         }
105         for (UpdatePreviewProgramTask updatePreviewProgramTask :
106                 mUpdatePreviewProgramTasks.values()) {
107             updatePreviewProgramTask.cancel(true);
108         }
109 
110         mQueryPreviewTask = null;
111         mCreatePreviewChannelTasks.clear();
112         mUpdatePreviewProgramTasks.clear();
113     }
114 
115     /** Gets preview channel ID from the preview channel type. */
getPreviewChannelId(long previewChannelType)116     public @PreviewChannelType long getPreviewChannelId(long previewChannelType) {
117         return mPreviewData.getPreviewChannelId(previewChannelType);
118     }
119 
120     /** Creates default preview channel. */
createDefaultPreviewChannel( OnPreviewChannelCreationResultListener onPreviewChannelCreationResultListener)121     public void createDefaultPreviewChannel(
122             OnPreviewChannelCreationResultListener onPreviewChannelCreationResultListener) {
123         createPreviewChannel(TYPE_DEFAULT_PREVIEW_CHANNEL, onPreviewChannelCreationResultListener);
124     }
125 
126     /** Creates a preview channel for specific channel type. */
createPreviewChannel( @reviewChannelType long previewChannelType, OnPreviewChannelCreationResultListener onPreviewChannelCreationResultListener)127     public void createPreviewChannel(
128             @PreviewChannelType long previewChannelType,
129             OnPreviewChannelCreationResultListener onPreviewChannelCreationResultListener) {
130         CreatePreviewChannelTask currentRunningCreateTask =
131                 mCreatePreviewChannelTasks.get(previewChannelType);
132         if (currentRunningCreateTask == null) {
133             CreatePreviewChannelTask createPreviewChannelTask =
134                     new CreatePreviewChannelTask(previewChannelType);
135             createPreviewChannelTask.addOnPreviewChannelCreationResultListener(
136                     onPreviewChannelCreationResultListener);
137             createPreviewChannelTask.execute();
138             mCreatePreviewChannelTasks.put(previewChannelType, createPreviewChannelTask);
139         } else {
140             currentRunningCreateTask.addOnPreviewChannelCreationResultListener(
141                     onPreviewChannelCreationResultListener);
142         }
143     }
144 
145     /** Returns {@code true} if the preview data is loaded. */
isLoadFinished()146     public boolean isLoadFinished() {
147         return mLoadFinished;
148     }
149 
150     /** Adds listener. */
addListener(PreviewDataListener previewDataListener)151     public void addListener(PreviewDataListener previewDataListener) {
152         mPreviewDataListeners.add(previewDataListener);
153     }
154 
155     /** Removes listener. */
removeListener(PreviewDataListener previewDataListener)156     public void removeListener(PreviewDataListener previewDataListener) {
157         mPreviewDataListeners.remove(previewDataListener);
158     }
159 
160     /** Updates the preview programs table for a specific preview channel. */
updatePreviewProgramsForChannel( long previewChannelId, Set<PreviewProgramContent> programs, PreviewDataListener previewDataListener)161     public void updatePreviewProgramsForChannel(
162             long previewChannelId,
163             Set<PreviewProgramContent> programs,
164             PreviewDataListener previewDataListener) {
165         UpdatePreviewProgramTask currentRunningUpdateTask =
166                 mUpdatePreviewProgramTasks.get(previewChannelId);
167         if (currentRunningUpdateTask != null
168                 && currentRunningUpdateTask.getPrograms().equals(programs)) {
169             currentRunningUpdateTask.addPreviewDataListener(previewDataListener);
170             return;
171         }
172         UpdatePreviewProgramTask updatePreviewProgramTask =
173                 new UpdatePreviewProgramTask(previewChannelId, programs);
174         updatePreviewProgramTask.addPreviewDataListener(previewDataListener);
175         if (currentRunningUpdateTask != null) {
176             currentRunningUpdateTask.cancel(true);
177             currentRunningUpdateTask.saveStatus();
178             updatePreviewProgramTask.addPreviewDataListeners(
179                     currentRunningUpdateTask.getPreviewDataListeners());
180         }
181         updatePreviewProgramTask.execute();
182         mUpdatePreviewProgramTasks.put(previewChannelId, updatePreviewProgramTask);
183     }
184 
notifyPreviewDataLoadFinished()185     private void notifyPreviewDataLoadFinished() {
186         for (PreviewDataListener l : mPreviewDataListeners) {
187             l.onPreviewDataLoadFinished();
188         }
189     }
190 
191     public interface PreviewDataListener {
192         /** Called when the preview data is loaded. */
onPreviewDataLoadFinished()193         void onPreviewDataLoadFinished();
194 
195         /** Called when the preview data is updated. */
onPreviewDataUpdateFinished()196         void onPreviewDataUpdateFinished();
197     }
198 
199     public interface OnPreviewChannelCreationResultListener {
200         /**
201          * Called when the creation of preview channel is finished.
202          *
203          * @param createdPreviewChannelId The preview channel ID if created successfully, otherwise
204          *     it's {@value #INVALID_PREVIEW_CHANNEL_ID}.
205          */
onPreviewChannelCreationResult(long createdPreviewChannelId)206         void onPreviewChannelCreationResult(long createdPreviewChannelId);
207     }
208 
209     private final class QueryPreviewDataTask extends AsyncTask<Void, Void, PreviewData> {
210         private final String PARAM_PREVIEW = "preview";
211         private final String mChannelSelection = TvContract.Channels.COLUMN_PACKAGE_NAME + "=?";
212 
213         @Override
doInBackground(Void... voids)214         protected PreviewData doInBackground(Void... voids) {
215             // Query preview channels and programs.
216             if (DEBUG) Log.d(TAG, "QueryPreviewDataTask.doInBackground");
217             PreviewData previewData = new PreviewData();
218             try {
219                 Uri previewChannelsUri =
220                         PreviewDataUtils.addQueryParamToUri(
221                                 TvContract.Channels.CONTENT_URI,
222                                 new Pair<>(PARAM_PREVIEW, String.valueOf(true)));
223                 String packageName = mContext.getPackageName();
224                 if (PermissionUtils.hasAccessAllEpg(mContext)) {
225                     try (Cursor cursor =
226                             mContentResolver.query(
227                                     previewChannelsUri,
228                                     android.support.media.tv.Channel.PROJECTION,
229                                     mChannelSelection,
230                                     new String[] {packageName},
231                                     null)) {
232                         if (cursor != null) {
233                             while (cursor.moveToNext()) {
234                                 android.support.media.tv.Channel previewChannel =
235                                         android.support.media.tv.Channel.fromCursor(cursor);
236                                 Long previewChannelType = previewChannel.getInternalProviderFlag1();
237                                 if (previewChannelType != null) {
238                                     previewData.addPreviewChannelId(
239                                             previewChannelType, previewChannel.getId());
240                                 }
241                             }
242                         }
243                     }
244                 } else {
245                     try (Cursor cursor =
246                             mContentResolver.query(
247                                     previewChannelsUri,
248                                     android.support.media.tv.Channel.PROJECTION,
249                                     null,
250                                     null,
251                                     null)) {
252                         if (cursor != null) {
253                             while (cursor.moveToNext()) {
254                                 android.support.media.tv.Channel previewChannel =
255                                         android.support.media.tv.Channel.fromCursor(cursor);
256                                 Long previewChannelType = previewChannel.getInternalProviderFlag1();
257                                 if (packageName.equals(previewChannel.getPackageName())
258                                         && previewChannelType != null) {
259                                     previewData.addPreviewChannelId(
260                                             previewChannelType, previewChannel.getId());
261                                 }
262                             }
263                         }
264                     }
265                 }
266 
267                 for (long previewChannelId : previewData.getAllPreviewChannelIds().values()) {
268                     Uri previewProgramsUriForPreviewChannel =
269                             TvContract.buildPreviewProgramsUriForChannel(previewChannelId);
270                     try (Cursor previewProgramCursor =
271                             mContentResolver.query(
272                                     previewProgramsUriForPreviewChannel,
273                                     PreviewProgram.PROJECTION,
274                                     null,
275                                     null,
276                                     null)) {
277                         if (previewProgramCursor != null) {
278                             while (previewProgramCursor.moveToNext()) {
279                                 PreviewProgram previewProgram =
280                                         PreviewProgram.fromCursor(previewProgramCursor);
281                                 previewData.addPreviewProgram(previewProgram);
282                             }
283                         }
284                     }
285                 }
286             } catch (SQLException e) {
287                 Log.w(TAG, "Unable to get preview data", e);
288             }
289             return previewData;
290         }
291 
292         @Override
onPostExecute(PreviewData result)293         protected void onPostExecute(PreviewData result) {
294             super.onPostExecute(result);
295             if (mQueryPreviewTask == this) {
296                 mQueryPreviewTask = null;
297                 mPreviewData = new PreviewData(result);
298                 mLoadFinished = true;
299                 notifyPreviewDataLoadFinished();
300             }
301         }
302     }
303 
304     private final class CreatePreviewChannelTask extends AsyncTask<Void, Void, Long> {
305         private final long mPreviewChannelType;
306         private Set<OnPreviewChannelCreationResultListener>
307                 mOnPreviewChannelCreationResultListeners = new CopyOnWriteArraySet<>();
308 
CreatePreviewChannelTask(long previewChannelType)309         public CreatePreviewChannelTask(long previewChannelType) {
310             mPreviewChannelType = previewChannelType;
311         }
312 
addOnPreviewChannelCreationResultListener( OnPreviewChannelCreationResultListener onPreviewChannelCreationResultListener)313         public void addOnPreviewChannelCreationResultListener(
314                 OnPreviewChannelCreationResultListener onPreviewChannelCreationResultListener) {
315             if (onPreviewChannelCreationResultListener != null) {
316                 mOnPreviewChannelCreationResultListeners.add(
317                         onPreviewChannelCreationResultListener);
318             }
319         }
320 
321         @Override
doInBackground(Void... params)322         protected Long doInBackground(Void... params) {
323             if (DEBUG) Log.d(TAG, "CreatePreviewChannelTask.doInBackground");
324             long previewChannelId;
325             try {
326                 Uri channelUri =
327                         mContentResolver.insert(
328                                 TvContract.Channels.CONTENT_URI,
329                                 PreviewDataUtils.createPreviewChannel(mContext, mPreviewChannelType)
330                                         .toContentValues());
331                 if (channelUri != null) {
332                     previewChannelId = ContentUris.parseId(channelUri);
333                 } else {
334                     Log.e(TAG, "Fail to insert preview channel");
335                     return INVALID_PREVIEW_CHANNEL_ID;
336                 }
337             } catch (UnsupportedOperationException | NumberFormatException e) {
338                 Log.e(TAG, "Fail to get channel ID");
339                 return INVALID_PREVIEW_CHANNEL_ID;
340             }
341             Drawable appIcon = mContext.getApplicationInfo().loadIcon(mContext.getPackageManager());
342             if (appIcon != null && appIcon instanceof BitmapDrawable) {
343                 ChannelLogoUtils.storeChannelLogo(
344                         mContext,
345                         previewChannelId,
346                         Bitmap.createScaledBitmap(
347                                 ((BitmapDrawable) appIcon).getBitmap(),
348                                 mPreviewChannelLogoWidth,
349                                 mPreviewChannelLogoHeight,
350                                 false));
351             }
352             return previewChannelId;
353         }
354 
355         @Override
onPostExecute(Long result)356         protected void onPostExecute(Long result) {
357             super.onPostExecute(result);
358             if (result != INVALID_PREVIEW_CHANNEL_ID) {
359                 mPreviewData.addPreviewChannelId(mPreviewChannelType, result);
360             }
361             for (OnPreviewChannelCreationResultListener onPreviewChannelCreationResultListener :
362                     mOnPreviewChannelCreationResultListeners) {
363                 onPreviewChannelCreationResultListener.onPreviewChannelCreationResult(result);
364             }
365             mCreatePreviewChannelTasks.remove(mPreviewChannelType);
366         }
367     }
368 
369     /**
370      * Updates the whole data which belongs to the package in preview programs table for a specific
371      * preview channel with a set of {@link PreviewProgramContent}.
372      */
373     private final class UpdatePreviewProgramTask extends AsyncTask<Void, Void, Void> {
374         private long mPreviewChannelId;
375         private Set<PreviewProgramContent> mPrograms;
376         private Map<Long, Long> mCurrentProgramId2PreviewProgramId;
377         private Set<PreviewDataListener> mPreviewDataListeners = new CopyOnWriteArraySet<>();
378 
UpdatePreviewProgramTask( long previewChannelId, Set<PreviewProgramContent> programs)379         public UpdatePreviewProgramTask(
380                 long previewChannelId, Set<PreviewProgramContent> programs) {
381             mPreviewChannelId = previewChannelId;
382             mPrograms = programs;
383             if (mPreviewData.getPreviewProgramIds(previewChannelId) == null) {
384                 mCurrentProgramId2PreviewProgramId = new HashMap<>();
385             } else {
386                 mCurrentProgramId2PreviewProgramId =
387                         new HashMap<>(mPreviewData.getPreviewProgramIds(previewChannelId));
388             }
389         }
390 
addPreviewDataListener(PreviewDataListener previewDataListener)391         public void addPreviewDataListener(PreviewDataListener previewDataListener) {
392             if (previewDataListener != null) {
393                 mPreviewDataListeners.add(previewDataListener);
394             }
395         }
396 
addPreviewDataListeners(Set<PreviewDataListener> previewDataListeners)397         public void addPreviewDataListeners(Set<PreviewDataListener> previewDataListeners) {
398             if (previewDataListeners != null) {
399                 mPreviewDataListeners.addAll(previewDataListeners);
400             }
401         }
402 
getPrograms()403         public Set<PreviewProgramContent> getPrograms() {
404             return mPrograms;
405         }
406 
getPreviewDataListeners()407         public Set<PreviewDataListener> getPreviewDataListeners() {
408             return mPreviewDataListeners;
409         }
410 
411         @Override
doInBackground(Void... params)412         protected Void doInBackground(Void... params) {
413             if (DEBUG) Log.d(TAG, "UpdatePreviewProgamTask.doInBackground");
414             Map<Long, Long> uncheckedPrograms = new HashMap<>(mCurrentProgramId2PreviewProgramId);
415             for (PreviewProgramContent program : mPrograms) {
416                 if (isCancelled()) {
417                     return null;
418                 }
419                 Long existingPreviewProgramId = uncheckedPrograms.remove(program.getId());
420                 if (existingPreviewProgramId != null) {
421                     if (DEBUG)
422                         Log.d(
423                                 TAG,
424                                 "Preview program "
425                                         + existingPreviewProgramId
426                                         + " "
427                                         + "already exists for program "
428                                         + program.getId());
429                     continue;
430                 }
431                 try {
432                     Uri programUri =
433                             mContentResolver.insert(
434                                     TvContract.PreviewPrograms.CONTENT_URI,
435                                     PreviewDataUtils.createPreviewProgramFromContent(program)
436                                             .toContentValues());
437                     if (programUri != null) {
438                         long previewProgramId = ContentUris.parseId(programUri);
439                         mCurrentProgramId2PreviewProgramId.put(program.getId(), previewProgramId);
440                         if (DEBUG) Log.d(TAG, "Add new preview program " + previewProgramId);
441                     } else {
442                         Log.e(TAG, "Fail to insert preview program");
443                     }
444                 } catch (Exception e) {
445                     Log.e(TAG, "Fail to get preview program ID");
446                 }
447             }
448 
449             for (Long key : uncheckedPrograms.keySet()) {
450                 if (isCancelled()) {
451                     return null;
452                 }
453                 try {
454                     if (DEBUG) Log.d(TAG, "Remove preview program " + uncheckedPrograms.get(key));
455                     mContentResolver.delete(
456                             TvContract.buildPreviewProgramUri(uncheckedPrograms.get(key)),
457                             null,
458                             null);
459                     mCurrentProgramId2PreviewProgramId.remove(key);
460                 } catch (Exception e) {
461                     Log.e(TAG, "Fail to remove preview program " + uncheckedPrograms.get(key));
462                 }
463             }
464             return null;
465         }
466 
467         @Override
onPostExecute(Void result)468         protected void onPostExecute(Void result) {
469             super.onPostExecute(result);
470             mPreviewData.setPreviewProgramIds(
471                     mPreviewChannelId, mCurrentProgramId2PreviewProgramId);
472             mUpdatePreviewProgramTasks.remove(mPreviewChannelId);
473             for (PreviewDataListener previewDataListener : mPreviewDataListeners) {
474                 previewDataListener.onPreviewDataUpdateFinished();
475             }
476         }
477 
saveStatus()478         public void saveStatus() {
479             mPreviewData.setPreviewProgramIds(
480                     mPreviewChannelId, mCurrentProgramId2PreviewProgramId);
481         }
482     }
483 
484     /** Class to store the query result of preview data. */
485     private static final class PreviewData {
486         private Map<Long, Long> mPreviewChannelType2Id = new HashMap<>();
487         private Map<Long, Map<Long, Long>> mProgramId2PreviewProgramId = new HashMap<>();
488 
PreviewData()489         PreviewData() {
490             mPreviewChannelType2Id = new HashMap<>();
491             mProgramId2PreviewProgramId = new HashMap<>();
492         }
493 
PreviewData(PreviewData previewData)494         PreviewData(PreviewData previewData) {
495             mPreviewChannelType2Id = new HashMap<>(previewData.mPreviewChannelType2Id);
496             mProgramId2PreviewProgramId = new HashMap<>(previewData.mProgramId2PreviewProgramId);
497         }
498 
addPreviewProgram(PreviewProgram previewProgram)499         public void addPreviewProgram(PreviewProgram previewProgram) {
500             long previewChannelId = previewProgram.getChannelId();
501             Map<Long, Long> programId2PreviewProgram =
502                     mProgramId2PreviewProgramId.get(previewChannelId);
503             if (programId2PreviewProgram == null) {
504                 programId2PreviewProgram = new HashMap<>();
505             }
506             mProgramId2PreviewProgramId.put(previewChannelId, programId2PreviewProgram);
507             if (previewProgram.getInternalProviderId() != null) {
508                 programId2PreviewProgram.put(
509                         Long.parseLong(previewProgram.getInternalProviderId()),
510                         previewProgram.getId());
511             }
512         }
513 
getPreviewChannelId(long previewChannelType)514         public @PreviewChannelType long getPreviewChannelId(long previewChannelType) {
515             Long result = mPreviewChannelType2Id.get(previewChannelType);
516             return result == null ? INVALID_PREVIEW_CHANNEL_ID : result;
517         }
518 
getAllPreviewChannelIds()519         public Map<Long, Long> getAllPreviewChannelIds() {
520             return mPreviewChannelType2Id;
521         }
522 
addPreviewChannelId(long previewChannelType, long previewChannelId)523         public void addPreviewChannelId(long previewChannelType, long previewChannelId) {
524             mPreviewChannelType2Id.put(previewChannelType, previewChannelId);
525         }
526 
removePreviewChannelId(long previewChannelType)527         public void removePreviewChannelId(long previewChannelType) {
528             mPreviewChannelType2Id.remove(previewChannelType);
529         }
530 
removePreviewChannel(long previewChannelId)531         public void removePreviewChannel(long previewChannelId) {
532             removePreviewChannelId(previewChannelId);
533             removePreviewProgramIds(previewChannelId);
534         }
535 
getPreviewProgramIds(long previewChannelId)536         public Map<Long, Long> getPreviewProgramIds(long previewChannelId) {
537             return mProgramId2PreviewProgramId.get(previewChannelId);
538         }
539 
getAllPreviewProgramIds()540         public Map<Long, Map<Long, Long>> getAllPreviewProgramIds() {
541             return mProgramId2PreviewProgramId;
542         }
543 
setPreviewProgramIds( long previewChannelId, Map<Long, Long> programId2PreviewProgramId)544         public void setPreviewProgramIds(
545                 long previewChannelId, Map<Long, Long> programId2PreviewProgramId) {
546             mProgramId2PreviewProgramId.put(previewChannelId, programId2PreviewProgramId);
547         }
548 
removePreviewProgramIds(long previewChannelId)549         public void removePreviewProgramIds(long previewChannelId) {
550             mProgramId2PreviewProgramId.remove(previewChannelId);
551         }
552     }
553 
554     /** A utils class for preview data. */
555     public static final class PreviewDataUtils {
556         /** Creates a preview channel. */
createPreviewChannel( Context context, @PreviewChannelType long previewChannelType)557         public static android.support.media.tv.Channel createPreviewChannel(
558                 Context context, @PreviewChannelType long previewChannelType) {
559             if (previewChannelType == TYPE_RECORDED_PROGRAM_PREVIEW_CHANNEL) {
560                 return createRecordedProgramPreviewChannel(context, previewChannelType);
561             }
562             return createDefaultPreviewChannel(context, previewChannelType);
563         }
564 
createDefaultPreviewChannel( Context context, @PreviewChannelType long previewChannelType)565         private static android.support.media.tv.Channel createDefaultPreviewChannel(
566                 Context context, @PreviewChannelType long previewChannelType) {
567             android.support.media.tv.Channel.Builder builder =
568                     new android.support.media.tv.Channel.Builder();
569             CharSequence appLabel =
570                     context.getApplicationInfo().loadLabel(context.getPackageManager());
571             CharSequence appDescription =
572                     context.getApplicationInfo().loadDescription(context.getPackageManager());
573             builder.setType(TvContract.Channels.TYPE_PREVIEW)
574                     .setDisplayName(appLabel == null ? null : appLabel.toString())
575                     .setDescription(appDescription == null ? null : appDescription.toString())
576                     .setAppLinkIntentUri(TvContract.Channels.CONTENT_URI)
577                     .setInternalProviderFlag1(previewChannelType);
578             return builder.build();
579         }
580 
createRecordedProgramPreviewChannel( Context context, @PreviewChannelType long previewChannelType)581         private static android.support.media.tv.Channel createRecordedProgramPreviewChannel(
582                 Context context, @PreviewChannelType long previewChannelType) {
583             android.support.media.tv.Channel.Builder builder =
584                     new android.support.media.tv.Channel.Builder();
585             builder.setType(TvContract.Channels.TYPE_PREVIEW)
586                     .setDisplayName(
587                             context.getResources()
588                                     .getString(R.string.recorded_programs_preview_channel))
589                     .setAppLinkIntentUri(TvContract.Channels.CONTENT_URI)
590                     .setInternalProviderFlag1(previewChannelType);
591             return builder.build();
592         }
593 
594         /** Creates a preview program. */
createPreviewProgramFromContent( PreviewProgramContent program)595         public static PreviewProgram createPreviewProgramFromContent(
596                 PreviewProgramContent program) {
597             PreviewProgram.Builder builder = new PreviewProgram.Builder();
598             builder.setChannelId(program.getPreviewChannelId())
599                     .setType(program.getType())
600                     .setLive(program.getLive())
601                     .setTitle(program.getTitle())
602                     .setDescription(program.getDescription())
603                     .setPosterArtUri(program.getPosterArtUri())
604                     .setIntentUri(program.getIntentUri())
605                     .setPreviewVideoUri(program.getPreviewVideoUri())
606                     .setInternalProviderId(Long.toString(program.getId()))
607                     .setContentId(program.getIntentUri().toString());
608             return builder.build();
609         }
610 
611         /** Appends query parameters to a Uri. */
addQueryParamToUri(Uri uri, Pair<String, String> param)612         public static Uri addQueryParamToUri(Uri uri, Pair<String, String> param) {
613             return uri.buildUpon().appendQueryParameter(param.first, param.second).build();
614         }
615     }
616 }
617