summaryrefslogtreecommitdiffstats
path: root/src/com/android/launcher3/compat
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/launcher3/compat')
-rw-r--r--src/com/android/launcher3/compat/WallpaperColorsCompat.java43
-rw-r--r--src/com/android/launcher3/compat/WallpaperManagerCompat.java61
-rw-r--r--src/com/android/launcher3/compat/WallpaperManagerCompatVL.java236
-rw-r--r--src/com/android/launcher3/compat/WallpaperManagerCompatVOMR1.java109
4 files changed, 449 insertions, 0 deletions
diff --git a/src/com/android/launcher3/compat/WallpaperColorsCompat.java b/src/com/android/launcher3/compat/WallpaperColorsCompat.java
new file mode 100644
index 000000000..fd08f947e
--- /dev/null
+++ b/src/com/android/launcher3/compat/WallpaperColorsCompat.java
@@ -0,0 +1,43 @@
+/*
+ * 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.launcher3.compat;
+
+import android.util.SparseIntArray;
+
+/**
+ * A compatibility layer around platform implementation of WallpaperColors
+ */
+public class WallpaperColorsCompat {
+
+ private final SparseIntArray mColors;
+ private final boolean mSupportsDarkText;
+
+ public WallpaperColorsCompat(SparseIntArray colors, boolean supportsDarkText) {
+ mColors = colors;
+ mSupportsDarkText = supportsDarkText;
+ }
+
+ /**
+ * A map of color code to their occurrences. The bigger the int, the more relevant the color.
+ */
+ public SparseIntArray getColors() {
+ return mColors;
+ }
+
+ public boolean supportsDarkText() {
+ return mSupportsDarkText;
+ }
+}
diff --git a/src/com/android/launcher3/compat/WallpaperManagerCompat.java b/src/com/android/launcher3/compat/WallpaperManagerCompat.java
new file mode 100644
index 000000000..cbcabdf9b
--- /dev/null
+++ b/src/com/android/launcher3/compat/WallpaperManagerCompat.java
@@ -0,0 +1,61 @@
+/*
+ * 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.launcher3.compat;
+
+import android.content.Context;
+import android.support.annotation.Nullable;
+
+import com.android.launcher3.Utilities;
+
+public abstract class WallpaperManagerCompat {
+
+ private static final Object sInstanceLock = new Object();
+ private static WallpaperManagerCompat sInstance;
+
+ public static WallpaperManagerCompat getInstance(Context context) {
+ synchronized (sInstanceLock) {
+ if (sInstance == null) {
+ context = context.getApplicationContext();
+
+ if (Utilities.isAtLeastO()) {
+ try {
+ sInstance = new WallpaperManagerCompatVOMR1(context);
+ } catch (Exception e) {
+ // The wallpaper APIs do not yet exist
+ }
+ }
+ if (sInstance == null) {
+ sInstance = new WallpaperManagerCompatVL(context);
+ }
+ }
+ return sInstance;
+ }
+ }
+
+
+ public abstract @Nullable WallpaperColorsCompat getWallpaperColors(int which);
+
+ public abstract void addOnColorsChangedListener(OnColorsChangedListenerCompat listener);
+
+ /**
+ * Interface definition for a callback to be invoked when colors change on a wallpaper.
+ */
+ public interface OnColorsChangedListenerCompat {
+
+ void onColorsChanged(WallpaperColorsCompat colors, int which);
+ }
+}
diff --git a/src/com/android/launcher3/compat/WallpaperManagerCompatVL.java b/src/com/android/launcher3/compat/WallpaperManagerCompatVL.java
new file mode 100644
index 000000000..b175f2113
--- /dev/null
+++ b/src/com/android/launcher3/compat/WallpaperManagerCompatVL.java
@@ -0,0 +1,236 @@
+/*
+ * 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.launcher3.compat;
+
+import static android.app.WallpaperManager.FLAG_SYSTEM;
+
+import android.app.IntentService;
+import android.app.WallpaperInfo;
+import android.app.WallpaperManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.BitmapRegionDecoder;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.ParcelFileDescriptor;
+import android.os.ResultReceiver;
+import android.support.annotation.Nullable;
+import android.support.v7.graphics.Palette;
+import android.util.Log;
+import android.util.Pair;
+import android.util.SparseIntArray;
+
+import com.android.launcher3.LauncherFiles;
+import com.android.launcher3.Utilities;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+public class WallpaperManagerCompatVL extends WallpaperManagerCompat {
+
+ private static final String TAG = "WMCompatVL";
+
+ private static final String VERSION_PREFIX = "1,";
+ private static final String KEY_COLORS = "wallpaper_parsed_colors";
+ private static final String EXTRA_RECEIVER = "receiver";
+
+ private final ArrayList<OnColorsChangedListenerCompat> mListeners = new ArrayList<>();
+
+ private final Context mContext;
+ private WallpaperColorsCompat mColorsCompat;
+
+ WallpaperManagerCompatVL(Context context) {
+ mContext = context;
+
+ String colors = prefs(mContext).getString(KEY_COLORS, "");
+ int wallpaperId = -1;
+ if (colors.startsWith(VERSION_PREFIX)) {
+ Pair<Integer, WallpaperColorsCompat> storedValue = parseValue(colors);
+ wallpaperId = storedValue.first;
+ mColorsCompat = storedValue.second;
+ }
+
+ if (wallpaperId == -1 || wallpaperId != getWallpaperId(context)) {
+ reloadColors();
+ }
+ context.registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ reloadColors();
+ }
+ }, new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED));
+ }
+
+ @Nullable
+ @Override
+ public WallpaperColorsCompat getWallpaperColors(int which) {
+ return which == FLAG_SYSTEM ? mColorsCompat : null;
+ }
+
+ @Override
+ public void addOnColorsChangedListener(OnColorsChangedListenerCompat listener) {
+ mListeners.add(listener);
+ }
+
+ private void reloadColors() {
+ ResultReceiver receiver = new ResultReceiver(new Handler()) {
+
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ handleResult(resultData.getString(KEY_COLORS));
+ }
+ };
+ mContext.startService(new Intent(mContext, ColorExtractionService.class)
+ .putExtra(EXTRA_RECEIVER, receiver));
+ }
+
+ private void handleResult(String result) {
+ prefs(mContext).edit().putString(KEY_COLORS, result).apply();
+ mColorsCompat = parseValue(result).second;
+ for (OnColorsChangedListenerCompat listener : mListeners) {
+ listener.onColorsChanged(mColorsCompat, FLAG_SYSTEM);
+ }
+ }
+
+ private static SharedPreferences prefs(Context context) {
+ return context.getSharedPreferences(
+ LauncherFiles.DEVICE_PREFERENCES_KEY, Context.MODE_PRIVATE);
+ }
+
+ private static final int getWallpaperId(Context context) {
+ if (!Utilities.ATLEAST_NOUGAT) {
+ return -1;
+ }
+ return context.getSystemService(WallpaperManager.class).getWallpaperId(FLAG_SYSTEM);
+ }
+
+ /**
+ * Parses the stored value and returns the wallpaper id and wallpaper colors.
+ */
+ private static Pair<Integer, WallpaperColorsCompat> parseValue(String value) {
+ String[] parts = value.split(",");
+ Integer wallpaperId = Integer.parseInt(parts[1]);
+ if (parts.length == 2) {
+ return Pair.create(wallpaperId, null);
+ }
+
+ SparseIntArray colors = new SparseIntArray((parts.length - 2) / 2);
+ for (int i = 2; i < parts.length; i += 2) {
+ colors.put(Integer.parseInt(parts[i]), Integer.parseInt(parts[i + 1]));
+ }
+ return Pair.create(wallpaperId, new WallpaperColorsCompat(colors, false));
+ }
+
+ /**
+ * Intent service to handle color extraction
+ */
+ public static class ColorExtractionService extends IntentService {
+ private static final int MAX_WALLPAPER_EXTRACTION_AREA = 112 * 112;
+
+ public ColorExtractionService() {
+ super("ColorExtractionService");
+ }
+
+ /**
+ * Extracts the wallpaper colors and sends the result back through the receiver.
+ */
+ @Override
+ protected void onHandleIntent(@Nullable Intent intent) {
+ int wallpaperId = getWallpaperId(this);
+
+ Bitmap bitmap = null;
+ Drawable drawable = null;
+
+ WallpaperManager wm = WallpaperManager.getInstance(this);
+ WallpaperInfo info = wm.getWallpaperInfo();
+ if (info != null) {
+ // For live wallpaper, extract colors from thumbnail
+ drawable = info.loadThumbnail(getPackageManager());
+ } else {
+ if (Utilities.ATLEAST_NOUGAT) {
+ try (ParcelFileDescriptor fd = wm.getWallpaperFile(FLAG_SYSTEM)) {
+ BitmapRegionDecoder decoder = BitmapRegionDecoder
+ .newInstance(fd.getFileDescriptor(), false);
+
+ int requestedArea = decoder.getWidth() * decoder.getHeight();
+ BitmapFactory.Options options = new BitmapFactory.Options();
+
+ if (requestedArea > MAX_WALLPAPER_EXTRACTION_AREA) {
+ double areaRatio =
+ MAX_WALLPAPER_EXTRACTION_AREA / (double) requestedArea;
+ double nearestPowOf2 =
+ Math.floor(Math.log(areaRatio) / (2 * Math.log(2)));
+ options.inSampleSize = (int) Math.pow(2, nearestPowOf2);
+ }
+ Rect region = new Rect(0, 0, decoder.getWidth(), decoder.getHeight());
+ bitmap = decoder.decodeRegion(region, options);
+ decoder.recycle();
+ } catch (IOException | NullPointerException e) {
+ Log.e(TAG, "Fetching partial bitmap failed, trying old method", e);
+ }
+ }
+ if (bitmap == null) {
+ drawable = wm.getDrawable();
+ }
+ }
+
+ if (drawable != null) {
+ // Calculate how big the bitmap needs to be.
+ // This avoids unnecessary processing and allocation inside Palette.
+ final int requestedArea = drawable.getIntrinsicWidth() *
+ drawable.getIntrinsicHeight();
+ double scale = 1;
+ if (requestedArea > MAX_WALLPAPER_EXTRACTION_AREA) {
+ scale = Math.sqrt(MAX_WALLPAPER_EXTRACTION_AREA / (double) requestedArea);
+ }
+ bitmap = Bitmap.createBitmap((int) (drawable.getIntrinsicWidth() * scale),
+ (int) (drawable.getIntrinsicHeight() * scale), Bitmap.Config.ARGB_8888);
+ final Canvas bmpCanvas = new Canvas(bitmap);
+ drawable.setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight());
+ drawable.draw(bmpCanvas);
+ }
+
+ String value = VERSION_PREFIX + wallpaperId;
+
+ if (bitmap != null) {
+ Palette palette = Palette.from(bitmap).generate();
+ bitmap.recycle();
+
+ StringBuilder builder = new StringBuilder(value);
+ for (Palette.Swatch swatch : palette.getSwatches()) {
+ builder.append(',')
+ .append(swatch.getRgb())
+ .append(',')
+ .append(swatch.getPopulation());
+ }
+ value = builder.toString();
+ }
+
+ ResultReceiver receiver = intent.getParcelableExtra(EXTRA_RECEIVER);
+ Bundle result = new Bundle();
+ result.putString(KEY_COLORS, value);
+ receiver.send(0, result);
+ }
+ }
+}
diff --git a/src/com/android/launcher3/compat/WallpaperManagerCompatVOMR1.java b/src/com/android/launcher3/compat/WallpaperManagerCompatVOMR1.java
new file mode 100644
index 000000000..c74ccc0cc
--- /dev/null
+++ b/src/com/android/launcher3/compat/WallpaperManagerCompatVOMR1.java
@@ -0,0 +1,109 @@
+/*
+ * 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.launcher3.compat;
+
+import android.annotation.TargetApi;
+import android.app.WallpaperManager;
+import android.content.Context;
+import android.graphics.Color;
+import android.os.Build;
+import android.support.annotation.Nullable;
+import android.util.Log;
+import android.util.Pair;
+import android.util.SparseIntArray;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.List;
+
+@TargetApi(Build.VERSION_CODES.O)
+public class WallpaperManagerCompatVOMR1 extends WallpaperManagerCompat {
+
+ private static final String TAG = "WMCompatVOMR1";
+
+ private final WallpaperManager mWm;
+
+ private final Class mOCLClass;
+ private final Method mAddOCLMethod;
+
+ private final Method mWCGetMethod;
+ private final Method mWCGetColorsMethod;
+ private final Method mWCSupportsDarkTextMethod;
+
+ WallpaperManagerCompatVOMR1(Context context) throws Exception {
+ mWm = context.getSystemService(WallpaperManager.class);
+
+ mOCLClass = Class.forName("android.app.WallpaperManager$OnColorsChangedListener");
+ mAddOCLMethod = WallpaperManager.class.getDeclaredMethod(
+ "addOnColorsChangedListener", mOCLClass);
+
+ mWCGetMethod = WallpaperManager.class.getDeclaredMethod("getWallpaperColors", int.class);
+ Class wallpaperColorsClass = mWCGetMethod.getReturnType();
+ mWCGetColorsMethod = wallpaperColorsClass.getDeclaredMethod("getColors");
+ mWCSupportsDarkTextMethod = wallpaperColorsClass.getDeclaredMethod("supportsDarkText");
+ }
+
+ @Nullable
+ @Override
+ public WallpaperColorsCompat getWallpaperColors(int which) {
+ try {
+ return convertColorsObject(mWCGetMethod.invoke(mWm, which));
+ } catch (Exception e) {
+ Log.e(TAG, "Error calling wallpaper API", e);
+ return null;
+ }
+ }
+
+ @Override
+ public void addOnColorsChangedListener(final OnColorsChangedListenerCompat listener) {
+ Object onChangeListener = Proxy.newProxyInstance(
+ WallpaperManager.class.getClassLoader(),
+ new Class[]{mOCLClass},
+ new InvocationHandler() {
+ @Override
+ public Object invoke(Object o, Method method, Object[] objects)
+ throws Throwable {
+ String methodName = method.getName();
+ if ("onColorsChanged".equals(methodName)) {
+ listener.onColorsChanged(
+ convertColorsObject(objects[0]), (Integer) objects[1]);
+ } else if ("toString".equals(methodName)) {
+ return listener.toString();
+ }
+ return null;
+ }
+ });
+ try {
+ mAddOCLMethod.invoke(mWm, onChangeListener);
+ } catch (Exception e) {
+ Log.e(TAG, "Error calling wallpaper API", e);
+ }
+ }
+
+ private WallpaperColorsCompat convertColorsObject(Object colors) throws Exception {
+ if (colors == null) {
+ return null;
+ }
+ List<Pair<Color, Integer>> list = (List) mWCGetColorsMethod.invoke(colors);
+ boolean supportsDarkText = (Boolean) mWCSupportsDarkTextMethod.invoke(colors);
+ SparseIntArray colorMap = new SparseIntArray(list.size());
+ for (Pair<Color, Integer> color : list) {
+ colorMap.put(color.first.toArgb(), color.second);
+ }
+ return new WallpaperColorsCompat(colorMap, supportsDarkText);
+ }
+}