import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.SharedPreferences;
+import android.content.res.XmlResourceParser;
+import android.preference.ListPreference;
+
import android.os.Handler;
import android.os.SystemClock;
import android.text.format.Time;
import android.util.MathUtils;
+import java.util.HashMap;
import java.util.TimeZone;
+import java.io.IOException;
+
+import org.xmlpull.v1.XmlPullParserException;
+import static org.xmlpull.v1.XmlPullParser.*;
+
+import com.android.wallpaper.R;
public class PolarClockWallpaper extends WallpaperService {
public static final String SHARED_PREFS_NAME = "polar_clock_settings";
-
+
public static final String PREF_SHOW_SECONDS = "show_seconds";
public static final String PREF_VARIABLE_LINE_WIDTH = "variable_line_width";
- public static final String PREF_CYCLE_COLORS = "cycle_colors";
+ public static final String PREF_PALETTE = "palette";
public static final int BACKGROUND_COLOR = 0xffffffff;
-
+
+ static abstract class ClockPalette {
+ public static ClockPalette parseXmlPaletteTag(XmlResourceParser xrp) {
+ String kind = xrp.getAttributeValue(null, "kind");
+ if ("cycling".equals(kind)) {
+ return CyclingClockPalette.parseXmlPaletteTag(xrp);
+ } else {
+ return FixedClockPalette.parseXmlPaletteTag(xrp);
+ }
+ }
+
+ public abstract int getBackgroundColor();
+
+ public abstract int getSecondColor(float forAngle);
+
+ public abstract int getMinuteColor(float forAngle);
+
+ public abstract int getHourColor(float forAngle);
+
+ public abstract int getDayColor(float forAngle);
+
+ public abstract int getMonthColor(float forAngle);
+
+ public abstract String getId();
+
+ }
+
+ static class FixedClockPalette extends ClockPalette {
+ protected String mId;
+ protected int mBackgroundColor;
+ protected int mSecondColor;
+ protected int mMinuteColor;
+ protected int mHourColor;
+ protected int mDayColor;
+ protected int mMonthColor;
+
+ private static FixedClockPalette sFallbackPalette = null;
+
+ public static FixedClockPalette getFallback() {
+ if (sFallbackPalette == null) {
+ sFallbackPalette = new FixedClockPalette();
+ sFallbackPalette.mId = "default";
+ sFallbackPalette.mBackgroundColor = Color.WHITE;
+ sFallbackPalette.mSecondColor =
+ sFallbackPalette.mMinuteColor =
+ sFallbackPalette.mHourColor =
+ sFallbackPalette.mDayColor =
+ sFallbackPalette.mMonthColor =
+ Color.BLACK;
+ }
+ return sFallbackPalette;
+ }
+
+ private FixedClockPalette() { }
+
+ @Override
+ public static ClockPalette parseXmlPaletteTag(XmlResourceParser xrp) {
+ final FixedClockPalette pal = new FixedClockPalette();
+ pal.mId = xrp.getAttributeValue(null, "id");
+ String val;
+ if ((val = xrp.getAttributeValue(null, "background")) != null)
+ pal.mBackgroundColor = Color.parseColor(val);
+ if ((val = xrp.getAttributeValue(null, "second")) != null)
+ pal.mSecondColor = Color.parseColor(val);
+ if ((val = xrp.getAttributeValue(null, "minute")) != null)
+ pal.mMinuteColor = Color.parseColor(val);
+ if ((val = xrp.getAttributeValue(null, "hour")) != null)
+ pal.mHourColor = Color.parseColor(val);
+ if ((val = xrp.getAttributeValue(null, "day")) != null)
+ pal.mDayColor = Color.parseColor(val);
+ if ((val = xrp.getAttributeValue(null, "month")) != null)
+ pal.mMonthColor = Color.parseColor(val);
+ return (pal.mId == null) ? null : pal;
+ }
+
+ @Override
+ public int getBackgroundColor() {
+ return mBackgroundColor;
+ }
+
+ @Override
+ public int getSecondColor(float forAngle) {
+ return mSecondColor;
+ }
+
+ @Override
+ public int getMinuteColor(float forAngle) {
+ return mMinuteColor;
+ }
+
+ @Override
+ public int getHourColor(float forAngle) {
+ return mHourColor;
+ }
+
+ @Override
+ public int getDayColor(float forAngle) {
+ return mDayColor;
+ }
+
+ @Override
+ public int getMonthColor(float forAngle) {
+ return mMonthColor;
+ }
+
+ @Override
+ public String getId() {
+ return mId;
+ }
+
+ }
+
+ static class CyclingClockPalette extends ClockPalette {
+ protected String mId;
+ protected int mBackgroundColor;
+ protected float mSaturation;
+ protected float mBrightness;
+
+ private static final int COLORS_CACHE_COUNT = 720;
+ private final int[] mColors = new int[COLORS_CACHE_COUNT];
+
+ private static CyclingClockPalette sFallbackPalette = null;
+
+ public static CyclingClockPalette getFallback() {
+ if (sFallbackPalette == null) {
+ sFallbackPalette = new CyclingClockPalette();
+ sFallbackPalette.mId = "default_c";
+ sFallbackPalette.mBackgroundColor = Color.WHITE;
+ sFallbackPalette.mSaturation = 0.8f;
+ sFallbackPalette.mBrightness = 0.9f;
+ sFallbackPalette.computeIntermediateColors();
+ }
+ return sFallbackPalette;
+ }
+
+ private CyclingClockPalette() { }
+
+ private void computeIntermediateColors() {
+ final int[] colors = mColors;
+ final int count = colors.length;
+ float invCount = 1.0f / (float) COLORS_CACHE_COUNT;
+ for (int i = 0; i < count; i++) {
+ colors[i] = Color.HSBtoColor(i * invCount, mSaturation, mBrightness);
+ }
+ }
+
+ @Override
+ public static ClockPalette parseXmlPaletteTag(XmlResourceParser xrp) {
+ final CyclingClockPalette pal = new CyclingClockPalette();
+ pal.mId = xrp.getAttributeValue(null, "id");
+ String val;
+ if ((val = xrp.getAttributeValue(null, "background")) != null)
+ pal.mBackgroundColor = Color.parseColor(val);
+ if ((val = xrp.getAttributeValue(null, "saturation")) != null)
+ pal.mSaturation = Float.parseFloat(val);
+ if ((val = xrp.getAttributeValue(null, "brightness")) != null)
+ pal.mBrightness = Float.parseFloat(val);
+ if (pal.mId == null) {
+ return null;
+ } else {
+ pal.computeIntermediateColors();
+ return pal;
+ }
+ }
+ @Override
+ public int getBackgroundColor() {
+ return mBackgroundColor;
+ }
+
+ @Override
+ public int getSecondColor(float forAngle) {
+ return mColors[((int) (forAngle * COLORS_CACHE_COUNT))];
+ }
+
+ @Override
+ public int getMinuteColor(float forAngle) {
+ return mColors[((int) (forAngle * COLORS_CACHE_COUNT))];
+ }
+
+ @Override
+ public int getHourColor(float forAngle) {
+ return mColors[((int) (forAngle * COLORS_CACHE_COUNT))];
+ }
+
+ @Override
+ public int getDayColor(float forAngle) {
+ return mColors[((int) (forAngle * COLORS_CACHE_COUNT))];
+ }
+
+ @Override
+ public int getMonthColor(float forAngle) {
+ return mColors[((int) (forAngle * COLORS_CACHE_COUNT))];
+ }
+
+ @Override
+ public String getId() {
+ return mId;
+ }
+ }
+
private final Handler mHandler = new Handler();
private IntentFilter mFilter;
class ClockEngine extends Engine
implements SharedPreferences.OnSharedPreferenceChangeListener {
- private static final float SATURATION = 0.8f;
- private static final float BRIGHTNESS = 0.9f;
-
private static final float SMALL_RING_THICKNESS = 8.0f;
private static final float MEDIUM_RING_THICKNESS = 16.0f;
private static final float LARGE_RING_THICKNESS = 32.0f;
private static final float SMALL_GAP = 14.0f;
private static final float LARGE_GAP = 38.0f;
- private static final int COLORS_CACHE_COUNT = 720;
-
- class ColorPalette {
- ColorPalette(String name, int bg, int s, int m, int h, int d, int o) {
- this.name = name; this.bg = bg;
- second = s; minute = m; hour = h; day = d; month = o;
- }
- public final String name;
- public final int bg;
- public final int second;
- public final int minute;
- public final int hour;
- public final int day;
- public final int month;
- }
-
- // XXX: make this an array of named palettes, selectable in prefs
- // via a spinner (bonus points: move to XML)
- private final ColorPalette mPalette = new ColorPalette(
- "MutedAndroid",
- 0xFF555555,
- 0xFF00FF00, 0xFF333333, 0xFF000000,
- 0xFF888888, 0xFFAAAAAA
- );
+ private final HashMap<String, ClockPalette> mPalettes = new HashMap<String, ClockPalette>();
+ private ClockPalette mPalette;
private SharedPreferences mPrefs;
private boolean mShowSeconds;
private boolean mVariableLineWidth;
- private boolean mCycleColors;
-
+
private boolean mWatcherRegistered;
private float mStartTime;
private Time mCalendar;
private final Paint mPaint = new Paint();
private final RectF mRect = new RectF();
- private final int[] mColors = new int[COLORS_CACHE_COUNT];
-
+
private float mOffsetX;
private final BroadcastReceiver mWatcher = new BroadcastReceiver() {
drawFrame();
}
};
-
+
private final Runnable mDrawClock = new Runnable() {
public void run() {
drawFrame();
private boolean mVisible;
ClockEngine() {
- final int[] colors = mColors;
- final int count = colors.length;
-
- float invCount = 1.0f / (float) COLORS_CACHE_COUNT;
- for (int i = 0; i < count; i++) {
- colors[i] = Color.HSBtoColor(i * invCount, SATURATION, BRIGHTNESS);
+ XmlResourceParser xrp = getResources().getXml(R.xml.polar_clock_palettes);
+ try {
+ int what = xrp.getEventType();
+ while (what != END_DOCUMENT) {
+ if (what == START_TAG) {
+ if ("palette".equals(xrp.getName())) {
+ ClockPalette pal = ClockPalette.parseXmlPaletteTag(xrp);
+ if (pal.getId() != null) {
+ mPalettes.put(pal.getId(), pal);
+ }
+ }
+ }
+ what = xrp.next();
+ }
+ } catch (IOException e) {
+ // XXX: Log?
+ } catch (XmlPullParserException e) {
+ // XXX: Log?
+ } finally {
+ xrp.close();
}
+
+ mPalette = CyclingClockPalette.getFallback();
}
@Override
mPrefs = PolarClockWallpaper.this.getSharedPreferences(SHARED_PREFS_NAME, 0);
mPrefs.registerOnSharedPreferenceChangeListener(this);
+
+ // load up user's settings
onSharedPreferenceChanged(mPrefs, null);
mCalendar = new Time();
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
String key) {
-
+
boolean changed = false;
- if (null == key || PREF_SHOW_SECONDS.equals(key)) {
+ if (key == null || PREF_SHOW_SECONDS.equals(key)) {
mShowSeconds = sharedPreferences.getBoolean(
PREF_SHOW_SECONDS, true);
changed = true;
}
- if (null == key || PREF_CYCLE_COLORS.equals(key)) {
- mCycleColors = sharedPreferences.getBoolean(
- PREF_CYCLE_COLORS, false);
- changed = true;
- }
- if (null == key || PREF_VARIABLE_LINE_WIDTH.equals(key)) {
+ if (key == null || PREF_VARIABLE_LINE_WIDTH.equals(key)) {
mVariableLineWidth = sharedPreferences.getBoolean(
PREF_VARIABLE_LINE_WIDTH, true);
changed = true;
}
+ if (key == null || PREF_PALETTE.equals(key)) {
+ String paletteId = sharedPreferences.getString(
+ PREF_PALETTE, "");
+ ClockPalette pal = mPalettes.get(paletteId);
+ if (pal != null) {
+ mPalette = pal;
+ changed = true;
+ }
+ }
if (mVisible && changed) {
drawFrame();
mOffsetX = xOffset;
drawFrame();
}
-
+
void drawFrame() {
+ if (mPalette == null) {
+ android.util.Log.w("PolarClockWallpaper", "no palette?!");
+ return;
+ }
+
final SurfaceHolder holder = getSurfaceHolder();
final Rect frame = holder.getSurfaceFrame();
final int width = frame.width();
if (c != null) {
final Time calendar = mCalendar;
final Paint paint = mPaint;
- final int[] colors = mColors;
calendar.setToNow();
calendar.normalize(false);
int s = width / 2;
int t = height / 2;
- if (mCycleColors) {
- c.drawColor(BACKGROUND_COLOR);
- } else {
- c.drawColor(mPalette.bg);
- }
+ c.drawColor(mPalette.getBackgroundColor());
+
c.translate(s + MathUtils.lerp(s, -s, mOffsetX), t);
c.rotate(-90.0f);
if (height < width) {
float angle;
float lastRingThickness = DEFAULT_RING_THICKNESS;
-
+
if (mShowSeconds) {
// Draw seconds
angle = ((mStartTime + SystemClock.elapsedRealtime()) % 60000) / 60000.0f;
if (angle < 0) angle = -angle;
- if (mCycleColors) {
- paint.setColor(colors[((int) (angle * COLORS_CACHE_COUNT))]);
- } else {
- paint.setColor(mPalette.second);
- }
+
+ paint.setColor(mPalette.getSecondColor(angle));
if (mVariableLineWidth) {
lastRingThickness = SMALL_RING_THICKNESS;
rect.set(-size, -size, size, size);
angle = ((calendar.minute * 60.0f + calendar.second) % 3600) / 3600.0f;
- if (mCycleColors) {
- paint.setColor(colors[((int) (angle * COLORS_CACHE_COUNT))]);
- } else {
- paint.setColor(mPalette.minute);
- }
+ paint.setColor(mPalette.getMinuteColor(angle));
if (mVariableLineWidth) {
lastRingThickness = MEDIUM_RING_THICKNESS;
paint.setStrokeWidth(lastRingThickness);
}
c.drawArc(rect, 0.0f, angle * 360.0f, false, paint);
-
+
// Draw hours
size -= (SMALL_GAP + lastRingThickness);
rect.set(-size, -size, size, size);
angle = ((calendar.hour * 60.0f + calendar.minute) % 1440) / 1440.0f;
- if (mCycleColors) {
- paint.setColor(colors[((int) (angle * COLORS_CACHE_COUNT))]);
- } else {
- paint.setColor(mPalette.hour);
- }
+ paint.setColor(mPalette.getHourColor(angle));
+
if (mVariableLineWidth) {
lastRingThickness = LARGE_RING_THICKNESS;
paint.setStrokeWidth(lastRingThickness);
angle = (calendar.monthDay - 1) /
(float) (calendar.getActualMaximum(Time.MONTH_DAY) - 1);
- if (mCycleColors) {
- paint.setColor(colors[((int) (angle * COLORS_CACHE_COUNT))]);
- } else {
- paint.setColor(mPalette.day);
- }
+ paint.setColor(mPalette.getDayColor(angle));
+
if (mVariableLineWidth) {
lastRingThickness = MEDIUM_RING_THICKNESS;
paint.setStrokeWidth(lastRingThickness);
rect.set(-size, -size, size, size);
angle = (calendar.month - 1) / 11.0f;
- if (mCycleColors) {
- paint.setColor(colors[((int) (angle * COLORS_CACHE_COUNT))]);
- } else {
- paint.setColor(mPalette.month);
- }
+
+ paint.setColor(mPalette.getMonthColor(angle));
+
if (mVariableLineWidth) {
lastRingThickness = LARGE_RING_THICKNESS;
paint.setStrokeWidth(lastRingThickness);