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