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