summaryrefslogtreecommitdiffstats
path: root/src/org/cyanogenmod/themes/provider/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/org/cyanogenmod/themes/provider/util')
-rw-r--r--src/org/cyanogenmod/themes/provider/util/BitmapUtils.java222
-rw-r--r--src/org/cyanogenmod/themes/provider/util/BootAnimationPreviewGenerator.java93
-rw-r--r--src/org/cyanogenmod/themes/provider/util/IconPreviewGenerator.java101
-rw-r--r--src/org/cyanogenmod/themes/provider/util/IconPreviewHelper.java190
-rw-r--r--src/org/cyanogenmod/themes/provider/util/LayoutRenderUtils.java49
-rw-r--r--src/org/cyanogenmod/themes/provider/util/StylePreviewGenerator.java49
-rw-r--r--src/org/cyanogenmod/themes/provider/util/SystemUiPreviewGenerator.java197
-rw-r--r--src/org/cyanogenmod/themes/provider/util/WallpaperPreviewGenerator.java88
8 files changed, 989 insertions, 0 deletions
diff --git a/src/org/cyanogenmod/themes/provider/util/BitmapUtils.java b/src/org/cyanogenmod/themes/provider/util/BitmapUtils.java
new file mode 100644
index 0000000..002970b
--- /dev/null
+++ b/src/org/cyanogenmod/themes/provider/util/BitmapUtils.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2014 The CyanogenMod 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 org.cyanogenmod.themes.provider.util;
+
+import android.content.Context;
+import android.content.res.AssetManager;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.BitmapRegionDecoder;
+import android.graphics.Rect;
+import android.net.Uri;
+import android.util.Log;
+import android.util.TypedValue;
+
+import java.io.Closeable;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class BitmapUtils {
+ private static final String TAG = "BitmapUtils";
+
+ /**
+ * Returns the bitmap from the given uri loaded using the given options.
+ * Returns null on failure.
+ */
+ public static Bitmap loadBitmap(Context context, InputStream is, BitmapFactory.Options o) {
+ try {
+ return BitmapFactory.decodeStream(is, null, o);
+ } finally {
+ closeSilently(is);
+ }
+ }
+
+ public static Bitmap decodeFile(String path, int reqWidth, int reqHeight) {
+ FileInputStream fis = null;
+ try {
+ fis = new FileInputStream(path);
+ return decodeStream(fis, reqWidth, reqHeight);
+ } catch (FileNotFoundException e) {
+ Log.e(TAG, "Unable to open resource in path" + path, e);
+ } finally {
+ closeSilently(fis);
+ }
+ return null;
+ }
+
+ public static Bitmap decodeStream(InputStream is, int reqWidth, int reqHeight) {
+ BitmapFactory.Options opts = new BitmapFactory.Options();
+
+ // Determine insample size
+ opts.inJustDecodeBounds = true;
+ BitmapFactory.decodeStream(is, null, opts);
+ opts.inSampleSize = calculateInSampleSize(opts, reqWidth, reqHeight);
+
+ // Decode the bitmap, regionally if necessary
+ Bitmap bitmap = null;
+ opts.inJustDecodeBounds = false;
+ Rect rect = getCropRectIfNecessary(opts, reqWidth, reqHeight);
+ try {
+ if (rect != null) {
+ BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(is, false);
+ bitmap = decoder.decodeRegion(rect, opts);
+ } else {
+ bitmap = BitmapFactory.decodeStream(is, null, opts);
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "Unable to decode bitmap from stream", e);
+ }
+ return bitmap;
+ }
+
+ public static Bitmap decodeResource(Resources res, int resId, int reqWidth, int reqHeight) {
+ BitmapFactory.Options opts = new BitmapFactory.Options();
+
+ // Determine insample size
+ opts.inJustDecodeBounds = true;
+ BitmapFactory.decodeResource(res, resId, opts);
+ opts.inSampleSize = calculateInSampleSize(opts, reqWidth, reqHeight);
+
+ // Decode the bitmap, regionally if necessary
+ Bitmap bitmap = null;
+ opts.inJustDecodeBounds = false;
+ Rect rect = getCropRectIfNecessary(opts, reqWidth, reqHeight);
+
+ InputStream stream = null;
+ try {
+ if (rect != null) {
+ stream = res.openRawResource(resId, new TypedValue());
+ if (stream == null) return null;
+ BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(stream, false);
+ bitmap = decoder.decodeRegion(rect, opts);
+ } else {
+ bitmap = BitmapFactory.decodeResource(res, resId, opts);
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "Unable to open resource " + resId, e);
+ } finally {
+ closeSilently(stream);
+ }
+ return bitmap;
+ }
+
+
+ public static Bitmap decodeByteArray(byte[] buffer, int reqWidth, int reqHeight) {
+ BitmapFactory.Options opts = new BitmapFactory.Options();
+
+ // Determine insample size
+ opts.inJustDecodeBounds = true;
+ BitmapFactory.decodeByteArray(buffer, 0, buffer.length, opts);
+ opts.inSampleSize = calculateInSampleSize(opts, reqWidth, reqHeight);
+
+ // Decode the bitmap, regionally if necessary
+ Bitmap bitmap = null;
+ opts.inJustDecodeBounds = false;
+ Rect rect = getCropRectIfNecessary(opts, reqWidth, reqHeight);
+ try {
+ if (rect != null) {
+ BitmapRegionDecoder decoder =
+ BitmapRegionDecoder.newInstance(buffer, 0, buffer.length, false);
+ bitmap = decoder.decodeRegion(rect, opts);
+ } else {
+ bitmap = BitmapFactory.decodeByteArray(buffer, 0, buffer.length, opts);
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "Unable to decode bitmap from stream", e);
+ }
+ return bitmap;
+ }
+
+ public static Bitmap getBitmapFromAsset(Context ctx, String path, int reqWidth, int reqHeight) {
+ if (ctx == null || path == null)
+ return null;
+
+ Bitmap bitmap = null;
+ try {
+ AssetManager assets = ctx.getAssets();
+ InputStream is = assets.open(path);
+ bitmap = decodeStream(is, reqWidth, reqHeight);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return bitmap;
+ }
+
+
+ /**
+ * For excessively large images with an awkward ratio we
+ * will want to crop them
+ * @return
+ */
+ public static Rect getCropRectIfNecessary(
+ BitmapFactory.Options options,int reqWidth, int reqHeight) {
+ final int width = options.outWidth;
+ final int height = options.outHeight;
+ Rect rect = new Rect(0, 0, width, height);
+ // Determine downsampled size
+ int targetWidth = reqWidth * options.inSampleSize;
+ int targetHeight = reqHeight * options.inSampleSize;
+
+ if (targetHeight < height) {
+ rect.top = (height - targetHeight) / 2;
+ rect.bottom = rect.top + targetHeight;
+ }
+ if (targetWidth < width) {
+ rect.left = (width - targetWidth) / 2;
+ rect.right = rect.left + targetWidth;
+ }
+ return rect;
+ }
+
+ public static int calculateInSampleSize(
+ BitmapFactory.Options options, int reqWidth, int reqHeight) {
+ return calculateInSampleSize(options.outWidth, options.outHeight, reqWidth, reqHeight);
+ }
+
+ // Modified from original source:
+ // http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
+ public static int calculateInSampleSize(
+ int decodeWidth, int decodeHeight, int reqWidth, int reqHeight) {
+ // Raw height and width of image
+ int inSampleSize = 1;
+
+ if (decodeHeight > reqHeight || decodeWidth > reqWidth) {
+ final int halfHeight = decodeHeight / 2;
+ final int halfWidth = decodeWidth / 2;
+
+ // Calculate the largest inSampleSize value that is a power of 2 and keeps both
+ // height and width larger than the requested height and width.
+ while ((halfHeight / inSampleSize) > reqHeight &&
+ (halfWidth / inSampleSize) > reqWidth) {
+ inSampleSize *= 2;
+ }
+ }
+
+ return inSampleSize;
+ }
+
+ public static void closeSilently(Closeable c) {
+ if (c == null) return;
+ try {
+ c.close();
+ } catch (IOException t) {
+ Log.w(TAG, "close fail ", t);
+ }
+ }
+}
diff --git a/src/org/cyanogenmod/themes/provider/util/BootAnimationPreviewGenerator.java b/src/org/cyanogenmod/themes/provider/util/BootAnimationPreviewGenerator.java
new file mode 100644
index 0000000..61771a7
--- /dev/null
+++ b/src/org/cyanogenmod/themes/provider/util/BootAnimationPreviewGenerator.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2014 The CyanogenMod 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 org.cyanogenmod.themes.provider.util;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.ThemeConfig;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.view.View;
+import android.widget.ImageView;
+import org.cyanogenmod.themes.provider.R;
+
+import java.io.BufferedInputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+public class BootAnimationPreviewGenerator {
+ public static final String SYSTEM_BOOT_ANI_PATH = "/system/media/bootanimation.zip";
+ public static final String THEME_BOOT_ANI_PATH = "bootanimation/bootanimation.zip";
+
+ private Context mContext;
+
+ public BootAnimationPreviewGenerator(Context context) {
+ mContext = context;
+ }
+
+ public Bitmap generateBootAnimationPreview(String pkgName)
+ throws IOException, PackageManager.NameNotFoundException {
+ ZipInputStream zis;
+ String previewName;
+ if (ThemeConfig.SYSTEM_DEFAULT.equals(pkgName)) {
+ previewName = getPreviewFrameEntryName(new FileInputStream(SYSTEM_BOOT_ANI_PATH));
+ zis = new ZipInputStream(new FileInputStream(SYSTEM_BOOT_ANI_PATH));
+ } else {
+ final Context themeCtx = mContext.createPackageContext(pkgName, 0);
+ previewName = getPreviewFrameEntryName(themeCtx.getAssets().open(THEME_BOOT_ANI_PATH));
+ zis = new ZipInputStream(themeCtx.getAssets().open(THEME_BOOT_ANI_PATH));
+ }
+ ZipEntry ze;
+ Bitmap bmp = null;
+ while ((ze = zis.getNextEntry()) != null) {
+ final String entryName = ze.getName();
+ if (entryName.equals(previewName)) {
+ bmp = BitmapFactory.decodeStream(zis);
+ break;
+ }
+ }
+ zis.close();
+
+ View v = View.inflate(mContext, R.layout.bootanimation_preview, null);
+ ImageView iv = (ImageView) v.findViewById(R.id.preview);
+ iv.setImageBitmap(bmp);
+ return LayoutRenderUtils.renderViewToBitmap(v);
+ }
+
+ private String getPreviewFrameEntryName(InputStream is) throws IOException {
+ ZipInputStream zis = (is instanceof ZipInputStream) ? (ZipInputStream) is
+ : new ZipInputStream(new BufferedInputStream(is));
+ ZipEntry ze;
+ String previewName = null;
+ long max = 0;
+ while ((ze = zis.getNextEntry()) != null) {
+ final String entryName = ze.getName();
+ if (entryName.contains("/")
+ && (entryName.endsWith(".png") || entryName.endsWith(".jpg"))) {
+ if(ze.getSize() > max) {
+ previewName = entryName;
+ max = ze.getSize();
+ }
+ }
+ }
+ zis.close();
+
+ return previewName;
+ }
+}
diff --git a/src/org/cyanogenmod/themes/provider/util/IconPreviewGenerator.java b/src/org/cyanogenmod/themes/provider/util/IconPreviewGenerator.java
new file mode 100644
index 0000000..b1857d8
--- /dev/null
+++ b/src/org/cyanogenmod/themes/provider/util/IconPreviewGenerator.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2014 The CyanogenMod 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 org.cyanogenmod.themes.provider.util;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+
+public class IconPreviewGenerator {
+ private static final ComponentName COMPONENT_DIALER =
+ new ComponentName("com.android.dialer", "com.android.dialer.DialtactsActivity");
+ private static final ComponentName COMPONENT_MESSAGING =
+ new ComponentName("com.android.mms", "com.android.mms.ui.ConversationList");
+ private static final ComponentName COMPONENT_CAMERANEXT =
+ new ComponentName("com.cyngn.cameranext", "com.android.camera.CameraLauncher");
+ private static final ComponentName COMPONENT_CAMERA =
+ new ComponentName("com.android.camera2", "com.android.camera.CameraLauncher");
+ private static final ComponentName COMPONENT_BROWSER =
+ new ComponentName("com.android.browser", "com.android.browser.BrowserActivity");
+ private static final ComponentName COMPONENT_SETTINGS =
+ new ComponentName("com.android.settings", "com.android.settings.Settings");
+ private static final ComponentName COMPONENT_CALENDAR =
+ new ComponentName("com.android.calendar", "com.android.calendar.AllInOneActivity");
+ private static final ComponentName COMPONENT_GALERY =
+ new ComponentName("com.android.gallery3d", "com.android.gallery3d.app.GalleryActivity");
+ private static final String CAMERA_NEXT_PACKAGE = "com.cyngn.cameranext";
+
+ private ComponentName[] mIconComponents;
+
+ private Context mContext;
+
+ public IconPreviewGenerator(Context context) {
+ mContext = context;
+ }
+
+ public IconItems generateIconItems(String pkgName) {
+ IconItems items = new IconItems();
+ IconPreviewHelper helper = new IconPreviewHelper(mContext, pkgName);
+
+ final ComponentName[] components = getIconComponents(mContext);
+ BitmapDrawable drawable;
+ drawable = (BitmapDrawable) helper.getIcon(components[0]);
+ items.icon1 = drawable.getBitmap();
+ drawable = (BitmapDrawable) helper.getIcon(components[1]);
+ items.icon2 = drawable.getBitmap();
+ drawable = (BitmapDrawable) helper.getIcon(components[2]);
+ items.icon3 = drawable.getBitmap();
+ return items;
+ }
+
+ private ComponentName[] getIconComponents(Context context) {
+ if (mIconComponents == null || mIconComponents.length == 0) {
+ mIconComponents = new ComponentName[]{COMPONENT_DIALER, COMPONENT_MESSAGING,
+ COMPONENT_CAMERA, COMPONENT_BROWSER};
+
+ PackageManager pm = context.getPackageManager();
+
+ // if device does not have telephony replace dialer and mms
+ if (!pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+ mIconComponents[0] = COMPONENT_CALENDAR;
+ mIconComponents[1] = COMPONENT_GALERY;
+ }
+
+ if (!pm.hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
+ mIconComponents[2] = COMPONENT_SETTINGS;
+ } else {
+ // decide on which camera icon to use
+ try {
+ if (pm.getPackageInfo(CAMERA_NEXT_PACKAGE, 0) != null) {
+ mIconComponents[2] = COMPONENT_CAMERANEXT;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ // default to COMPONENT_CAMERA
+ }
+ }
+
+ }
+
+ return mIconComponents;
+ }
+ public class IconItems {
+ public Bitmap icon1;
+ public Bitmap icon2;
+ public Bitmap icon3;
+ }
+}
diff --git a/src/org/cyanogenmod/themes/provider/util/IconPreviewHelper.java b/src/org/cyanogenmod/themes/provider/util/IconPreviewHelper.java
new file mode 100644
index 0000000..484136c
--- /dev/null
+++ b/src/org/cyanogenmod/themes/provider/util/IconPreviewHelper.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2014 The CyanogenMod 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 org.cyanogenmod.themes.provider.util;
+
+import android.app.ActivityManager;
+import android.app.ComposedIconInfo;
+import android.app.IconPackHelper;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageItemInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.AssetManager;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.util.SparseArray;
+
+/**
+ * This class handles all the logic to build a preview icon
+ * If the system currently has a theme applied we do NOT
+ * want this code to be impacted by it. So code in this
+ * class creates special "no theme attached" resource objects
+ * to retrieve objects from.
+ */
+public class IconPreviewHelper {
+ private static final String TAG = IconPreviewHelper.class.getSimpleName();
+ private final static float ICON_SCALE_FACTOR = 1.3f; //Arbitrary. Looks good
+
+ private Context mContext;
+ private DisplayMetrics mDisplayMetrics;
+ private Configuration mConfiguration;
+ private int mIconDpi = 0;
+ private String mThemePkgName;
+ private IconPackHelper mIconPackHelper;
+ private int mIconSize;
+
+ /**
+ * @param themePkgName - The package name of the theme we wish to preview
+ */
+ public IconPreviewHelper(Context context, String themePkgName) {
+ mContext = context;
+ mDisplayMetrics = context.getResources().getDisplayMetrics();
+ mConfiguration = context.getResources().getConfiguration();
+ ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
+ mIconDpi = (int) (am.getLauncherLargeIconDensity() * ICON_SCALE_FACTOR);
+ mThemePkgName = themePkgName;
+ mIconPackHelper = new IconPackHelper(mContext);
+ try {
+ mIconPackHelper.loadIconPack(mThemePkgName);
+ } catch (NameNotFoundException e) {}
+ mIconSize = (int) (am.getLauncherLargeIconSize() * ICON_SCALE_FACTOR);
+ }
+
+ /**
+ * Returns the actual label name for a given component
+ * If the activity does not have a label it will return app's label
+ * If neither has a label returns empty string
+ */
+ public String getLabel(ComponentName component) {
+ String label = "";
+ try {
+ PackageManager pm = mContext.getPackageManager();
+ ApplicationInfo appInfo = pm.getApplicationInfo(component.getPackageName(), 0);
+ ActivityInfo activityInfo = pm.getActivityInfo(component, 0);
+
+ AssetManager assets = new AssetManager();
+ assets.addAssetPath(appInfo.publicSourceDir);
+ Resources res = new Resources(assets, mDisplayMetrics, mConfiguration);
+
+ if (activityInfo.labelRes != 0) {
+ label = res.getString(activityInfo.labelRes);
+ } else if (appInfo.labelRes != 0) {
+ label = res.getString(appInfo.labelRes);
+ }
+ } catch(NameNotFoundException exception) {
+ Log.e(TAG, "unable to find pkg for " + component.toString());
+ }
+ return label;
+ }
+
+ /**
+ * Returns the icon for the given component regardless of the system's
+ * currently applied theme. If the preview theme does not support the icon, then
+ * return the system default icon.
+ */
+ public Drawable getIcon(ComponentName component) {
+ String packageName = component.getPackageName();
+ String activityName = component.getClassName();
+ Drawable icon = getThemedIcon(packageName, activityName);
+ if (icon == null) {
+ icon = getDefaultIcon(packageName, activityName);
+ }
+ if (icon != null) {
+ icon.setBounds(0, 0, mIconSize, mIconSize);
+ }
+ return icon;
+ }
+
+ private Drawable getThemedIcon(String pkgName, String activityName) {
+ Drawable drawable = null;
+ ActivityInfo info = new ActivityInfo();
+ info.packageName = pkgName;
+ info.name = activityName;
+ drawable = mIconPackHelper.getDrawableForActivityWithDensity(info, mIconDpi);
+
+ return drawable;
+ }
+
+ /**
+ * Returns the default icon. This can be the normal icon associated with the app or a composed
+ * icon if the icon pack supports background, mask, and/or foreground.
+ * @param pkgName
+ * @param activityName
+ * @return
+ */
+ private Drawable getDefaultIcon(String pkgName, String activityName) {
+ Drawable drawable = null;
+ ComponentName component = new ComponentName(pkgName, activityName);
+ PackageManager pm = mContext.getPackageManager();
+ Resources res = null;
+ try {
+ ActivityInfo info = pm.getActivityInfo(component, 0);
+ ApplicationInfo appInfo = pm.getApplicationInfo(pkgName, 0);
+
+ res = pm.getThemedResourcesForApplication(pkgName, mThemePkgName);
+
+ final int iconId = info.icon != 0 ? info.icon : appInfo.icon;
+ info.themedIcon = 0;
+ setupComposedIcon(res, info, iconId);
+ drawable = getFullResIcon(res, iconId);
+ } catch (NameNotFoundException e2) {
+ Log.w(TAG, "Unable to get the icon for " + pkgName + " using default");
+ }
+ drawable = (drawable != null) ?
+ getComposedIcon(res, drawable) : getFullResDefaultActivityIcon();
+ return drawable;
+ }
+
+ private Drawable getComposedIcon(Resources res, Drawable baseIcon) {
+ ComposedIconInfo iconInfo = mIconPackHelper.getComposedIconInfo();
+ if (res != null && iconInfo != null && (iconInfo.iconBacks != null ||
+ iconInfo.iconMask != 0 || iconInfo.iconUpon != 0)) {
+ return IconPackHelper.IconCustomizer.getComposedIconDrawable(baseIcon, res, iconInfo);
+ }
+ return baseIcon;
+ }
+
+ private void setupComposedIcon(Resources res, ActivityInfo info, int iconId) {
+ ComposedIconInfo iconInfo = mIconPackHelper.getComposedIconInfo();
+
+ res.setComposedIconInfo(iconInfo);
+
+ SparseArray<PackageItemInfo> icons = new SparseArray<PackageItemInfo>(1);
+ info.themedIcon = 0;
+ icons.put(iconId, info);
+ res.setIconResources(icons);
+ }
+
+ private Drawable getFullResIcon(Resources resources, int iconId) {
+ Drawable d;
+ try {
+ d = resources.getDrawableForDensity(iconId, mIconDpi, null, false);
+ } catch (Resources.NotFoundException e) {
+ d = null;
+ }
+ return (d != null) ? d : getFullResDefaultActivityIcon();
+ }
+
+ private Drawable getFullResDefaultActivityIcon() {
+ return getFullResIcon(Resources.getSystem(), android.R.mipmap.sym_def_app_icon);
+ }
+}
diff --git a/src/org/cyanogenmod/themes/provider/util/LayoutRenderUtils.java b/src/org/cyanogenmod/themes/provider/util/LayoutRenderUtils.java
new file mode 100644
index 0000000..c174d8e
--- /dev/null
+++ b/src/org/cyanogenmod/themes/provider/util/LayoutRenderUtils.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014 The CyanogenMod 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 org.cyanogenmod.themes.provider.util;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.view.View;
+import android.widget.FrameLayout;
+
+public class LayoutRenderUtils {
+
+ public static Bitmap renderViewToBitmap(View view) {
+ // Provide it with a layout params. It should necessarily be wrapping the
+ // content as we not really going to have a parent for it.
+ view.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT,
+ FrameLayout.LayoutParams.WRAP_CONTENT));
+
+ // Pre-measure the view so that height and width don't remain null.
+ view.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
+ View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
+
+ // Assign a size and position to the view and all of its descendants
+ view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
+
+ // Create the bitmap
+ Bitmap bitmap = Bitmap.createBitmap(view.getMeasuredWidth(),
+ view.getMeasuredHeight(),
+ Bitmap.Config.ARGB_8888);
+ // Create a canvas with the specified bitmap to draw into
+ Canvas c = new Canvas(bitmap);
+
+ // Render this view to the given Canvas
+ view.draw(c);
+ return bitmap;
+ }
+}
diff --git a/src/org/cyanogenmod/themes/provider/util/StylePreviewGenerator.java b/src/org/cyanogenmod/themes/provider/util/StylePreviewGenerator.java
new file mode 100644
index 0000000..eebcb36
--- /dev/null
+++ b/src/org/cyanogenmod/themes/provider/util/StylePreviewGenerator.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014 The CyanogenMod 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 org.cyanogenmod.themes.provider.util;
+
+import android.content.Context;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.graphics.Bitmap;
+import android.os.UserHandle;
+import android.view.View;
+
+import org.cyanogenmod.themes.provider.R;
+
+public class StylePreviewGenerator {
+ private Context mContext;
+
+ public StylePreviewGenerator(Context context) {
+ mContext = context;
+ }
+
+ public StyleItems generateStylePreviews(String pkgName) throws NameNotFoundException {
+ StyleItems items = new StyleItems();
+ Context themeContext = mContext.createPackageContextAsUser("android", pkgName, 0,
+ UserHandle.CURRENT);
+ View v = View.inflate(themeContext, R.layout.controls_thumbnail, null);
+ items.thumbnail = LayoutRenderUtils.renderViewToBitmap(v);
+
+ v = View.inflate(themeContext, R.layout.controls_preview, null);
+ items.preview = LayoutRenderUtils.renderViewToBitmap(v);
+ return items;
+ }
+
+ public class StyleItems {
+ public Bitmap thumbnail;
+ public Bitmap preview;
+ }
+}
diff --git a/src/org/cyanogenmod/themes/provider/util/SystemUiPreviewGenerator.java b/src/org/cyanogenmod/themes/provider/util/SystemUiPreviewGenerator.java
new file mode 100644
index 0000000..d53ad2f
--- /dev/null
+++ b/src/org/cyanogenmod/themes/provider/util/SystemUiPreviewGenerator.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2014 The CyanogenMod 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 org.cyanogenmod.themes.provider.util;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.VectorDrawable;
+import android.widget.FrameLayout;
+import org.cyanogenmod.themes.provider.view.BatteryMeterView;
+
+import org.cyanogenmod.themes.provider.R;
+
+public class SystemUiPreviewGenerator {
+ public static final String SYSTEMUI_PACKAGE = "com.android.systemui";
+ private static final String WIFI_DRAWABLE = "stat_sys_wifi_signal_3_fully";
+ private static final String SIGNAL_DRAWABLE = "stat_sys_signal_3_fully";
+ private static final String BLUETOOTH_DRAWABLE = "stat_sys_data_bluetooth_connected";
+ private static final String STATUS_BAR_BATTERY_WIDTH = "status_bar_battery_width";
+ private static final String STATUS_BAR_BATTERY_HEIGHT = "status_bar_battery_height";
+ private static final String STATUS_BAR_CLOCK_COLOR = "status_bar_clock_color";
+ private static final String SYSTEM_BAR_BACKGROUND = "system_bar_background";
+ private static final String STATUS_BAR_BACKGROUND_OPAQUE = "status_bar_background_opaque";
+ private static final String NAVIGATION_BAR_BACKGROUND_OPAQUE
+ = "navigation_bar_background_opaque";
+ private static final String IC_SYSBAR_BACK = "ic_sysbar_back";
+ private static final String IC_SYSBAR_HOME = "ic_sysbar_home";
+ private static final String IC_SYSBAR_RECENT = "ic_sysbar_recent";
+ private static final String STATUS_BAR_ICON_SIZE = "status_bar_icon_size";
+
+ private Context mContext;
+
+ public SystemUiPreviewGenerator(Context context) {
+ mContext = context;
+ }
+
+ /**
+ * Loads the necessary resources for the given theme.
+ * @param pkgName Package name for the theme to use
+ * @return
+ * @throws NameNotFoundException
+ */
+ public SystemUiItems generateSystemUiItems(String pkgName) throws NameNotFoundException {
+ PackageManager pm = mContext.getPackageManager();
+ Resources res = pm.getThemedResourcesForApplication(SYSTEMUI_PACKAGE, pkgName);
+ int iconSize = res.getDimensionPixelSize(res.getIdentifier(STATUS_BAR_ICON_SIZE, "dimen",
+ SYSTEMUI_PACKAGE));
+
+ SystemUiItems items = new SystemUiItems();
+ Drawable d = res.getDrawable(res.getIdentifier(BLUETOOTH_DRAWABLE, "drawable",
+ SYSTEMUI_PACKAGE));
+ items.bluetoothIcon = renderDrawableToBitmap(d, iconSize);
+
+ d = res.getDrawable(res.getIdentifier(WIFI_DRAWABLE, "drawable", SYSTEMUI_PACKAGE));
+ items.wifiIcon = renderDrawableToBitmap(d, iconSize);
+
+ d = res.getDrawable(res.getIdentifier(SIGNAL_DRAWABLE, "drawable", SYSTEMUI_PACKAGE));
+ items.signalIcon = renderDrawableToBitmap(d, iconSize);
+
+ items.clockColor = res.getColor(res.getIdentifier(STATUS_BAR_CLOCK_COLOR, "color",
+ SYSTEMUI_PACKAGE));
+ // wifi margin no longer used in systemui
+ items.wifiMarginEnd = 0;
+ generateBatteryPreviews(res, items);
+ generateBackgroundPreviews(res, items);
+
+ items.navbarBack = BitmapFactory.decodeResource(res, res.getIdentifier(IC_SYSBAR_BACK,
+ "drawable", SYSTEMUI_PACKAGE));
+ items.navbarHome = BitmapFactory.decodeResource(res, res.getIdentifier(IC_SYSBAR_HOME,
+ "drawable", SYSTEMUI_PACKAGE));
+ items.navbarRecent = BitmapFactory.decodeResource(res, res.getIdentifier(IC_SYSBAR_RECENT,
+ "drawable", SYSTEMUI_PACKAGE));
+
+ return items;
+ }
+
+ private Bitmap renderDrawableToBitmap(Drawable d, int iconSize) {
+ if (d instanceof VectorDrawable) {
+ return renderVectorDrawableToBitmap((VectorDrawable) d, iconSize);
+ } else if (d instanceof BitmapDrawable) {
+ return ((BitmapDrawable) d).getBitmap();
+ }
+
+ return null;
+ }
+
+ private Bitmap renderVectorDrawableToBitmap(VectorDrawable d, int iconSize) {
+ Bitmap bmp = Bitmap.createBitmap(iconSize, iconSize, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bmp);
+ d.setBounds(0, 0, iconSize, iconSize);
+ d.draw(canvas);
+
+ return bmp;
+ }
+
+ /**
+ * Generates the various battery types using the provided resources.
+ * @param res
+ * @param items
+ */
+ private void generateBatteryPreviews(Resources res, SystemUiItems items) {
+ FrameLayout view = new FrameLayout(mContext);
+ BatteryMeterView battery = new BatteryMeterView(mContext, res);
+ view.addView(battery,
+ new FrameLayout.LayoutParams(mContext.getResources().getDimensionPixelSize(
+ R.dimen.status_bar_battery_width),
+ mContext.getResources().getDimensionPixelSize(
+ R.dimen.status_bar_battery_height)));
+ battery.setMode(BatteryMeterView.BatteryMeterMode.BATTERY_METER_ICON_PORTRAIT);
+ items.batteryPortrait = LayoutRenderUtils.renderViewToBitmap(view);
+ battery.setMode(BatteryMeterView.BatteryMeterMode.BATTERY_METER_ICON_LANDSCAPE);
+ items.batteryLandscape = LayoutRenderUtils.renderViewToBitmap(view);
+ battery.setMode(BatteryMeterView.BatteryMeterMode.BATTERY_METER_CIRCLE);
+ items.batteryCircle = LayoutRenderUtils.renderViewToBitmap(view);
+ }
+
+ private void generateBackgroundPreviews(Resources res, SystemUiItems items) {
+ int width = Math.max(res.getDisplayMetrics().widthPixels,
+ res.getDisplayMetrics().heightPixels);
+ int height = mContext.getResources().getDimensionPixelSize(
+ R.dimen.status_bar_battery_height);
+ Drawable defaultBackground = res.getDrawable(
+ res.getIdentifier(SYSTEM_BAR_BACKGROUND, "drawable", SYSTEMUI_PACKAGE));
+ defaultBackground.setBounds(0, 0, width, height);
+
+ Drawable statusbarBackground = null;
+ int resId = res.getIdentifier(STATUS_BAR_BACKGROUND_OPAQUE, "color", SYSTEMUI_PACKAGE);
+ if (resId != 0) {
+ statusbarBackground = new ColorDrawable(res.getColor(resId));
+ statusbarBackground.setBounds(0, 0, width, height);
+ }
+ items.statusbarBackground = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(items.statusbarBackground);
+ if (statusbarBackground != null) {
+ statusbarBackground.draw(canvas);
+ } else {
+ defaultBackground.draw(canvas);
+ }
+
+ Drawable navbarBackground = null;
+ resId = res.getIdentifier(NAVIGATION_BAR_BACKGROUND_OPAQUE, "color", SYSTEMUI_PACKAGE);
+ if (resId != 0) {
+ navbarBackground = new ColorDrawable(res.getColor(resId));
+ navbarBackground.setBounds(0, 0, width, height);
+ }
+ items.navbarBackground = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ canvas.setBitmap(items.navbarBackground);
+ if (navbarBackground != null) {
+ navbarBackground.draw(canvas);
+ } else {
+ defaultBackground.draw(canvas);
+ }
+ }
+
+ public class SystemUiItems {
+ /**
+ * Status bar items
+ */
+ public Bitmap bluetoothIcon;
+ public Bitmap wifiIcon;
+ public Bitmap signalIcon;
+ public Bitmap batteryPortrait;
+ public Bitmap batteryLandscape;
+ public Bitmap batteryCircle;
+ public Bitmap statusbarBackground;
+ public int clockColor;
+ public int wifiMarginEnd;
+
+ /**
+ * Navigation bar items
+ */
+ public Bitmap navbarBackground;
+ public Bitmap navbarBack;
+ public Bitmap navbarHome;
+ public Bitmap navbarRecent;
+ }
+}
diff --git a/src/org/cyanogenmod/themes/provider/util/WallpaperPreviewGenerator.java b/src/org/cyanogenmod/themes/provider/util/WallpaperPreviewGenerator.java
new file mode 100644
index 0000000..01b888a
--- /dev/null
+++ b/src/org/cyanogenmod/themes/provider/util/WallpaperPreviewGenerator.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2014 The CyanogenMod 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 org.cyanogenmod.themes.provider.util;
+
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ThemeUtils;
+import android.content.res.AssetManager;
+import android.content.res.Resources;
+import android.content.res.ThemeConfig;
+import android.graphics.Bitmap;
+
+import org.cyanogenmod.themes.provider.R;
+
+import java.io.File;
+import java.io.IOException;
+
+public class WallpaperPreviewGenerator {
+ private static final String WALLPAPER_ASSET_PATH = "wallpapers";
+ private static final String LOCKSCREEN_ASSET_PATH = "lockscreen";
+ private Context mContext;
+ private int mPreviewSize;
+ private int mThumbnailSize;
+
+ public WallpaperPreviewGenerator(Context context) {
+ mContext = context;
+ final Resources res = context.getResources();
+ mPreviewSize = res.getDimensionPixelSize(R.dimen.wallpaper_preview_size);
+ mThumbnailSize = res.getDimensionPixelSize(R.dimen.wallpaper_thumbnail_size);
+ }
+
+ public WallpaperItems generateWallpaperPreviews(PackageInfo themeInfo)
+ throws NameNotFoundException, IOException {
+ WallpaperItems items = new WallpaperItems();
+ if (themeInfo == null) {
+ Resources res = mContext.getPackageManager().getThemedResourcesForApplication("android",
+ ThemeConfig.SYSTEM_DEFAULT);
+ items.wpPreview = items.lsPreview = BitmapUtils.decodeResource(res,
+ com.android.internal.R.drawable.default_wallpaper, mPreviewSize, mPreviewSize);
+ } else {
+ final Context themeContext = mContext.createPackageContext(themeInfo.packageName, 0);
+ final AssetManager assets = themeContext.getAssets();
+ String path = ThemeUtils.getWallpaperPath(assets);
+ if (path != null) {
+ items.wpPreview = BitmapUtils.getBitmapFromAsset(themeContext, path,
+ mPreviewSize, mPreviewSize);
+ }
+ path = ThemeUtils.getLockscreenWallpaperPath(assets);
+ if (path != null) {
+ items.lsPreview = BitmapUtils.getBitmapFromAsset(themeContext, path,
+ mPreviewSize, mPreviewSize);
+ }
+ }
+ if (items.wpPreview != null) {
+ items.wpThumbnail = Bitmap.createScaledBitmap(items.wpPreview, mThumbnailSize,
+ mThumbnailSize, true);
+ }
+ if (items.lsPreview != null) {
+ items.lsThumbnail = Bitmap.createScaledBitmap(items.lsPreview, mThumbnailSize,
+ mThumbnailSize, true);
+ }
+ return items;
+ }
+
+ public class WallpaperItems {
+ // Wallpaper items
+ public Bitmap wpThumbnail;
+ public Bitmap wpPreview;
+
+ // Lockscreen wallpaper items
+ public Bitmap lsThumbnail;
+ public Bitmap lsPreview;
+ }
+}