Home | History | Annotate | Download | only in menu
      1 /*
      2  * Copyright (C) 2015 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.tv.menu;
     18 
     19 import android.content.Context;
     20 import android.os.Handler;
     21 import android.os.Looper;
     22 import android.os.Message;
     23 import android.support.annotation.MainThread;
     24 import android.support.annotation.NonNull;
     25 import android.util.Log;
     26 
     27 import com.android.tv.R;
     28 import com.android.tv.common.SoftPreconditions;
     29 import com.android.tv.common.WeakHandler;
     30 import com.android.tv.data.Channel;
     31 import com.android.tv.data.Program;
     32 import com.android.tv.data.ProgramDataManager;
     33 
     34 import java.util.List;
     35 
     36 /**
     37  * A poster image prefetcher to show the program poster art in the Channels row faster.
     38  */
     39 public class ChannelsPosterPrefetcher {
     40     private static final String TAG = "PosterPrefetcher";
     41     private static final boolean DEBUG = false;
     42     private static final int MSG_PREFETCH_IMAGE = 1000;
     43     private static final int ONDEMAND_POSTER_PREFETCH_DELAY_MILLIS = 500;  // 500 milliseconds
     44 
     45     private final ProgramDataManager mProgramDataManager;
     46     private final ChannelsRowAdapter mChannelsAdapter;
     47     private final int mPosterArtWidth;
     48     private final int mPosterArtHeight;
     49     private final Context mContext;
     50     private final Handler mHandler = new PrefetchHandler(this);
     51 
     52     private boolean isCanceled;
     53 
     54     /**
     55      * Create {@link ChannelsPosterPrefetcher} object with given parameters.
     56      */
     57     public ChannelsPosterPrefetcher(Context context, ProgramDataManager programDataManager,
     58             ChannelsRowAdapter adapter) {
     59         mProgramDataManager = programDataManager;
     60         mChannelsAdapter = adapter;
     61         mPosterArtWidth = context.getResources().getDimensionPixelSize(
     62                 R.dimen.card_image_layout_width);
     63         mPosterArtHeight = context.getResources().getDimensionPixelSize(
     64                 R.dimen.card_image_layout_height);
     65         mContext = context.getApplicationContext();
     66     }
     67 
     68     /**
     69      * Start prefetching of program poster art of recommendation.
     70      */
     71     public void prefetch() {
     72         SoftPreconditions.checkState(!isCanceled, TAG, "Prefetch called after cancel was called.");
     73         if (isCanceled) {
     74             return;
     75         }
     76         if (DEBUG) Log.d(TAG, "startPrefetching()");
     77         /*
     78          * When a user browse channels, this method could be called many times. We don't need to
     79          * prefetch the intermediate channels. So ignore previous schedule.
     80          */
     81         mHandler.removeMessages(MSG_PREFETCH_IMAGE);
     82         mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_PREFETCH_IMAGE),
     83                 ONDEMAND_POSTER_PREFETCH_DELAY_MILLIS);
     84     }
     85 
     86     /**
     87      * Cancels pending and current prefetch requests.
     88      */
     89     public void cancel() {
     90         isCanceled = true;
     91         mHandler.removeCallbacksAndMessages(null);
     92     }
     93 
     94     @MainThread // ProgramDataManager.getCurrentProgram must be called from the main thread
     95     private void doPrefetchImages() {
     96         if (DEBUG) Log.d(TAG, "doPrefetchImages() started");
     97 
     98         // This executes on the main thread, but since the item list is expected to be about 5 items
     99         // and ImageLoader spawns an async task so this is fast enough. 1 ms in local testing.
    100         List<Channel> channelList = mChannelsAdapter.getItemList();
    101         if (channelList != null) {
    102             for (Channel channel : channelList) {
    103                 if (isCanceled) {
    104                     return;
    105                 }
    106                 if (!Channel.isValid(channel)) {
    107                     continue;
    108                 }
    109                 channel.prefetchImage(mContext, Channel.LOAD_IMAGE_TYPE_CHANNEL_LOGO,
    110                         mPosterArtWidth, mPosterArtHeight);
    111                 Program program = mProgramDataManager.getCurrentProgram(channel.getId());
    112                 if (program != null) {
    113                     program.prefetchPosterArt(mContext, mPosterArtWidth, mPosterArtHeight);
    114                 }
    115             }
    116         }
    117         if (DEBUG) {
    118             Log.d(TAG, "doPrefetchImages() finished. ImageLoader may still have async tasks for "
    119                             + "channels " + channelList);
    120         }
    121     }
    122 
    123     private static class PrefetchHandler extends WeakHandler<ChannelsPosterPrefetcher> {
    124         public PrefetchHandler(ChannelsPosterPrefetcher ref) {
    125             // doPrefetchImages must be called from the main thread.
    126             super(Looper.getMainLooper(), ref);
    127         }
    128 
    129         @Override
    130         @MainThread
    131         public void handleMessage(Message msg, @NonNull ChannelsPosterPrefetcher prefetcher) {
    132             switch (msg.what) {
    133                 case MSG_PREFETCH_IMAGE:
    134                     prefetcher.doPrefetchImages();
    135                     break;
    136             }
    137         }
    138     }
    139 }
    140