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