Home | History | Annotate | Download | only in tv
      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 androidx.tvprovider.media.tv;
     18 
     19 import android.content.ContentResolver;
     20 import android.content.Context;
     21 import android.database.sqlite.SQLiteException;
     22 import android.graphics.Bitmap;
     23 import android.graphics.BitmapFactory;
     24 import android.media.tv.TvContract;
     25 import android.net.Uri;
     26 import android.util.Log;
     27 
     28 import androidx.annotation.NonNull;
     29 import androidx.annotation.WorkerThread;
     30 
     31 import java.io.FileNotFoundException;
     32 import java.io.IOException;
     33 import java.io.InputStream;
     34 import java.io.OutputStream;
     35 import java.net.HttpURLConnection;
     36 import java.net.URL;
     37 import java.net.URLConnection;
     38 
     39 /** A utility class for conveniently storing and loading channel logos. */
     40 @WorkerThread
     41 public class ChannelLogoUtils {
     42     private static final String TAG = "ChannelLogoUtils";
     43 
     44     private static final int CONNECTION_TIMEOUT_MS_FOR_URLCONNECTION = 3000;  // 3 sec
     45     private static final int READ_TIMEOUT_MS_FOR_URLCONNECTION = 10000;  // 10 sec
     46 
     47     /**
     48      * Stores channel logo in the system content provider from the given URI. The method will try
     49      * to fetch the image file and decode it into {@link Bitmap}. Once the image is successfully
     50      * fetched, it will be stored in the system content provider and associated with the given
     51      * channel ID.
     52      *
     53      * <p>The URI provided to this method can be a URL referring to a image file residing in some
     54      * remote site/server, or a URI in one of the following formats:
     55      *
     56      *    <ul>
     57      *        <li>content ({@link android.content.ContentResolver#SCHEME_CONTENT})</li>
     58      *        <li>android.resource ({@link android.content.ContentResolver
     59      *                                     #SCHEME_ANDROID_RESOURCE})</li>
     60      *        <li>file ({@link android.content.ContentResolver#SCHEME_FILE})</li>
     61      *    </ul>
     62      *
     63      * <p>This method should be run in a worker thread since it may require network connection,
     64      * which will raise an exception if it's running in the main thread.
     65      *
     66      * @param context the context used to access the system content provider
     67      * @param channelId the ID of the target channel with which the fetched logo should be
     68      *                  associated
     69      * @param logoUri the {@link Uri} of the logo file to be fetched and stored in the system
     70      *                provider
     71      *
     72      * @return {@code true} if successfully fetched the image file referred by the give logo URI
     73      *         and stored it in the system content provider, or {@code false} if failed.
     74      *
     75      * @see #loadChannelLogo(Context, long)
     76      */
     77     public static boolean storeChannelLogo(@NonNull Context context, long channelId,
     78             @NonNull Uri logoUri) {
     79         String scheme = logoUri.normalizeScheme().getScheme();
     80         URLConnection urlConnection = null;
     81         InputStream inputStream = null;
     82         Bitmap fetchedLogo = null;
     83         try {
     84             if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme)
     85                     || ContentResolver.SCHEME_FILE.equals(scheme)
     86                     || ContentResolver.SCHEME_CONTENT.equals(scheme)) {
     87                 // A local resource
     88                 inputStream = context.getContentResolver().openInputStream(logoUri);
     89             } else {
     90                 // A remote resource, should be an valid URL.
     91                 urlConnection = getUrlConnection(logoUri.toString());
     92                 inputStream = urlConnection.getInputStream();
     93             }
     94             fetchedLogo = BitmapFactory.decodeStream(inputStream);
     95         } catch (IOException e) {
     96             Log.i(TAG, "Failed to get logo from the URI: " + logoUri + "\n", e);
     97         } finally {
     98             if (inputStream != null) {
     99                 try {
    100                     inputStream.close();
    101                 } catch (IOException e) {
    102                     // Do nothing.
    103                 }
    104             }
    105             if (urlConnection instanceof HttpURLConnection) {
    106                 ((HttpURLConnection) urlConnection).disconnect();
    107             }
    108         }
    109         return fetchedLogo != null && storeChannelLogo(context, channelId, fetchedLogo);
    110     }
    111 
    112     /**
    113      * Stores the given channel logo {@link Bitmap} in the system content provider and associate
    114      * it with the given channel ID.
    115      *
    116      * @param context the context used to access the system content provider
    117      * @param channelId the ID of the target channel with which the given logo should be associated
    118      * @param logo the logo image to be stored
    119      *
    120      * @return {@code true} if successfully stored the logo in the system content provider,
    121      *         otherwise {@code false}.
    122      *
    123      * @see #loadChannelLogo(Context, long)
    124      */
    125     public static boolean storeChannelLogo(@NonNull Context context, long channelId,
    126             @NonNull Bitmap logo) {
    127         boolean result = false;
    128         Uri localUri = TvContract.buildChannelLogoUri(channelId);
    129         try (OutputStream outputStream = context.getContentResolver().openOutputStream(localUri)) {
    130             result = logo.compress(Bitmap.CompressFormat.PNG, 100, outputStream);
    131             outputStream.flush();
    132         } catch (SQLiteException | IOException e) {
    133             Log.i(TAG, "Failed to store the logo to the system content provider.\n", e);
    134         }
    135         return result;
    136     }
    137 
    138     /**
    139      * A convenient helper method to get the channel logo associated to the given channel ID from
    140      * the system content provider.
    141      *
    142      * @param context the context used to access the system content provider
    143      * @param channelId the ID of the channel whose logo is supposed to be loaded
    144      *
    145      * @return the requested channel logo in {@link Bitmap}, or {@code null} if not available.
    146      *
    147      * @see #storeChannelLogo(Context, long, Uri)
    148      * @see #storeChannelLogo(Context, long, Bitmap)
    149      */
    150     public static Bitmap loadChannelLogo(@NonNull Context context, long channelId) {
    151         Bitmap channelLogo = null;
    152         try {
    153             channelLogo = BitmapFactory.decodeStream(context.getContentResolver().openInputStream(
    154                     TvContract.buildChannelLogoUri(channelId)));
    155         } catch (FileNotFoundException e) {
    156             // Channel logo is not found in the content provider.
    157             Log.i(TAG, "Channel logo for channel (ID:" + channelId + ") not found.", e);
    158         }
    159         return channelLogo;
    160     }
    161 
    162     private static URLConnection getUrlConnection(String uriString) throws IOException {
    163         URLConnection urlConnection = new URL(uriString).openConnection();
    164         urlConnection.setConnectTimeout(CONNECTION_TIMEOUT_MS_FOR_URLCONNECTION);
    165         urlConnection.setReadTimeout(READ_TIMEOUT_MS_FOR_URLCONNECTION);
    166         return urlConnection;
    167     }
    168 
    169     /** @deprecated This type should not be instantiated as it contains only static methods. */
    170     @Deprecated
    171     @SuppressWarnings("PrivateConstructorForUtilityClass")
    172     public ChannelLogoUtils() {
    173     }
    174 }
    175