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 package com.android.wallpaper.asset; 17 18 import android.annotation.TargetApi; 19 import android.app.Activity; 20 import android.app.WallpaperManager; 21 import android.content.Context; 22 import android.graphics.Bitmap; 23 import android.graphics.Point; 24 import android.graphics.Rect; 25 import android.graphics.drawable.BitmapDrawable; 26 import android.graphics.drawable.ColorDrawable; 27 import android.graphics.drawable.Drawable; 28 import android.os.AsyncTask; 29 import android.os.Build; 30 import android.os.Build.VERSION; 31 import android.os.Build.VERSION_CODES; 32 import android.widget.ImageView; 33 34 import com.bumptech.glide.Glide; 35 import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions; 36 import com.bumptech.glide.request.RequestOptions; 37 38 /** 39 * Asset representing the system's built-in wallpaper. 40 * NOTE: This is only used for KitKat and newer devices. On older versions of Android, the 41 * built-in wallpaper is accessed via the system Resources object, and is thus be represented 42 * by a {@code ResourceAsset} instead. 43 */ 44 @TargetApi(Build.VERSION_CODES.KITKAT) 45 public final class BuiltInWallpaperAsset extends Asset { 46 private static final boolean SCALE_TO_FIT = true; 47 private static final boolean CROP_TO_FIT = false; 48 private static final float HORIZONTAL_CENTER_ALIGNED = 0.5f; 49 private static final float VERTICAL_CENTER_ALIGNED = 0.5f; 50 51 private final Context mContext; 52 53 private Point mDimensions; 54 private WallpaperModel mBuiltInWallpaperModel; 55 56 /** 57 * @param context The application's context. 58 */ BuiltInWallpaperAsset(Context context)59 public BuiltInWallpaperAsset(Context context) { 60 if (VERSION.SDK_INT < VERSION_CODES.KITKAT) { 61 throw new AssertionError("BuiltInWallpaperAsset should not be instantiated on a pre-KitKat" 62 + " build"); 63 } 64 65 mContext = context.getApplicationContext(); 66 } 67 68 @Override decodeBitmapRegion(Rect rect, int targetWidth, int targetHeight, boolean shouldAdjustForRtl, BitmapReceiver receiver)69 public void decodeBitmapRegion(Rect rect, int targetWidth, int targetHeight, 70 boolean shouldAdjustForRtl, BitmapReceiver receiver) { 71 DecodeBitmapRegionAsyncTask task = new DecodeBitmapRegionAsyncTask(rect, receiver); 72 task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 73 } 74 75 @Override decodeRawDimensions(Activity unused, DimensionsReceiver receiver)76 public void decodeRawDimensions(Activity unused, DimensionsReceiver receiver) { 77 DecodeDimensionsAsyncTask task = new DecodeDimensionsAsyncTask(receiver); 78 task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 79 } 80 81 @Override decodeBitmap(int targetWidth, int targetHeight, BitmapReceiver receiver)82 public void decodeBitmap(int targetWidth, int targetHeight, 83 BitmapReceiver receiver) { 84 DecodeBitmapAsyncTask task = new DecodeBitmapAsyncTask(targetWidth, targetHeight, receiver); 85 task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 86 } 87 88 @Override supportsTiling()89 public boolean supportsTiling() { 90 return false; 91 } 92 93 /** 94 * Calculates the raw dimensions of the built-in drawable. This method should not be called from 95 * the main UI thread. 96 * 97 * @return Raw dimensions of the built-in wallpaper drawable. 98 */ calculateRawDimensions()99 private Point calculateRawDimensions() { 100 if (mDimensions != null) { 101 return mDimensions; 102 } 103 104 Drawable builtInDrawable = WallpaperManager.getInstance(mContext).getBuiltInDrawable(); 105 Bitmap builtInBitmap = ((BitmapDrawable) builtInDrawable).getBitmap(); 106 mDimensions = new Point(builtInBitmap.getWidth(), builtInBitmap.getHeight()); 107 return mDimensions; 108 } 109 110 @Override loadDrawable(Context context, ImageView imageView, int placeholderColor)111 public void loadDrawable(Context context, ImageView imageView, int placeholderColor) { 112 if (mBuiltInWallpaperModel == null) { 113 mBuiltInWallpaperModel = 114 new WallpaperModel(context.getApplicationContext(), WallpaperModel.SOURCE_BUILT_IN); 115 } 116 117 Glide.with(context) 118 .asDrawable() 119 .load(mBuiltInWallpaperModel) 120 .apply(RequestOptions.centerCropTransform() 121 .placeholder(new ColorDrawable(placeholderColor))) 122 .transition(DrawableTransitionOptions.withCrossFade()) 123 .into(imageView); 124 } 125 126 /** 127 * AsyncTask subclass which decodes the full built-in wallpaper bitmap off the UI thread. 128 */ 129 private class DecodeBitmapAsyncTask extends AsyncTask<Void, Void, Bitmap> { 130 131 private int mWidth; 132 private int mHeight; 133 private BitmapReceiver mReceiver; 134 DecodeBitmapAsyncTask(int width, int height, BitmapReceiver receiver)135 public DecodeBitmapAsyncTask(int width, int height, BitmapReceiver receiver) { 136 mWidth = width; 137 mHeight = height; 138 mReceiver = receiver; 139 } 140 141 @Override doInBackground(Void... unused)142 protected Bitmap doInBackground(Void... unused) { 143 final WallpaperManager wallpaperManager = WallpaperManager.getInstance(mContext); 144 145 Drawable drawable = wallpaperManager.getBuiltInDrawable( 146 mWidth, 147 mHeight, 148 SCALE_TO_FIT, 149 HORIZONTAL_CENTER_ALIGNED, 150 VERTICAL_CENTER_ALIGNED); 151 152 // Manually request that WallpaperManager loses its reference to the built-in wallpaper 153 // bitmap, which can occupy a large memory allocation for the lifetime of the app. 154 wallpaperManager.forgetLoadedWallpaper(); 155 156 return ((BitmapDrawable) drawable).getBitmap(); 157 } 158 159 @Override onPostExecute(Bitmap bitmap)160 protected void onPostExecute(Bitmap bitmap) { 161 mReceiver.onBitmapDecoded(bitmap); 162 } 163 } 164 165 /** 166 * AsyncTask subclass which decodes a bitmap region off the UI thread. 167 */ 168 private class DecodeBitmapRegionAsyncTask extends AsyncTask<Void, Void, Bitmap> { 169 170 private Rect mRect; 171 private BitmapReceiver mReceiver; 172 DecodeBitmapRegionAsyncTask(Rect rect, BitmapReceiver receiver)173 public DecodeBitmapRegionAsyncTask(Rect rect, BitmapReceiver receiver) { 174 mRect = rect; 175 mReceiver = receiver; 176 } 177 178 @Override doInBackground(Void... unused)179 protected Bitmap doInBackground(Void... unused) { 180 Point dimensions = calculateRawDimensions(); 181 182 float horizontalCenter = BitmapUtils.calculateHorizontalAlignment(dimensions, mRect); 183 float verticalCenter = BitmapUtils.calculateVerticalAlignment(dimensions, mRect); 184 185 Drawable drawable = WallpaperManager.getInstance(mContext).getBuiltInDrawable( 186 mRect.width(), 187 mRect.height(), 188 CROP_TO_FIT, 189 horizontalCenter, 190 verticalCenter); 191 192 return ((BitmapDrawable) drawable).getBitmap(); 193 } 194 195 @Override onPostExecute(Bitmap bitmap)196 protected void onPostExecute(Bitmap bitmap) { 197 mReceiver.onBitmapDecoded(bitmap); 198 } 199 } 200 201 /** 202 * AsyncTask subclass which decodes the raw dimensions of the built-in wallpaper drawable off the 203 * main UI thread. 204 */ 205 private class DecodeDimensionsAsyncTask extends AsyncTask<Void, Void, Point> { 206 private DimensionsReceiver mReceiver; 207 DecodeDimensionsAsyncTask(DimensionsReceiver receiver)208 public DecodeDimensionsAsyncTask(DimensionsReceiver receiver) { 209 mReceiver = receiver; 210 } 211 212 @Override doInBackground(Void... unused)213 protected Point doInBackground(Void... unused) { 214 return calculateRawDimensions(); 215 } 216 217 @Override onPostExecute(Point dimensions)218 protected void onPostExecute(Point dimensions) { 219 mReceiver.onDimensionsDecoded(dimensions); 220 } 221 } 222 } 223