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