diff options
6 files changed, 219 insertions, 9 deletions
diff --git a/src/org/cyanogenmod/themes/provider/PreviewGenerationService.java b/src/org/cyanogenmod/themes/provider/PreviewGenerationService.java index 99b40f5..e4dc14e 100644 --- a/src/org/cyanogenmod/themes/provider/PreviewGenerationService.java +++ b/src/org/cyanogenmod/themes/provider/PreviewGenerationService.java @@ -74,6 +74,7 @@ public class PreviewGenerationService extends IntentService { final Bundle extras = intent.getExtras(); String pkgName = extras.getString(EXTRA_PKG_NAME); boolean hasSystemUi = false; + boolean hasHeaders = false; boolean hasIcons = false; boolean hasWallpaper = false; boolean hasStyles = false; @@ -86,7 +87,9 @@ public class PreviewGenerationService extends IntentService { // mods_status_bar was added in version 7 of the database so we need to make sure // it exists when trying to get the int value from the row. final int sysUiIndex = c.getColumnIndex(ThemesColumns.MODIFIES_STATUS_BAR); + final int headersIndex = c.getColumnIndex(ThemesColumns.MODIFIES_STATUSBAR_HEADERS); hasSystemUi = sysUiIndex >= 0 && c.getInt(sysUiIndex) == 1; + hasHeaders = headersIndex >= 0 && c.getInt(headersIndex) == 1; hasIcons = c.getInt(c.getColumnIndex(ThemesColumns.MODIFIES_ICONS)) == 1; hasWallpaper = c.getInt(c.getColumnIndex(ThemesColumns.MODIFIES_LAUNCHER)) == 1 || c.getInt(c.getColumnIndex(ThemesColumns.MODIFIES_LOCKSCREEN)) == 1; @@ -116,7 +119,7 @@ public class PreviewGenerationService extends IntentService { SystemUiItems items = null; try { - items = !hasSystemUi ? null : + items = (!hasSystemUi && !hasHeaders) ? null : new SystemUiPreviewGenerator(this).generateSystemUiItems(pkgName); } catch (Exception e) { Log.e(TAG, "Unable to create statusbar previews for " + pkgName, e); @@ -267,6 +270,22 @@ public class PreviewGenerationService extends IntentService { values = createPreviewEntryString(id, PreviewColumns.NAVBAR_RECENT_BUTTON, path); themeValues.add(values); + + path = PreviewUtils.compressAndSavePng(items.headerMorning, filesDir, pkgName, + PreviewColumns.HEADER_PREVIEW_1); + values = createPreviewEntryString(id, PreviewColumns.HEADER_PREVIEW_1, path); + themeValues.add(values); + + path = PreviewUtils.compressAndSavePng(items.headerNoon, filesDir, pkgName, + PreviewColumns.HEADER_PREVIEW_2); + values = createPreviewEntryString(id, PreviewColumns.HEADER_PREVIEW_2, path); + themeValues.add(values); + + path = PreviewUtils.compressAndSavePng(items.headerEvening, filesDir, pkgName, + PreviewColumns.HEADER_PREVIEW_3); + values = createPreviewEntryString(id, PreviewColumns.HEADER_PREVIEW_3, + path); + themeValues.add(values); } if (icons != null) { path = PreviewUtils.compressAndSavePng(icons.icon1, filesDir, pkgName, diff --git a/src/org/cyanogenmod/themes/provider/ThemePackageHelper.java b/src/org/cyanogenmod/themes/provider/ThemePackageHelper.java index 5479229..1e75212 100644 --- a/src/org/cyanogenmod/themes/provider/ThemePackageHelper.java +++ b/src/org/cyanogenmod/themes/provider/ThemePackageHelper.java @@ -41,10 +41,14 @@ import org.cyanogenmod.internal.util.ThemeUtils; import org.cyanogenmod.themes.provider.util.ProviderUtils; import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; +import static android.content.res.ThemeConfig.SYSTEMUI_STATUSBAR_HEADER_PKG; import static android.content.res.ThemeConfig.SYSTEMUI_NAVBAR_PKG; import static android.content.res.ThemeConfig.SYSTEMUI_STATUS_BAR_PKG; import static android.content.res.ThemeConfig.SYSTEM_DEFAULT; @@ -73,6 +77,19 @@ public class ThemePackageHelper { "overlays/com.android.systemui"); sComponentToFolderName.put(ThemesColumns.MODIFIES_LIVE_LOCK_SCREEN, "live-lockscreen"); + sComponentToFolderName.put(ThemesColumns.MODIFIES_STATUSBAR_HEADERS, + "overlays/com.android.systemui"); + } + + // allow theme to explicitly declare what components to show by listing desired + // components in a string array in package resources. Currently only "headers" + // is supported but soon "navbar" will also be supported + public static final String THEME_PACKAGE_WHITELIST = "theme_component_whitelist"; + + // map theme whitelist component names to theme column + public static HashMap<String, String> sComponentWhitelist = new HashMap<String, String>(); + static { + sComponentWhitelist.put("headers", ThemesColumns.MODIFIES_STATUSBAR_HEADERS); } public static boolean insertPackage(Context context, String pkgName, boolean isProcessing) @@ -320,9 +337,56 @@ public class ThemePackageHelper { boolean hasComponent = hasThemeComponent(themeContext, folderName); implementMap.put(component, hasComponent); } + + // populate map first then pass through whitelist filter + filterWhitelistCapabilities(themeContext, implementMap); return implementMap; } + private static void filterWhitelistCapabilities(Context themeContext, + Map<String, Boolean> capabilities) { + List<String> items = null; + try { + int id = themeContext.getResources().getIdentifier(THEME_PACKAGE_WHITELIST, + "array", + themeContext.getPackageName()); + items = Arrays.asList(themeContext.getResources().getStringArray(id)); + } catch (Exception e) { + Log.e(TAG, "Theme whitelist not supported for " + themeContext.getPackageName()); + } + if (items != null && items.size() > 0) { + // validate entries against authorized whitelist elements + Log.e(TAG, "Theme whitelist found for " + themeContext.getPackageName() + " Elements: " + + items.toString()); + List<String> toRemove = new ArrayList<String>(); + for (String component : items) { + if (!sComponentWhitelist.containsKey(component)) { + toRemove.add(component); + Log.e(TAG, + "Unsupported whitelist element found in " + + themeContext.getPackageName() + " Elements: " + + component.toString()); + } + } + items.removeAll(toRemove); + if (items.size() > 0) { + Log.e(TAG, "Whitelist elements validated in " + themeContext.getPackageName() + + " Elements: " + items.toString() + " Disabling all other components"); + // set everything to false + for (Map.Entry<String, Boolean> entry : capabilities.entrySet()) { + String component = entry.getKey(); + Boolean isImplemented = false; + capabilities.put(component, isImplemented); + } + for (String component : items) { + String entry = sComponentWhitelist.get(component); + Boolean isImplemented = true; + capabilities.put(entry, isImplemented); + } + } + } + } + private static void insertCapabilities(Map<String, Boolean> capabilities, ContentValues values) { for (Map.Entry<String, Boolean> entry : capabilities.entrySet()) { @@ -381,6 +445,9 @@ public class ThemePackageHelper { if (pkgName.equals(themeConfig.getOverlayPkgNameForApp(SYSTEMUI_NAVBAR_PKG))) { builder.setNavBar(pkgName); } + if (pkgName.equals(themeConfig.getOverlayPkgNameForApp(SYSTEMUI_STATUSBAR_HEADER_PKG))) { + builder.setHeaders(pkgName); + } // Check if there are any per-app overlays using this theme final Map<String, ThemeConfig.AppTheme> themes = themeConfig.getAppThemes(); diff --git a/src/org/cyanogenmod/themes/provider/ThemesOpenHelper.java b/src/org/cyanogenmod/themes/provider/ThemesOpenHelper.java index d70ad50..003e9f3 100644 --- a/src/org/cyanogenmod/themes/provider/ThemesOpenHelper.java +++ b/src/org/cyanogenmod/themes/provider/ThemesOpenHelper.java @@ -38,7 +38,7 @@ import org.cyanogenmod.internal.util.ThemeUtils; public class ThemesOpenHelper extends SQLiteOpenHelper { private static final String TAG = ThemesOpenHelper.class.getName(); - private static final int DATABASE_VERSION = 20; + private static final int DATABASE_VERSION = 21; private static final String DATABASE_NAME = "themes.db"; private static final String SYSTEM_THEME_PKG_NAME = ThemeConfig.SYSTEM_DEFAULT; private static final String OLD_SYSTEM_THEME_PKG_NAME = "holo"; @@ -138,6 +138,10 @@ public class ThemesOpenHelper extends SQLiteOpenHelper { upgradeToVersion20(db); oldVersion = 20; } + if (oldVersion == 20) { + upgradeToVersion21(db); + oldVersion = 21; + } if (oldVersion != DATABASE_VERSION) { Log.e(TAG, "Recreating db because unknown database version: " + oldVersion); dropTables(db); @@ -497,6 +501,47 @@ public class ThemesOpenHelper extends SQLiteOpenHelper { SYSTEM_THEME_PKG_NAME)); } + private void upgradeToVersion21(SQLiteDatabase db) { + String sql = String.format("ALTER TABLE %s ADD COLUMN %s INTEGER", + ThemesTable.TABLE_NAME, ThemesColumns.MODIFIES_STATUSBAR_HEADERS); + db.execSQL(sql); + + // we need to update any existing themes + final String[] projection = { + ThemesColumns.PKG_NAME + }; + final String selection = ThemesColumns.MODIFIES_STATUS_BAR + "=?"; + final String[] selectionArgs = { + "1" + }; + final Cursor c = db.query(ThemesTable.TABLE_NAME, projection, selection, selectionArgs, + null, null, null); + if (c != null) { + while (c.moveToNext()) { + final String pkgName = c.getString(0); + boolean hasSystemUi = false; + try { + Context themeContext = mContext.createPackageContext(pkgName, 0); + hasSystemUi = ThemePackageHelper.hasThemeComponent(themeContext, + ThemePackageHelper.sComponentToFolderName.get( + ThemesColumns.MODIFIES_STATUS_BAR)); + } catch (PackageManager.NameNotFoundException e) { + // default to false + } + if (hasSystemUi) { + db.execSQL(String.format("UPDATE %s SET %s='1'", ThemesTable.TABLE_NAME, + ThemesColumns.MODIFIES_STATUSBAR_HEADERS, ThemesColumns.PKG_NAME, + pkgName)); + Intent intent = new Intent(mContext, PreviewGenerationService.class); + intent.setAction(PreviewGenerationService.ACTION_INSERT); + intent.putExtra(PreviewGenerationService.EXTRA_PKG_NAME, pkgName); + mContext.startService(intent); + } + } + c.close(); + } + } + private void dropTables(SQLiteDatabase db) { db.execSQL("DROP TABLE IF EXISTS " + ThemesTable.TABLE_NAME); db.execSQL("DROP TABLE IF EXISTS " + MixnMatchTable.TABLE_NAME); @@ -535,6 +580,7 @@ public class ThemesOpenHelper extends SQLiteOpenHelper { ThemesColumns.MODIFIES_STATUS_BAR + " INTEGER DEFAULT 0, " + ThemesColumns.MODIFIES_NAVIGATION_BAR + " INTEGER DEFAULT 0, " + ThemesColumns.MODIFIES_LIVE_LOCK_SCREEN + " INTEGER DEFAULT 0, " + + ThemesColumns.MODIFIES_STATUSBAR_HEADERS + " INTEGER DEFAULT 0, " + ThemesColumns.PRESENT_AS_THEME + " INTEGER DEFAULT 0, " + ThemesColumns.IS_LEGACY_THEME + " INTEGER DEFAULT 0, " + ThemesColumns.IS_DEFAULT_THEME + " INTEGER DEFAULT 0, " + @@ -563,6 +609,7 @@ public class ThemesOpenHelper extends SQLiteOpenHelper { values.put(ThemesColumns.MODIFIES_RINGTONES, 1); values.put(ThemesColumns.MODIFIES_STATUS_BAR, 1); values.put(ThemesColumns.MODIFIES_NAVIGATION_BAR, 1); + values.put(ThemesColumns.MODIFIES_STATUSBAR_HEADERS, 1); values.put(ThemesColumns.PRESENT_AS_THEME, 1); values.put(ThemesColumns.IS_LEGACY_THEME, 0); values.put(ThemesColumns.IS_DEFAULT_THEME, isDefault); @@ -634,6 +681,11 @@ public class ThemesOpenHelper extends SQLiteOpenHelper { PreviewColumns.NAVBAR_RECENT_BUTTON, PreviewColumns.NAVBAR_BACKGROUND }; + public static final String[] STATUSBAR_HEADERS_PREVIEW_KEYS = { + PreviewColumns.HEADER_PREVIEW_1, + PreviewColumns.HEADER_PREVIEW_2, + PreviewColumns.HEADER_PREVIEW_3 + }; public static final String[] ICON_PREVIEW_KEYS = { PreviewColumns.ICON_PREVIEW_1, PreviewColumns.ICON_PREVIEW_2, @@ -653,5 +705,3 @@ public class ThemesOpenHelper extends SQLiteOpenHelper { return ThemeConfig.SYSTEM_DEFAULT == ThemeUtils.getDefaultThemePackageName(context); } } - - diff --git a/src/org/cyanogenmod/themes/provider/ThemesProvider.java b/src/org/cyanogenmod/themes/provider/ThemesProvider.java index 61828fb..1a916a6 100644 --- a/src/org/cyanogenmod/themes/provider/ThemesProvider.java +++ b/src/org/cyanogenmod/themes/provider/ThemesProvider.java @@ -420,6 +420,15 @@ public class ThemesProvider extends ContentProvider { previewKey)); delimeter = ","; } + } else if (ThemesColumns.MODIFIES_STATUSBAR_HEADERS.equals(component)) { + for (String previewKey : PreviewsTable.STATUSBAR_HEADERS_PREVIEW_KEYS) { + sb.append(delimeter).append(String.format(Locale.US, + "(SELECT %s AS %s FROM previews WHERE %s=%d AND %s='%s')", + PreviewColumns.COL_VALUE, previewKey, + PreviewColumns.THEME_ID, id, PreviewColumns.COL_KEY, + previewKey)); + delimeter = ","; + } } else if (ThemesColumns.MODIFIES_OVERLAYS.equals(component)) { String previewKey = PreviewColumns.STYLE_PREVIEW; sb.append(delimeter).append(String.format(Locale.US, diff --git a/src/org/cyanogenmod/themes/provider/util/BitmapUtils.java b/src/org/cyanogenmod/themes/provider/util/BitmapUtils.java index de1bf98..afedaf0 100644 --- a/src/org/cyanogenmod/themes/provider/util/BitmapUtils.java +++ b/src/org/cyanogenmod/themes/provider/util/BitmapUtils.java @@ -175,6 +175,31 @@ public class BitmapUtils { } /** + * @param image Bitmap to scale + * @param maxSize max size of larget dimen + * @return resized bitmap + * + * Credit StackOverflow + * http://stackoverflow.com/questions/15759195/reduce + * -size-of-bitmap-to-some-specified-pixel-in-android + */ + public static Bitmap getResizedBitmap(Bitmap image, int maxSize) { + int width = image.getWidth(); + int height = image.getHeight(); + + float bitmapRatio = (float) width / (float) height; + if (bitmapRatio > 1) { + width = maxSize; + height = (int) (width / bitmapRatio); + } else { + height = maxSize; + width = (int) (height * bitmapRatio); + } + + return Bitmap.createScaledBitmap(image, width, height, true); + } + + /** * For excessively large images with an awkward ratio we * will want to crop them * @return diff --git a/src/org/cyanogenmod/themes/provider/util/SystemUiPreviewGenerator.java b/src/org/cyanogenmod/themes/provider/util/SystemUiPreviewGenerator.java index 9b5df5e..aed8eb8 100644 --- a/src/org/cyanogenmod/themes/provider/util/SystemUiPreviewGenerator.java +++ b/src/org/cyanogenmod/themes/provider/util/SystemUiPreviewGenerator.java @@ -22,12 +22,17 @@ import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; +import android.graphics.Bitmap.Config; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.VectorDrawable; +import android.util.AttributeSet; +import android.util.DisplayMetrics; +import android.util.Xml; import android.widget.FrameLayout; import org.cyanogenmod.themes.provider.view.BatteryMeterView; +import org.cyanogenmod.themes.provider.util.*; import org.cyanogenmod.themes.provider.R; @@ -47,9 +52,13 @@ public class SystemUiPreviewGenerator { 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 static final String STATUS_BAR_HEADER_MORNING = "notifhead_morning"; + private static final String STATUS_BAR_HEADER_NOON = "notifhead_noon"; + private static final String STATUS_BAR_HEADER_EVENING = "notifhead_sunset"; // Style used for tinting of wifi and signal icons private static final String DUAL_TONE_LIGHT_THEME = "DualToneLightTheme"; + private static final int HEADER_WIDTH = 1440; + private static final int HEADER_HEIGHT = 300; private Context mContext; @@ -107,12 +116,36 @@ public class SystemUiPreviewGenerator { items.navbarRecent = BitmapFactory.decodeResource(res, res.getIdentifier(IC_SYSBAR_RECENT, "drawable", SYSTEMUI_PACKAGE)); + final int headerWidth = convertDpToPixel(HEADER_WIDTH, mContext); + final int headerHeight = convertDpToPixel(HEADER_HEIGHT, mContext); + + // Generate headers + d = themeContext.getDrawable(res.getIdentifier(STATUS_BAR_HEADER_MORNING, "drawable", + SYSTEMUI_PACKAGE)); + items.headerMorning = BitmapUtils.getResizedBitmap(renderDrawableToBitmap(d, headerWidth, headerHeight), HEADER_WIDTH); + d = themeContext.getDrawable(res.getIdentifier(STATUS_BAR_HEADER_NOON, "drawable", + SYSTEMUI_PACKAGE)); + items.headerNoon = BitmapUtils.getResizedBitmap(renderDrawableToBitmap(d, headerWidth, headerHeight), HEADER_WIDTH); + d = themeContext.getDrawable(res.getIdentifier(STATUS_BAR_HEADER_EVENING, "drawable", + SYSTEMUI_PACKAGE)); + items.headerEvening = BitmapUtils.getResizedBitmap(renderDrawableToBitmap(d, headerWidth, headerHeight), HEADER_WIDTH); return items; } + private static int convertDpToPixel(float dp, Context context) { + Resources resources = context.getResources(); + DisplayMetrics metrics = resources.getDisplayMetrics(); + float px = dp * (metrics.densityDpi / 160f); + return Math.round(px); + } + private Bitmap renderDrawableToBitmap(Drawable d, int iconSize) { + return renderDrawableToBitmap(d, iconSize, iconSize); + } + + private Bitmap renderDrawableToBitmap(Drawable d, int iconWidth, int iconHeight) { if (d instanceof VectorDrawable) { - return renderVectorDrawableToBitmap((VectorDrawable) d, iconSize); + return renderVectorDrawableToBitmap((VectorDrawable) d, iconWidth, iconHeight); } else if (d instanceof BitmapDrawable) { return ((BitmapDrawable) d).getBitmap(); } @@ -120,10 +153,10 @@ public class SystemUiPreviewGenerator { return null; } - private Bitmap renderVectorDrawableToBitmap(VectorDrawable d, int iconSize) { - Bitmap bmp = Bitmap.createBitmap(iconSize, iconSize, Bitmap.Config.ARGB_8888); + private Bitmap renderVectorDrawableToBitmap(VectorDrawable d, int iconWidth, int iconHeight) { + Bitmap bmp = Bitmap.createBitmap(iconWidth, iconHeight, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bmp); - d.setBounds(0, 0, iconSize, iconSize); + d.setBounds(0, 0, iconWidth, iconHeight); d.draw(canvas); return bmp; @@ -209,5 +242,12 @@ public class SystemUiPreviewGenerator { public Bitmap navbarBack; public Bitmap navbarHome; public Bitmap navbarRecent; + + /** + * Statusbar Header items + */ + public Bitmap headerMorning; + public Bitmap headerNoon; + public Bitmap headerEvening; } } |
