diff options
Diffstat (limited to 'src/org/cyanogenmod/themes/provider/util')
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; + } +} |