package com.battlelancer.seriesguide.thetvdbapi; import android.content.Context; import android.os.Build; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.text.TextUtils; import android.util.Base64; import android.widget.ImageView; import com.battlelancer.seriesguide.BuildConfig; import com.battlelancer.seriesguide.R; import com.battlelancer.seriesguide.util.ServiceUtils; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import timber.log.Timber; public class TvdbImageTools { private static final String TVDB_MIRROR_BANNERS = "http://thetvdb.com/banners/"; private static final String TVDB_MIRROR_BANNERS_CACHE = TVDB_MIRROR_BANNERS + "_cache/"; private static Mac sha256_hmac; // prevent init private TvdbImageTools() { } /** * Builds a full size url for a TVDb poster or screenshot (episode still) using the given image * path. * * <p>Posters probably should use {@link #smallSizeUrl(String)} which downloads a much smaller * version. */ @Nullable public static String fullSizeUrl(@Nullable String imagePath) { if (TextUtils.isEmpty(imagePath)) { return null; } else { return buildImageCacheUrl(TVDB_MIRROR_BANNERS + imagePath); } } /** * Builds a full url for a TVDb show poster using the given image path. */ @Nullable public static String smallSizeUrl(@Nullable String imagePath) { if (TextUtils.isEmpty(imagePath)) { return null; } else { return buildImageCacheUrl(TVDB_MIRROR_BANNERS_CACHE + imagePath); } } /** * Builds a full url for a TVDb show poster using the given image path. * * @param imagePath If empty, will return the URL of the first uploaded poster. */ @Nullable public static String smallSizeOrFirstUrl(@Nullable String imagePath, int showTvdbId) { if (TextUtils.isEmpty(imagePath)) { imagePath = firstPosterPath(showTvdbId); } return smallSizeUrl(imagePath); } /** * Builds a fall-back path for a TVDb show poster using the TVDB id, equals the first image * uploaded. */ @NonNull public static String firstPosterPath(int showTvdbId) { return "posters/" + showTvdbId + "-1.jpg"; } /** * Tries to load the given TVDb show poster into the given {@link ImageView} * without any resizing or cropping. */ public static void loadShowPoster(Context context, ImageView imageView, String posterPath) { ServiceUtils.loadWithPicasso(context, smallSizeUrl(posterPath)) .noFade() .into(imageView); } /** * Tries to load the given TVDb show poster into the given {@link ImageView} without any * resizing or cropping. In addition sets alpha on the view. */ public static void loadShowPosterAlpha(Context context, ImageView imageView, String posterPath) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { imageView.setImageAlpha(30); } else { //noinspection deprecation imageView.setAlpha(30); } loadShowPoster(context, imageView, posterPath); } /** * Tries to load a resized, center cropped version of the show poster into the given {@link * ImageView}. On failure displays an error drawable (ensure image view is set to center * inside). * * <p>The resize dimensions are those used for posters in the show list and change depending on * screen size. */ public static void loadShowPosterResizeCrop(Context context, ImageView imageView, String posterPath) { ServiceUtils.loadWithPicasso(context, smallSizeUrl(posterPath)) .resizeDimen(R.dimen.show_poster_width, R.dimen.show_poster_height) .centerCrop() .error(R.drawable.ic_image_missing) .into(imageView); } /** * Tries to load a resized, center cropped version of the show poster into the given {@link * ImageView}. On failure displays an error drawable (ensure image view is set to center * inside). * * <p>The resize dimensions are fixed for all screen sizes. Like for items using the show list * layout, use {@link TvdbImageTools#loadShowPosterResizeCrop(Context, ImageView, String)}. * * @param posterUrl This should already be a built TVDB poster URL, not just a poster path! */ public static void loadShowPosterResizeSmallCrop(Context context, ImageView imageView, String posterUrl) { ServiceUtils.loadWithPicasso(context, posterUrl) .resizeDimen(R.dimen.show_poster_width_default, R.dimen.show_poster_height_default) .centerCrop() .error(R.drawable.ic_image_missing) .into(imageView); } /** * Tries to load a resized, center cropped version of the show poster into the given {@link * ImageView}. On failure displays an error drawable (ensure image view is set to center * inside). * * <p>The resize dimensions are determined based on the image view size. */ public static void loadShowPosterFitCrop(Context context, ImageView imageView, String posterPath) { ServiceUtils.loadWithPicasso(context, smallSizeUrl(posterPath)) .fit() .centerCrop() .error(R.drawable.ic_image_missing) .into(imageView); } /** * @param posterUrlTvdb Expected to be not empty. */ @Nullable private static String buildImageCacheUrl(@NonNull String posterUrlTvdb) { String mac = encodeImageUrl(BuildConfig.IMAGE_CACHE_SECRET, posterUrlTvdb); if (mac != null) { return String.format("%s/s%s/%s", BuildConfig.IMAGE_CACHE_URL, mac, posterUrlTvdb); } else { return null; } } @Nullable private static synchronized String encodeImageUrl(@NonNull String key, @NonNull String data) { try { if (sha256_hmac == null) { sha256_hmac = Mac.getInstance("HmacSHA256"); SecretKeySpec secret_key = new SecretKeySpec(key.getBytes(), "HmacSHA256"); sha256_hmac.init(secret_key); } return Base64.encodeToString(sha256_hmac.doFinal(data.getBytes()), Base64.NO_WRAP | Base64.URL_SAFE); } catch (NoSuchAlgorithmException | InvalidKeyException e) { Timber.e(e, "Signing image URL failed."); return null; } } }