/* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.wallpaper.asset; import android.annotation.TargetApi; import android.app.Activity; import android.app.WallpaperManager; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Point; import android.graphics.Rect; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.os.AsyncTask; import android.os.Build; import android.os.Build.VERSION; import android.os.Build.VERSION_CODES; import android.widget.ImageView; import com.bumptech.glide.Glide; import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions; import com.bumptech.glide.request.RequestOptions; /** * Asset representing the system's built-in wallpaper. * NOTE: This is only used for KitKat and newer devices. On older versions of Android, the * built-in wallpaper is accessed via the system Resources object, and is thus be represented * by a {@code ResourceAsset} instead. */ @TargetApi(Build.VERSION_CODES.KITKAT) public final class BuiltInWallpaperAsset extends Asset { private static final boolean SCALE_TO_FIT = true; private static final boolean CROP_TO_FIT = false; private static final float HORIZONTAL_CENTER_ALIGNED = 0.5f; private static final float VERTICAL_CENTER_ALIGNED = 0.5f; private final Context mContext; private Point mDimensions; private WallpaperModel mBuiltInWallpaperModel; /** * @param context The application's context. */ public BuiltInWallpaperAsset(Context context) { if (VERSION.SDK_INT < VERSION_CODES.KITKAT) { throw new AssertionError("BuiltInWallpaperAsset should not be instantiated on a pre-KitKat" + " build"); } mContext = context.getApplicationContext(); } @Override public void decodeBitmapRegion(Rect rect, int targetWidth, int targetHeight, boolean shouldAdjustForRtl, BitmapReceiver receiver) { DecodeBitmapRegionAsyncTask task = new DecodeBitmapRegionAsyncTask(rect, receiver); task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } @Override public void decodeRawDimensions(Activity unused, DimensionsReceiver receiver) { DecodeDimensionsAsyncTask task = new DecodeDimensionsAsyncTask(receiver); task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } @Override public void decodeBitmap(int targetWidth, int targetHeight, BitmapReceiver receiver) { DecodeBitmapAsyncTask task = new DecodeBitmapAsyncTask(targetWidth, targetHeight, receiver); task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } @Override public boolean supportsTiling() { return false; } /** * Calculates the raw dimensions of the built-in drawable. This method should not be called from * the main UI thread. * * @return Raw dimensions of the built-in wallpaper drawable. */ private Point calculateRawDimensions() { if (mDimensions != null) { return mDimensions; } Drawable builtInDrawable = WallpaperManager.getInstance(mContext).getBuiltInDrawable(); Bitmap builtInBitmap = ((BitmapDrawable) builtInDrawable).getBitmap(); mDimensions = new Point(builtInBitmap.getWidth(), builtInBitmap.getHeight()); return mDimensions; } @Override public void loadDrawable(Context context, ImageView imageView, int placeholderColor) { if (mBuiltInWallpaperModel == null) { mBuiltInWallpaperModel = new WallpaperModel(context.getApplicationContext(), WallpaperModel.SOURCE_BUILT_IN); } Glide.with(context) .asDrawable() .load(mBuiltInWallpaperModel) .apply(RequestOptions.centerCropTransform() .placeholder(new ColorDrawable(placeholderColor))) .transition(DrawableTransitionOptions.withCrossFade()) .into(imageView); } /** * AsyncTask subclass which decodes the full built-in wallpaper bitmap off the UI thread. */ private class DecodeBitmapAsyncTask extends AsyncTask { private int mWidth; private int mHeight; private BitmapReceiver mReceiver; public DecodeBitmapAsyncTask(int width, int height, BitmapReceiver receiver) { mWidth = width; mHeight = height; mReceiver = receiver; } @Override protected Bitmap doInBackground(Void... unused) { final WallpaperManager wallpaperManager = WallpaperManager.getInstance(mContext); Drawable drawable = wallpaperManager.getBuiltInDrawable( mWidth, mHeight, SCALE_TO_FIT, HORIZONTAL_CENTER_ALIGNED, VERTICAL_CENTER_ALIGNED); // Manually request that WallpaperManager loses its reference to the built-in wallpaper // bitmap, which can occupy a large memory allocation for the lifetime of the app. wallpaperManager.forgetLoadedWallpaper(); return ((BitmapDrawable) drawable).getBitmap(); } @Override protected void onPostExecute(Bitmap bitmap) { mReceiver.onBitmapDecoded(bitmap); } } /** * AsyncTask subclass which decodes a bitmap region off the UI thread. */ private class DecodeBitmapRegionAsyncTask extends AsyncTask { private Rect mRect; private BitmapReceiver mReceiver; public DecodeBitmapRegionAsyncTask(Rect rect, BitmapReceiver receiver) { mRect = rect; mReceiver = receiver; } @Override protected Bitmap doInBackground(Void... unused) { Point dimensions = calculateRawDimensions(); float horizontalCenter = BitmapUtils.calculateHorizontalAlignment(dimensions, mRect); float verticalCenter = BitmapUtils.calculateVerticalAlignment(dimensions, mRect); Drawable drawable = WallpaperManager.getInstance(mContext).getBuiltInDrawable( mRect.width(), mRect.height(), CROP_TO_FIT, horizontalCenter, verticalCenter); return ((BitmapDrawable) drawable).getBitmap(); } @Override protected void onPostExecute(Bitmap bitmap) { mReceiver.onBitmapDecoded(bitmap); } } /** * AsyncTask subclass which decodes the raw dimensions of the built-in wallpaper drawable off the * main UI thread. */ private class DecodeDimensionsAsyncTask extends AsyncTask { private DimensionsReceiver mReceiver; public DecodeDimensionsAsyncTask(DimensionsReceiver receiver) { mReceiver = receiver; } @Override protected Point doInBackground(Void... unused) { return calculateRawDimensions(); } @Override protected void onPostExecute(Point dimensions) { mReceiver.onDimensionsDecoded(dimensions); } } }