Material themes for widget

This commit is contained in:
serso
2015-06-18 11:55:53 +02:00
parent 70a5e7e2e5
commit a0ff19e4cd
34 changed files with 864 additions and 109 deletions

View File

@@ -96,6 +96,12 @@ public final class Check {
throw new AssertionException(message);
}
public static void isTrue(boolean expression) {
if (!expression) {
throw new AssertionException("");
}
}
public static void isTrue(boolean expression, @Nonnull String message) {
if (!expression) {
throw new AssertionException(message);

View File

@@ -165,7 +165,7 @@ public final class App {
}
}
});
App.broadcaster = new CalculatorBroadcaster(application);
App.broadcaster = new CalculatorBroadcaster(application, preferences);
App.vibrator = new Vibrator(application, preferences);
App.screenMetrics = new ScreenMetrics(application);
@@ -285,15 +285,15 @@ public final class App {
}
@Nonnull
public static Preferences.Onscreen.Theme getOnscreenTheme() {
return Preferences.Onscreen.getTheme(getPreferences());
public static Preferences.SimpleTheme getWidgetTheme() {
return Preferences.Widget.getTheme(getPreferences());
}
@Nonnull
public static Preferences.Gui.Theme getThemeIn(@Nonnull Context context) {
if (context instanceof CalculatorOnscreenService) {
final SharedPreferences p = getPreferences();
final Preferences.Onscreen.Theme onscreenTheme = Preferences.Onscreen.getTheme(p);
final Preferences.SimpleTheme onscreenTheme = Preferences.Onscreen.getTheme(p);
final Preferences.Gui.Theme appTheme = Preferences.Gui.getTheme(p);
return onscreenTheme.resolveThemeFor(appTheme).getAppTheme();
} else {

View File

@@ -2,21 +2,24 @@ package org.solovyev.android.calculator;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public final class CalculatorBroadcaster implements CalculatorEventListener {
public final class CalculatorBroadcaster implements CalculatorEventListener, SharedPreferences.OnSharedPreferenceChangeListener {
public static final String ACTION_INIT = "org.solovyev.android.calculator.INIT";
public static final String ACTION_EDITOR_STATE_CHANGED = "org.solovyev.android.calculator.EDITOR_STATE_CHANGED";
public static final String ACTION_DISPLAY_STATE_CHANGED = "org.solovyev.android.calculator.DISPLAY_STATE_CHANGED";
public static final String ACTION_THEME_CHANGED = "org.solovyev.android.calculator.THEME_CHANGED";
@Nonnull
private final Context context;
public CalculatorBroadcaster(@Nonnull Context context) {
public CalculatorBroadcaster(@Nonnull Context context, @Nonnull SharedPreferences preferences) {
this.context = context;
preferences.registerOnSharedPreferenceChangeListener(this);
}
@Override
@@ -43,4 +46,11 @@ public final class CalculatorBroadcaster implements CalculatorEventListener {
public void sendBroadcastIntent(@Nonnull String action) {
context.sendBroadcast(new Intent(action));
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (Preferences.Gui.theme.isSameKey(key) || Preferences.Widget.theme.isSameKey(key)) {
sendBroadcastIntent(ACTION_THEME_CHANGED);
}
}
}

View File

@@ -31,6 +31,7 @@ import android.support.annotation.StyleRes;
import android.util.SparseArray;
import android.view.ContextThemeWrapper;
import org.solovyev.android.Check;
import org.solovyev.android.calculator.language.Languages;
import org.solovyev.android.calculator.math.MathType;
import org.solovyev.android.calculator.model.AndroidCalculatorEngine;
@@ -44,7 +45,9 @@ import org.solovyev.android.prefs.Preference;
import org.solovyev.android.prefs.StringPreference;
import java.text.DecimalFormatSymbols;
import java.util.EnumMap;
import java.util.Locale;
import java.util.Map;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -65,63 +68,93 @@ public final class Preferences {
public static final Preference<Integer> appVersion = IntegerPreference.of("application.version", -1);
public static final Preference<Integer> appOpenedCounter = IntegerPreference.of("app_opened_counter", 0);
public enum SimpleTheme {
default_theme(0, 0, null),
metro_blue_theme(R.layout.onscreen_layout, R.layout.widget_layout, Gui.Theme.metro_blue_theme),
material_theme(R.layout.onscreen_layout_material, R.layout.widget_layout_material, Gui.Theme.material_theme),
material_light_theme(R.layout.onscreen_layout_material_light, R.layout.widget_layout_material_light, Gui.Theme.material_light_theme);
@LayoutRes
private final int onscreenLayout;
@LayoutRes
private final int widgetLayout;
@Nullable
private final Gui.Theme appTheme;
@Nonnull
private final Map<Gui.Theme, SimpleTheme> cache = new EnumMap<>(Gui.Theme.class);
SimpleTheme(int onscreenLayout, int widgetLayout, @Nullable Gui.Theme appTheme) {
this.onscreenLayout = onscreenLayout;
this.widgetLayout = widgetLayout;
this.appTheme = appTheme;
}
public int getOnscreenLayout(@Nonnull Gui.Theme appTheme) {
return resolveThemeFor(appTheme).onscreenLayout;
}
public int getWidgetLayout(@Nonnull Gui.Theme appTheme) {
return resolveThemeFor(appTheme).widgetLayout;
}
@Nonnull
public SimpleTheme resolveThemeFor(@Nonnull Gui.Theme appTheme) {
if (this == default_theme) {
SimpleTheme theme = cache.get(appTheme);
if (theme == null) {
theme = lookUpThemeFor(appTheme);
cache.put(appTheme, theme);
}
return theme;
}
return this;
}
@Nonnull
private SimpleTheme lookUpThemeFor(@Nonnull Gui.Theme appTheme) {
Check.isTrue(this == default_theme);
// find direct match
for (SimpleTheme theme : values()) {
if (theme.appTheme == appTheme) {
return theme;
}
}
// for metro themes return metro theme
if (appTheme == Gui.Theme.metro_green_theme || appTheme == Gui.Theme.metro_purple_theme) {
return metro_blue_theme;
}
// for old themes return dark material
return material_theme;
}
@Nullable
public Gui.Theme getAppTheme() {
return appTheme;
}
}
public static class Widget {
public static final Preference<SimpleTheme> theme = StringPreference.ofEnum("widget.theme", SimpleTheme.default_theme, SimpleTheme.class);
@Nonnull
public static SimpleTheme getTheme(@Nonnull SharedPreferences preferences) {
return theme.getPreferenceNoError(preferences);
}
}
public static class Onscreen {
public static final Preference<Boolean> startOnBoot = BooleanPreference.of("onscreen_start_on_boot", false);
public static final Preference<Boolean> showAppIcon = BooleanPreference.of("onscreen_show_app_icon", true);
public static final Preference<Theme> theme = StringPreference.ofEnum("onscreen.theme", Theme.default_theme, Theme.class);
public enum Theme {
default_theme(0, null),
metro_blue_theme(R.layout.onscreen_layout, Gui.Theme.metro_blue_theme),
material_theme(R.layout.onscreen_layout_material, Gui.Theme.material_theme),
material_light_theme(R.layout.onscreen_layout_material_light, Gui.Theme.material_light_theme);
@LayoutRes
private final int layout;
@Nullable
private final Gui.Theme appTheme;
Theme(int layout, @Nullable Gui.Theme appTheme) {
this.layout = layout;
this.appTheme = appTheme;
}
public int getLayout(@Nonnull Gui.Theme appTheme) {
return resolveThemeFor(appTheme).layout;
}
@Nonnull
public Theme resolveThemeFor(@Nonnull Gui.Theme appTheme) {
if (this == default_theme) {
// find direct match
for (Theme theme : values()) {
if (theme.appTheme == appTheme) {
return theme;
}
}
// for metro themes return metro theme
if (appTheme == Gui.Theme.metro_green_theme || appTheme == Gui.Theme.metro_purple_theme) {
return metro_blue_theme;
}
// for old themes return dark material
return material_theme;
}
return this;
}
@Nullable
public Gui.Theme getAppTheme() {
return appTheme;
}
}
public static final Preference<SimpleTheme> theme = StringPreference.ofEnum("onscreen.theme", SimpleTheme.default_theme, SimpleTheme.class);
@Nonnull
public static Theme getTheme(@Nonnull SharedPreferences preferences) {
public static SimpleTheme getTheme(@Nonnull SharedPreferences preferences) {
return theme.getPreferenceNoError(preferences);
}
}
@@ -345,6 +378,8 @@ public final class Preferences {
applyDefaultPreference(preferences, Onscreen.startOnBoot);
applyDefaultPreference(preferences, Onscreen.theme);
applyDefaultPreference(preferences, Widget.theme);
applyDefaultPreference(preferences, Ga.initialReportDone);

View File

@@ -150,9 +150,9 @@ public class CalculatorOnscreenView {
final CalculatorOnscreenView result = new CalculatorOnscreenView();
final SharedPreferences p = App.getPreferences();
final Preferences.Onscreen.Theme theme = Preferences.Onscreen.theme.getPreferenceNoError(p);
final Preferences.SimpleTheme theme = Preferences.Onscreen.theme.getPreferenceNoError(p);
final Preferences.Gui.Theme appTheme = Preferences.Gui.theme.getPreferenceNoError(p);
result.root = View.inflate(context, theme.getLayout(appTheme), null);
result.root = View.inflate(context, theme.getOnscreenLayout(appTheme), null);
result.context = context;
result.viewListener = viewListener;

View File

@@ -35,6 +35,7 @@ public class PreferencesActivity extends BaseActivity implements SharedPreferenc
preferences.append(R.xml.preferences_plot, new PrefDef("screen-plot", R.string.prefs_graph_screen_title));
preferences.append(R.xml.preferences_other, new PrefDef("screen-other", R.string.c_prefs_other_category));
preferences.append(R.xml.preferences_onscreen, new PrefDef("screen-onscreen", R.string.prefs_onscreen_title));
preferences.append(R.xml.preferences_widget, new PrefDef("screen-widget", R.string.prefs_widget_title));
}
static class PrefDef {

View File

@@ -34,7 +34,15 @@ import android.os.Build;
import android.os.Bundle;
import android.text.Html;
import android.widget.RemoteViews;
import org.solovyev.android.calculator.*;
import org.solovyev.android.calculator.App;
import org.solovyev.android.calculator.CalculatorButton;
import org.solovyev.android.calculator.CalculatorButtons;
import org.solovyev.android.calculator.CalculatorDisplayViewState;
import org.solovyev.android.calculator.CalculatorEditorViewState;
import org.solovyev.android.calculator.Locator;
import org.solovyev.android.calculator.Preferences;
import org.solovyev.android.calculator.R;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -42,6 +50,7 @@ import javax.annotation.Nullable;
import static android.content.Intent.ACTION_CONFIGURATION_CHANGED;
import static org.solovyev.android.calculator.CalculatorBroadcaster.ACTION_DISPLAY_STATE_CHANGED;
import static org.solovyev.android.calculator.CalculatorBroadcaster.ACTION_EDITOR_STATE_CHANGED;
import static org.solovyev.android.calculator.CalculatorBroadcaster.ACTION_THEME_CHANGED;
import static org.solovyev.android.calculator.CalculatorReceiver.newButtonClickedIntent;
/**
@@ -56,36 +65,12 @@ public abstract class BaseCalculatorWidgetProvider extends AppWidgetProvider {
private static final String OPTION_APPWIDGET_HOST_CATEGORY = "appWidgetCategory";
private static final String ACTION_APPWIDGET_OPTIONS_CHANGED = "android.appwidget.action.APPWIDGET_UPDATE_OPTIONS";
/*
**********************************************************************
*
* FIELDS
*
**********************************************************************
*/
@Nullable
private String cursorColor;
/*
**********************************************************************
*
* CONSTRUCTORS
*
**********************************************************************
*/
protected BaseCalculatorWidgetProvider() {
}
/*
**********************************************************************
*
* METHODS
*
**********************************************************************
*/
@Override
public void onEnabled(Context context) {
super.onEnabled(context);
@@ -127,8 +112,9 @@ public abstract class BaseCalculatorWidgetProvider extends AppWidgetProvider {
final CalculatorDisplayViewState displayState = Locator.getInstance().getDisplay().getViewState();
final Resources resources = context.getResources();
final Preferences.SimpleTheme theme = App.getWidgetTheme().resolveThemeFor(App.getTheme());
for (int appWidgetId : appWidgetIds) {
final RemoteViews views = new RemoteViews(context.getPackageName(), getLayout(appWidgetManager, appWidgetId, resources));
final RemoteViews views = new RemoteViews(context.getPackageName(), getLayout(appWidgetManager, appWidgetId, resources, theme));
for (CalculatorButton button : CalculatorButton.values()) {
final PendingIntent pendingIntent = PendingIntent.getBroadcast(context, button.getButtonId(), newButtonClickedIntent(context, button), PendingIntent.FLAG_UPDATE_CURRENT);
@@ -138,7 +124,7 @@ public abstract class BaseCalculatorWidgetProvider extends AppWidgetProvider {
}
updateEditorState(context, views, editorState);
updateDisplayState(context, views, displayState);
updateDisplayState(context, views, displayState, theme);
CalculatorButtons.initMultiplicationButton(views);
@@ -146,15 +132,15 @@ public abstract class BaseCalculatorWidgetProvider extends AppWidgetProvider {
}
}
private int getLayout(@Nonnull AppWidgetManager appWidgetManager, int appWidgetId, @Nonnull Resources resources) {
private int getLayout(@Nonnull AppWidgetManager appWidgetManager, int appWidgetId, @Nonnull Resources resources, @Nonnull Preferences.SimpleTheme theme) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
return getLayoutJellyBean(appWidgetManager, appWidgetId, resources);
return getLayoutJellyBean(appWidgetManager, appWidgetId, resources, theme);
}
return R.layout.widget_layout;
return theme.getWidgetLayout(App.getTheme());
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
private int getLayoutJellyBean(AppWidgetManager appWidgetManager, int appWidgetId, Resources resources) {
private int getLayoutJellyBean(@Nonnull AppWidgetManager appWidgetManager, int appWidgetId, Resources resources, @Nonnull Preferences.SimpleTheme theme) {
final Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId);
if (options != null) {
@@ -164,7 +150,7 @@ public abstract class BaseCalculatorWidgetProvider extends AppWidgetProvider {
if (category != -1) {
// If the value is WIDGET_CATEGORY_KEYGUARD, it's a lockscreen widget
final boolean keyguard = category == WIDGET_CATEGORY_KEYGUARD;
if(keyguard) {
if (keyguard) {
final int minHeightDp = options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, -1);
final int minHeight = resources.getDimensionPixelSize(R.dimen.min_expanded_height_lock_screen);
final boolean expanded = (minHeightDp >= minHeight / resources.getDisplayMetrics().density);
@@ -176,11 +162,11 @@ public abstract class BaseCalculatorWidgetProvider extends AppWidgetProvider {
}
}
}
return R.layout.widget_layout;
return theme.getWidgetLayout(App.getTheme());
}
@Override
public void onReceive(Context context, Intent intent) {
public void onReceive(@Nonnull Context context, @Nonnull Intent intent) {
super.onReceive(context, intent);
final String action = intent.getAction();
@@ -192,15 +178,18 @@ public abstract class BaseCalculatorWidgetProvider extends AppWidgetProvider {
updateState(context);
} else if (ACTION_APPWIDGET_OPTIONS_CHANGED.equals(action)) {
updateState(context);
} else if (ACTION_THEME_CHANGED.equals(action)) {
updateState(context);
}
}
private void updateDisplayState(@Nonnull Context context, @Nonnull RemoteViews views, @Nonnull CalculatorDisplayViewState displayState) {
private void updateDisplayState(@Nonnull Context context, @Nonnull RemoteViews views, @Nonnull CalculatorDisplayViewState displayState, @Nonnull Preferences.SimpleTheme theme) {
final Resources resources = context.getResources();
if (displayState.isValid()) {
views.setTextViewText(R.id.calculator_display, displayState.getText());
views.setTextColor(R.id.calculator_display, context.getResources().getColor(R.color.cpp_text));
views.setTextColor(R.id.calculator_display, resources.getColor(theme == Preferences.SimpleTheme.material_light_theme ? R.color.cpp_text_inverse : R.color.cpp_text));
} else {
views.setTextColor(R.id.calculator_display, context.getResources().getColor(R.color.cpp_text_error));
views.setTextColor(R.id.calculator_display, resources.getColor(theme == Preferences.SimpleTheme.material_light_theme ? R.color.cpp_text_inverse_error : R.color.cpp_text_error));
}
}