diff --git a/mobile/android/base/moz.build b/mobile/android/base/moz.build index ea2b81f38617..ef964a658896 100644 --- a/mobile/android/base/moz.build +++ b/mobile/android/base/moz.build @@ -480,6 +480,19 @@ gbjar.sources += [ 'widget/TwoWayView.java', 'ZoomConstraints.java', ] +# The following sources are checked in to version control but +# generated by a script (widget/generate_themed_views.py). If you're +# editing this list, make sure to edit that script. +gbjar.sources += [ + 'widget/ThemedEditText.java', + 'widget/ThemedImageButton.java', + 'widget/ThemedImageView.java', + 'widget/ThemedLinearLayout.java', + 'widget/ThemedRelativeLayout.java', + 'widget/ThemedTextSwitcher.java', + 'widget/ThemedTextView.java', + 'widget/ThemedView.java', +] gbjar.sources += [ thirdparty_source_dir + f for f in [ 'com/googlecode/eyesfree/braille/selfbraille/ISelfBrailleService.java', 'com/googlecode/eyesfree/braille/selfbraille/SelfBrailleClient.java', @@ -488,14 +501,6 @@ gbjar.sources += [ thirdparty_source_dir + f for f in [ android_package_dir = CONFIG['ANDROID_PACKAGE_NAME'].replace('.', '/') gbjar.generated_sources += [ 'org/mozilla/gecko/SysInfo.java', - 'org/mozilla/gecko/widget/ThemedEditText.java', - 'org/mozilla/gecko/widget/ThemedImageButton.java', - 'org/mozilla/gecko/widget/ThemedImageView.java', - 'org/mozilla/gecko/widget/ThemedLinearLayout.java', - 'org/mozilla/gecko/widget/ThemedRelativeLayout.java', - 'org/mozilla/gecko/widget/ThemedTextSwitcher.java', - 'org/mozilla/gecko/widget/ThemedTextView.java', - 'org/mozilla/gecko/widget/ThemedView.java', ] if CONFIG['MOZ_CRASHREPORTER']: gbjar.sources += [ 'CrashReporter.java' ] diff --git a/mobile/android/base/widget/ThemedEditText.java b/mobile/android/base/widget/ThemedEditText.java new file mode 100644 index 000000000000..382702bee4bb --- /dev/null +++ b/mobile/android/base/widget/ThemedEditText.java @@ -0,0 +1,155 @@ +// This file is generated by generate_themed_views.py; do not edit. + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.gecko.widget; + +import org.mozilla.gecko.GeckoApplication; +import org.mozilla.gecko.LightweightTheme; +import org.mozilla.gecko.R; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.drawable.ColorDrawable; +import android.util.AttributeSet; + +public class ThemedEditText extends android.widget.EditText + implements LightweightTheme.OnChangeListener { + private LightweightTheme mTheme; + + private static final int[] STATE_PRIVATE_MODE = { R.attr.state_private }; + private static final int[] STATE_LIGHT = { R.attr.state_light }; + private static final int[] STATE_DARK = { R.attr.state_dark }; + + protected static final int[] PRIVATE_PRESSED_STATE_SET = { R.attr.state_private, android.R.attr.state_pressed }; + protected static final int[] PRIVATE_FOCUSED_STATE_SET = { R.attr.state_private, android.R.attr.state_focused }; + protected static final int[] PRIVATE_STATE_SET = { R.attr.state_private }; + + private boolean mIsPrivate; + private boolean mIsLight; + private boolean mIsDark; + private boolean mAutoUpdateTheme; // always false if there's no theme. + + public ThemedEditText(Context context, AttributeSet attrs) { + super(context, attrs); + initialize(context, attrs); + } + + public ThemedEditText(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + initialize(context, attrs); + } + + private void initialize(final Context context, final AttributeSet attrs) { + // The theme can be null, particularly for webapps: Bug 1089266. + mTheme = ((GeckoApplication) context.getApplicationContext()).getLightweightTheme(); + + final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.LightweightTheme); + mAutoUpdateTheme = mTheme != null && a.getBoolean(R.styleable.LightweightTheme_autoUpdateTheme, true); + a.recycle(); + } + + @Override + public void onAttachedToWindow() { + super.onAttachedToWindow(); + + if (mAutoUpdateTheme) + mTheme.addListener(this); + } + + @Override + public void onDetachedFromWindow() { + super.onDetachedFromWindow(); + + if (mAutoUpdateTheme) + mTheme.removeListener(this); + } + + @Override + public int[] onCreateDrawableState(int extraSpace) { + final int[] drawableState = super.onCreateDrawableState(extraSpace + 1); + + if (mIsPrivate) + mergeDrawableStates(drawableState, STATE_PRIVATE_MODE); + else if (mIsLight) + mergeDrawableStates(drawableState, STATE_LIGHT); + else if (mIsDark) + mergeDrawableStates(drawableState, STATE_DARK); + + return drawableState; + } + + @Override + public void onLightweightThemeChanged() { + if (mAutoUpdateTheme && mTheme.isEnabled()) + setTheme(mTheme.isLightTheme()); + } + + @Override + public void onLightweightThemeReset() { + if (mAutoUpdateTheme) + resetTheme(); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + onLightweightThemeChanged(); + } + + public boolean isPrivateMode() { + return mIsPrivate; + } + + public void setPrivateMode(boolean isPrivate) { + if (mIsPrivate != isPrivate) { + mIsPrivate = isPrivate; + refreshDrawableState(); + } + } + + public void setTheme(boolean isLight) { + // Set the theme only if it is different from existing theme. + if ((isLight && mIsLight != isLight) || + (!isLight && mIsDark == isLight)) { + if (isLight) { + mIsLight = true; + mIsDark = false; + } else { + mIsLight = false; + mIsDark = true; + } + + refreshDrawableState(); + } + } + + public void resetTheme() { + if (mIsLight || mIsDark) { + mIsLight = false; + mIsDark = false; + refreshDrawableState(); + } + } + + public void setAutoUpdateTheme(boolean autoUpdateTheme) { + if (mTheme == null) { + return; + } + + if (mAutoUpdateTheme != autoUpdateTheme) { + mAutoUpdateTheme = autoUpdateTheme; + + if (mAutoUpdateTheme) + mTheme.addListener(this); + else + mTheme.removeListener(this); + } + } + + public ColorDrawable getColorDrawable(int id) { + return new ColorDrawable(getResources().getColor(id)); + } +} diff --git a/mobile/android/base/widget/ThemedEditText.java.in b/mobile/android/base/widget/ThemedEditText.java.in deleted file mode 100644 index 3349eb88ac23..000000000000 --- a/mobile/android/base/widget/ThemedEditText.java.in +++ /dev/null @@ -1,5 +0,0 @@ -//#filter substitution -//#define VIEW_NAME_SUFFIX EditText -//#define BASE_TYPE android.widget.EditText -//#define STYLE_CONSTRUCTOR 1 -//#include ThemedView.java.frag diff --git a/mobile/android/base/widget/ThemedImageButton.java b/mobile/android/base/widget/ThemedImageButton.java new file mode 100644 index 000000000000..9d48d33d3b45 --- /dev/null +++ b/mobile/android/base/widget/ThemedImageButton.java @@ -0,0 +1,155 @@ +// This file is generated by generate_themed_views.py; do not edit. + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.gecko.widget; + +import org.mozilla.gecko.GeckoApplication; +import org.mozilla.gecko.LightweightTheme; +import org.mozilla.gecko.R; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.drawable.ColorDrawable; +import android.util.AttributeSet; + +public class ThemedImageButton extends android.widget.ImageButton + implements LightweightTheme.OnChangeListener { + private LightweightTheme mTheme; + + private static final int[] STATE_PRIVATE_MODE = { R.attr.state_private }; + private static final int[] STATE_LIGHT = { R.attr.state_light }; + private static final int[] STATE_DARK = { R.attr.state_dark }; + + protected static final int[] PRIVATE_PRESSED_STATE_SET = { R.attr.state_private, android.R.attr.state_pressed }; + protected static final int[] PRIVATE_FOCUSED_STATE_SET = { R.attr.state_private, android.R.attr.state_focused }; + protected static final int[] PRIVATE_STATE_SET = { R.attr.state_private }; + + private boolean mIsPrivate; + private boolean mIsLight; + private boolean mIsDark; + private boolean mAutoUpdateTheme; // always false if there's no theme. + + public ThemedImageButton(Context context, AttributeSet attrs) { + super(context, attrs); + initialize(context, attrs); + } + + public ThemedImageButton(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + initialize(context, attrs); + } + + private void initialize(final Context context, final AttributeSet attrs) { + // The theme can be null, particularly for webapps: Bug 1089266. + mTheme = ((GeckoApplication) context.getApplicationContext()).getLightweightTheme(); + + final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.LightweightTheme); + mAutoUpdateTheme = mTheme != null && a.getBoolean(R.styleable.LightweightTheme_autoUpdateTheme, true); + a.recycle(); + } + + @Override + public void onAttachedToWindow() { + super.onAttachedToWindow(); + + if (mAutoUpdateTheme) + mTheme.addListener(this); + } + + @Override + public void onDetachedFromWindow() { + super.onDetachedFromWindow(); + + if (mAutoUpdateTheme) + mTheme.removeListener(this); + } + + @Override + public int[] onCreateDrawableState(int extraSpace) { + final int[] drawableState = super.onCreateDrawableState(extraSpace + 1); + + if (mIsPrivate) + mergeDrawableStates(drawableState, STATE_PRIVATE_MODE); + else if (mIsLight) + mergeDrawableStates(drawableState, STATE_LIGHT); + else if (mIsDark) + mergeDrawableStates(drawableState, STATE_DARK); + + return drawableState; + } + + @Override + public void onLightweightThemeChanged() { + if (mAutoUpdateTheme && mTheme.isEnabled()) + setTheme(mTheme.isLightTheme()); + } + + @Override + public void onLightweightThemeReset() { + if (mAutoUpdateTheme) + resetTheme(); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + onLightweightThemeChanged(); + } + + public boolean isPrivateMode() { + return mIsPrivate; + } + + public void setPrivateMode(boolean isPrivate) { + if (mIsPrivate != isPrivate) { + mIsPrivate = isPrivate; + refreshDrawableState(); + } + } + + public void setTheme(boolean isLight) { + // Set the theme only if it is different from existing theme. + if ((isLight && mIsLight != isLight) || + (!isLight && mIsDark == isLight)) { + if (isLight) { + mIsLight = true; + mIsDark = false; + } else { + mIsLight = false; + mIsDark = true; + } + + refreshDrawableState(); + } + } + + public void resetTheme() { + if (mIsLight || mIsDark) { + mIsLight = false; + mIsDark = false; + refreshDrawableState(); + } + } + + public void setAutoUpdateTheme(boolean autoUpdateTheme) { + if (mTheme == null) { + return; + } + + if (mAutoUpdateTheme != autoUpdateTheme) { + mAutoUpdateTheme = autoUpdateTheme; + + if (mAutoUpdateTheme) + mTheme.addListener(this); + else + mTheme.removeListener(this); + } + } + + public ColorDrawable getColorDrawable(int id) { + return new ColorDrawable(getResources().getColor(id)); + } +} diff --git a/mobile/android/base/widget/ThemedImageButton.java.in b/mobile/android/base/widget/ThemedImageButton.java.in deleted file mode 100644 index dce15019a034..000000000000 --- a/mobile/android/base/widget/ThemedImageButton.java.in +++ /dev/null @@ -1,5 +0,0 @@ -//#filter substitution -//#define VIEW_NAME_SUFFIX ImageButton -//#define BASE_TYPE android.widget.ImageButton -//#define STYLE_CONSTRUCTOR 1 -//#include ThemedView.java.frag diff --git a/mobile/android/base/widget/ThemedImageView.java b/mobile/android/base/widget/ThemedImageView.java new file mode 100644 index 000000000000..b925792a3aab --- /dev/null +++ b/mobile/android/base/widget/ThemedImageView.java @@ -0,0 +1,155 @@ +// This file is generated by generate_themed_views.py; do not edit. + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.gecko.widget; + +import org.mozilla.gecko.GeckoApplication; +import org.mozilla.gecko.LightweightTheme; +import org.mozilla.gecko.R; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.drawable.ColorDrawable; +import android.util.AttributeSet; + +public class ThemedImageView extends android.widget.ImageView + implements LightweightTheme.OnChangeListener { + private LightweightTheme mTheme; + + private static final int[] STATE_PRIVATE_MODE = { R.attr.state_private }; + private static final int[] STATE_LIGHT = { R.attr.state_light }; + private static final int[] STATE_DARK = { R.attr.state_dark }; + + protected static final int[] PRIVATE_PRESSED_STATE_SET = { R.attr.state_private, android.R.attr.state_pressed }; + protected static final int[] PRIVATE_FOCUSED_STATE_SET = { R.attr.state_private, android.R.attr.state_focused }; + protected static final int[] PRIVATE_STATE_SET = { R.attr.state_private }; + + private boolean mIsPrivate; + private boolean mIsLight; + private boolean mIsDark; + private boolean mAutoUpdateTheme; // always false if there's no theme. + + public ThemedImageView(Context context, AttributeSet attrs) { + super(context, attrs); + initialize(context, attrs); + } + + public ThemedImageView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + initialize(context, attrs); + } + + private void initialize(final Context context, final AttributeSet attrs) { + // The theme can be null, particularly for webapps: Bug 1089266. + mTheme = ((GeckoApplication) context.getApplicationContext()).getLightweightTheme(); + + final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.LightweightTheme); + mAutoUpdateTheme = mTheme != null && a.getBoolean(R.styleable.LightweightTheme_autoUpdateTheme, true); + a.recycle(); + } + + @Override + public void onAttachedToWindow() { + super.onAttachedToWindow(); + + if (mAutoUpdateTheme) + mTheme.addListener(this); + } + + @Override + public void onDetachedFromWindow() { + super.onDetachedFromWindow(); + + if (mAutoUpdateTheme) + mTheme.removeListener(this); + } + + @Override + public int[] onCreateDrawableState(int extraSpace) { + final int[] drawableState = super.onCreateDrawableState(extraSpace + 1); + + if (mIsPrivate) + mergeDrawableStates(drawableState, STATE_PRIVATE_MODE); + else if (mIsLight) + mergeDrawableStates(drawableState, STATE_LIGHT); + else if (mIsDark) + mergeDrawableStates(drawableState, STATE_DARK); + + return drawableState; + } + + @Override + public void onLightweightThemeChanged() { + if (mAutoUpdateTheme && mTheme.isEnabled()) + setTheme(mTheme.isLightTheme()); + } + + @Override + public void onLightweightThemeReset() { + if (mAutoUpdateTheme) + resetTheme(); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + onLightweightThemeChanged(); + } + + public boolean isPrivateMode() { + return mIsPrivate; + } + + public void setPrivateMode(boolean isPrivate) { + if (mIsPrivate != isPrivate) { + mIsPrivate = isPrivate; + refreshDrawableState(); + } + } + + public void setTheme(boolean isLight) { + // Set the theme only if it is different from existing theme. + if ((isLight && mIsLight != isLight) || + (!isLight && mIsDark == isLight)) { + if (isLight) { + mIsLight = true; + mIsDark = false; + } else { + mIsLight = false; + mIsDark = true; + } + + refreshDrawableState(); + } + } + + public void resetTheme() { + if (mIsLight || mIsDark) { + mIsLight = false; + mIsDark = false; + refreshDrawableState(); + } + } + + public void setAutoUpdateTheme(boolean autoUpdateTheme) { + if (mTheme == null) { + return; + } + + if (mAutoUpdateTheme != autoUpdateTheme) { + mAutoUpdateTheme = autoUpdateTheme; + + if (mAutoUpdateTheme) + mTheme.addListener(this); + else + mTheme.removeListener(this); + } + } + + public ColorDrawable getColorDrawable(int id) { + return new ColorDrawable(getResources().getColor(id)); + } +} diff --git a/mobile/android/base/widget/ThemedImageView.java.in b/mobile/android/base/widget/ThemedImageView.java.in deleted file mode 100644 index 3ada26e54289..000000000000 --- a/mobile/android/base/widget/ThemedImageView.java.in +++ /dev/null @@ -1,5 +0,0 @@ -//#filter substitution -//#define VIEW_NAME_SUFFIX ImageView -//#define BASE_TYPE android.widget.ImageView -//#define STYLE_CONSTRUCTOR 1 -//#include ThemedView.java.frag diff --git a/mobile/android/base/widget/ThemedLinearLayout.java b/mobile/android/base/widget/ThemedLinearLayout.java new file mode 100644 index 000000000000..de62102681a3 --- /dev/null +++ b/mobile/android/base/widget/ThemedLinearLayout.java @@ -0,0 +1,150 @@ +// This file is generated by generate_themed_views.py; do not edit. + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.gecko.widget; + +import org.mozilla.gecko.GeckoApplication; +import org.mozilla.gecko.LightweightTheme; +import org.mozilla.gecko.R; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.drawable.ColorDrawable; +import android.util.AttributeSet; + +public class ThemedLinearLayout extends android.widget.LinearLayout + implements LightweightTheme.OnChangeListener { + private LightweightTheme mTheme; + + private static final int[] STATE_PRIVATE_MODE = { R.attr.state_private }; + private static final int[] STATE_LIGHT = { R.attr.state_light }; + private static final int[] STATE_DARK = { R.attr.state_dark }; + + protected static final int[] PRIVATE_PRESSED_STATE_SET = { R.attr.state_private, android.R.attr.state_pressed }; + protected static final int[] PRIVATE_FOCUSED_STATE_SET = { R.attr.state_private, android.R.attr.state_focused }; + protected static final int[] PRIVATE_STATE_SET = { R.attr.state_private }; + + private boolean mIsPrivate; + private boolean mIsLight; + private boolean mIsDark; + private boolean mAutoUpdateTheme; // always false if there's no theme. + + public ThemedLinearLayout(Context context, AttributeSet attrs) { + super(context, attrs); + initialize(context, attrs); + } + + private void initialize(final Context context, final AttributeSet attrs) { + // The theme can be null, particularly for webapps: Bug 1089266. + mTheme = ((GeckoApplication) context.getApplicationContext()).getLightweightTheme(); + + final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.LightweightTheme); + mAutoUpdateTheme = mTheme != null && a.getBoolean(R.styleable.LightweightTheme_autoUpdateTheme, true); + a.recycle(); + } + + @Override + public void onAttachedToWindow() { + super.onAttachedToWindow(); + + if (mAutoUpdateTheme) + mTheme.addListener(this); + } + + @Override + public void onDetachedFromWindow() { + super.onDetachedFromWindow(); + + if (mAutoUpdateTheme) + mTheme.removeListener(this); + } + + @Override + public int[] onCreateDrawableState(int extraSpace) { + final int[] drawableState = super.onCreateDrawableState(extraSpace + 1); + + if (mIsPrivate) + mergeDrawableStates(drawableState, STATE_PRIVATE_MODE); + else if (mIsLight) + mergeDrawableStates(drawableState, STATE_LIGHT); + else if (mIsDark) + mergeDrawableStates(drawableState, STATE_DARK); + + return drawableState; + } + + @Override + public void onLightweightThemeChanged() { + if (mAutoUpdateTheme && mTheme.isEnabled()) + setTheme(mTheme.isLightTheme()); + } + + @Override + public void onLightweightThemeReset() { + if (mAutoUpdateTheme) + resetTheme(); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + onLightweightThemeChanged(); + } + + public boolean isPrivateMode() { + return mIsPrivate; + } + + public void setPrivateMode(boolean isPrivate) { + if (mIsPrivate != isPrivate) { + mIsPrivate = isPrivate; + refreshDrawableState(); + } + } + + public void setTheme(boolean isLight) { + // Set the theme only if it is different from existing theme. + if ((isLight && mIsLight != isLight) || + (!isLight && mIsDark == isLight)) { + if (isLight) { + mIsLight = true; + mIsDark = false; + } else { + mIsLight = false; + mIsDark = true; + } + + refreshDrawableState(); + } + } + + public void resetTheme() { + if (mIsLight || mIsDark) { + mIsLight = false; + mIsDark = false; + refreshDrawableState(); + } + } + + public void setAutoUpdateTheme(boolean autoUpdateTheme) { + if (mTheme == null) { + return; + } + + if (mAutoUpdateTheme != autoUpdateTheme) { + mAutoUpdateTheme = autoUpdateTheme; + + if (mAutoUpdateTheme) + mTheme.addListener(this); + else + mTheme.removeListener(this); + } + } + + public ColorDrawable getColorDrawable(int id) { + return new ColorDrawable(getResources().getColor(id)); + } +} diff --git a/mobile/android/base/widget/ThemedLinearLayout.java.in b/mobile/android/base/widget/ThemedLinearLayout.java.in deleted file mode 100644 index 5f4d4c6b67f7..000000000000 --- a/mobile/android/base/widget/ThemedLinearLayout.java.in +++ /dev/null @@ -1,4 +0,0 @@ -//#filter substitution -//#define VIEW_NAME_SUFFIX LinearLayout -//#define BASE_TYPE android.widget.LinearLayout -//#include ThemedView.java.frag diff --git a/mobile/android/base/widget/ThemedRelativeLayout.java b/mobile/android/base/widget/ThemedRelativeLayout.java new file mode 100644 index 000000000000..55946d646307 --- /dev/null +++ b/mobile/android/base/widget/ThemedRelativeLayout.java @@ -0,0 +1,155 @@ +// This file is generated by generate_themed_views.py; do not edit. + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.gecko.widget; + +import org.mozilla.gecko.GeckoApplication; +import org.mozilla.gecko.LightweightTheme; +import org.mozilla.gecko.R; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.drawable.ColorDrawable; +import android.util.AttributeSet; + +public class ThemedRelativeLayout extends android.widget.RelativeLayout + implements LightweightTheme.OnChangeListener { + private LightweightTheme mTheme; + + private static final int[] STATE_PRIVATE_MODE = { R.attr.state_private }; + private static final int[] STATE_LIGHT = { R.attr.state_light }; + private static final int[] STATE_DARK = { R.attr.state_dark }; + + protected static final int[] PRIVATE_PRESSED_STATE_SET = { R.attr.state_private, android.R.attr.state_pressed }; + protected static final int[] PRIVATE_FOCUSED_STATE_SET = { R.attr.state_private, android.R.attr.state_focused }; + protected static final int[] PRIVATE_STATE_SET = { R.attr.state_private }; + + private boolean mIsPrivate; + private boolean mIsLight; + private boolean mIsDark; + private boolean mAutoUpdateTheme; // always false if there's no theme. + + public ThemedRelativeLayout(Context context, AttributeSet attrs) { + super(context, attrs); + initialize(context, attrs); + } + + public ThemedRelativeLayout(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + initialize(context, attrs); + } + + private void initialize(final Context context, final AttributeSet attrs) { + // The theme can be null, particularly for webapps: Bug 1089266. + mTheme = ((GeckoApplication) context.getApplicationContext()).getLightweightTheme(); + + final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.LightweightTheme); + mAutoUpdateTheme = mTheme != null && a.getBoolean(R.styleable.LightweightTheme_autoUpdateTheme, true); + a.recycle(); + } + + @Override + public void onAttachedToWindow() { + super.onAttachedToWindow(); + + if (mAutoUpdateTheme) + mTheme.addListener(this); + } + + @Override + public void onDetachedFromWindow() { + super.onDetachedFromWindow(); + + if (mAutoUpdateTheme) + mTheme.removeListener(this); + } + + @Override + public int[] onCreateDrawableState(int extraSpace) { + final int[] drawableState = super.onCreateDrawableState(extraSpace + 1); + + if (mIsPrivate) + mergeDrawableStates(drawableState, STATE_PRIVATE_MODE); + else if (mIsLight) + mergeDrawableStates(drawableState, STATE_LIGHT); + else if (mIsDark) + mergeDrawableStates(drawableState, STATE_DARK); + + return drawableState; + } + + @Override + public void onLightweightThemeChanged() { + if (mAutoUpdateTheme && mTheme.isEnabled()) + setTheme(mTheme.isLightTheme()); + } + + @Override + public void onLightweightThemeReset() { + if (mAutoUpdateTheme) + resetTheme(); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + onLightweightThemeChanged(); + } + + public boolean isPrivateMode() { + return mIsPrivate; + } + + public void setPrivateMode(boolean isPrivate) { + if (mIsPrivate != isPrivate) { + mIsPrivate = isPrivate; + refreshDrawableState(); + } + } + + public void setTheme(boolean isLight) { + // Set the theme only if it is different from existing theme. + if ((isLight && mIsLight != isLight) || + (!isLight && mIsDark == isLight)) { + if (isLight) { + mIsLight = true; + mIsDark = false; + } else { + mIsLight = false; + mIsDark = true; + } + + refreshDrawableState(); + } + } + + public void resetTheme() { + if (mIsLight || mIsDark) { + mIsLight = false; + mIsDark = false; + refreshDrawableState(); + } + } + + public void setAutoUpdateTheme(boolean autoUpdateTheme) { + if (mTheme == null) { + return; + } + + if (mAutoUpdateTheme != autoUpdateTheme) { + mAutoUpdateTheme = autoUpdateTheme; + + if (mAutoUpdateTheme) + mTheme.addListener(this); + else + mTheme.removeListener(this); + } + } + + public ColorDrawable getColorDrawable(int id) { + return new ColorDrawable(getResources().getColor(id)); + } +} diff --git a/mobile/android/base/widget/ThemedRelativeLayout.java.in b/mobile/android/base/widget/ThemedRelativeLayout.java.in deleted file mode 100644 index deef14c6a8e1..000000000000 --- a/mobile/android/base/widget/ThemedRelativeLayout.java.in +++ /dev/null @@ -1,5 +0,0 @@ -//#filter substitution -//#define VIEW_NAME_SUFFIX RelativeLayout -//#define BASE_TYPE android.widget.RelativeLayout -//#define STYLE_CONSTRUCTOR 1 -//#include ThemedView.java.frag diff --git a/mobile/android/base/widget/ThemedTextSwitcher.java b/mobile/android/base/widget/ThemedTextSwitcher.java new file mode 100644 index 000000000000..7477aca14db9 --- /dev/null +++ b/mobile/android/base/widget/ThemedTextSwitcher.java @@ -0,0 +1,150 @@ +// This file is generated by generate_themed_views.py; do not edit. + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.gecko.widget; + +import org.mozilla.gecko.GeckoApplication; +import org.mozilla.gecko.LightweightTheme; +import org.mozilla.gecko.R; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.drawable.ColorDrawable; +import android.util.AttributeSet; + +public class ThemedTextSwitcher extends android.widget.TextSwitcher + implements LightweightTheme.OnChangeListener { + private LightweightTheme mTheme; + + private static final int[] STATE_PRIVATE_MODE = { R.attr.state_private }; + private static final int[] STATE_LIGHT = { R.attr.state_light }; + private static final int[] STATE_DARK = { R.attr.state_dark }; + + protected static final int[] PRIVATE_PRESSED_STATE_SET = { R.attr.state_private, android.R.attr.state_pressed }; + protected static final int[] PRIVATE_FOCUSED_STATE_SET = { R.attr.state_private, android.R.attr.state_focused }; + protected static final int[] PRIVATE_STATE_SET = { R.attr.state_private }; + + private boolean mIsPrivate; + private boolean mIsLight; + private boolean mIsDark; + private boolean mAutoUpdateTheme; // always false if there's no theme. + + public ThemedTextSwitcher(Context context, AttributeSet attrs) { + super(context, attrs); + initialize(context, attrs); + } + + private void initialize(final Context context, final AttributeSet attrs) { + // The theme can be null, particularly for webapps: Bug 1089266. + mTheme = ((GeckoApplication) context.getApplicationContext()).getLightweightTheme(); + + final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.LightweightTheme); + mAutoUpdateTheme = mTheme != null && a.getBoolean(R.styleable.LightweightTheme_autoUpdateTheme, true); + a.recycle(); + } + + @Override + public void onAttachedToWindow() { + super.onAttachedToWindow(); + + if (mAutoUpdateTheme) + mTheme.addListener(this); + } + + @Override + public void onDetachedFromWindow() { + super.onDetachedFromWindow(); + + if (mAutoUpdateTheme) + mTheme.removeListener(this); + } + + @Override + public int[] onCreateDrawableState(int extraSpace) { + final int[] drawableState = super.onCreateDrawableState(extraSpace + 1); + + if (mIsPrivate) + mergeDrawableStates(drawableState, STATE_PRIVATE_MODE); + else if (mIsLight) + mergeDrawableStates(drawableState, STATE_LIGHT); + else if (mIsDark) + mergeDrawableStates(drawableState, STATE_DARK); + + return drawableState; + } + + @Override + public void onLightweightThemeChanged() { + if (mAutoUpdateTheme && mTheme.isEnabled()) + setTheme(mTheme.isLightTheme()); + } + + @Override + public void onLightweightThemeReset() { + if (mAutoUpdateTheme) + resetTheme(); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + onLightweightThemeChanged(); + } + + public boolean isPrivateMode() { + return mIsPrivate; + } + + public void setPrivateMode(boolean isPrivate) { + if (mIsPrivate != isPrivate) { + mIsPrivate = isPrivate; + refreshDrawableState(); + } + } + + public void setTheme(boolean isLight) { + // Set the theme only if it is different from existing theme. + if ((isLight && mIsLight != isLight) || + (!isLight && mIsDark == isLight)) { + if (isLight) { + mIsLight = true; + mIsDark = false; + } else { + mIsLight = false; + mIsDark = true; + } + + refreshDrawableState(); + } + } + + public void resetTheme() { + if (mIsLight || mIsDark) { + mIsLight = false; + mIsDark = false; + refreshDrawableState(); + } + } + + public void setAutoUpdateTheme(boolean autoUpdateTheme) { + if (mTheme == null) { + return; + } + + if (mAutoUpdateTheme != autoUpdateTheme) { + mAutoUpdateTheme = autoUpdateTheme; + + if (mAutoUpdateTheme) + mTheme.addListener(this); + else + mTheme.removeListener(this); + } + } + + public ColorDrawable getColorDrawable(int id) { + return new ColorDrawable(getResources().getColor(id)); + } +} diff --git a/mobile/android/base/widget/ThemedTextSwitcher.java.in b/mobile/android/base/widget/ThemedTextSwitcher.java.in deleted file mode 100644 index 49d2fa2eb395..000000000000 --- a/mobile/android/base/widget/ThemedTextSwitcher.java.in +++ /dev/null @@ -1,4 +0,0 @@ -//#filter substitution -//#define VIEW_NAME_SUFFIX TextSwitcher -//#define BASE_TYPE android.widget.TextSwitcher -//#include ThemedView.java.frag diff --git a/mobile/android/base/widget/ThemedTextView.java b/mobile/android/base/widget/ThemedTextView.java new file mode 100644 index 000000000000..40ddf0c1180c --- /dev/null +++ b/mobile/android/base/widget/ThemedTextView.java @@ -0,0 +1,155 @@ +// This file is generated by generate_themed_views.py; do not edit. + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.gecko.widget; + +import org.mozilla.gecko.GeckoApplication; +import org.mozilla.gecko.LightweightTheme; +import org.mozilla.gecko.R; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.drawable.ColorDrawable; +import android.util.AttributeSet; + +public class ThemedTextView extends android.widget.TextView + implements LightweightTheme.OnChangeListener { + private LightweightTheme mTheme; + + private static final int[] STATE_PRIVATE_MODE = { R.attr.state_private }; + private static final int[] STATE_LIGHT = { R.attr.state_light }; + private static final int[] STATE_DARK = { R.attr.state_dark }; + + protected static final int[] PRIVATE_PRESSED_STATE_SET = { R.attr.state_private, android.R.attr.state_pressed }; + protected static final int[] PRIVATE_FOCUSED_STATE_SET = { R.attr.state_private, android.R.attr.state_focused }; + protected static final int[] PRIVATE_STATE_SET = { R.attr.state_private }; + + private boolean mIsPrivate; + private boolean mIsLight; + private boolean mIsDark; + private boolean mAutoUpdateTheme; // always false if there's no theme. + + public ThemedTextView(Context context, AttributeSet attrs) { + super(context, attrs); + initialize(context, attrs); + } + + public ThemedTextView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + initialize(context, attrs); + } + + private void initialize(final Context context, final AttributeSet attrs) { + // The theme can be null, particularly for webapps: Bug 1089266. + mTheme = ((GeckoApplication) context.getApplicationContext()).getLightweightTheme(); + + final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.LightweightTheme); + mAutoUpdateTheme = mTheme != null && a.getBoolean(R.styleable.LightweightTheme_autoUpdateTheme, true); + a.recycle(); + } + + @Override + public void onAttachedToWindow() { + super.onAttachedToWindow(); + + if (mAutoUpdateTheme) + mTheme.addListener(this); + } + + @Override + public void onDetachedFromWindow() { + super.onDetachedFromWindow(); + + if (mAutoUpdateTheme) + mTheme.removeListener(this); + } + + @Override + public int[] onCreateDrawableState(int extraSpace) { + final int[] drawableState = super.onCreateDrawableState(extraSpace + 1); + + if (mIsPrivate) + mergeDrawableStates(drawableState, STATE_PRIVATE_MODE); + else if (mIsLight) + mergeDrawableStates(drawableState, STATE_LIGHT); + else if (mIsDark) + mergeDrawableStates(drawableState, STATE_DARK); + + return drawableState; + } + + @Override + public void onLightweightThemeChanged() { + if (mAutoUpdateTheme && mTheme.isEnabled()) + setTheme(mTheme.isLightTheme()); + } + + @Override + public void onLightweightThemeReset() { + if (mAutoUpdateTheme) + resetTheme(); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + onLightweightThemeChanged(); + } + + public boolean isPrivateMode() { + return mIsPrivate; + } + + public void setPrivateMode(boolean isPrivate) { + if (mIsPrivate != isPrivate) { + mIsPrivate = isPrivate; + refreshDrawableState(); + } + } + + public void setTheme(boolean isLight) { + // Set the theme only if it is different from existing theme. + if ((isLight && mIsLight != isLight) || + (!isLight && mIsDark == isLight)) { + if (isLight) { + mIsLight = true; + mIsDark = false; + } else { + mIsLight = false; + mIsDark = true; + } + + refreshDrawableState(); + } + } + + public void resetTheme() { + if (mIsLight || mIsDark) { + mIsLight = false; + mIsDark = false; + refreshDrawableState(); + } + } + + public void setAutoUpdateTheme(boolean autoUpdateTheme) { + if (mTheme == null) { + return; + } + + if (mAutoUpdateTheme != autoUpdateTheme) { + mAutoUpdateTheme = autoUpdateTheme; + + if (mAutoUpdateTheme) + mTheme.addListener(this); + else + mTheme.removeListener(this); + } + } + + public ColorDrawable getColorDrawable(int id) { + return new ColorDrawable(getResources().getColor(id)); + } +} diff --git a/mobile/android/base/widget/ThemedTextView.java.in b/mobile/android/base/widget/ThemedTextView.java.in deleted file mode 100644 index 4cfd7d5d9c60..000000000000 --- a/mobile/android/base/widget/ThemedTextView.java.in +++ /dev/null @@ -1,5 +0,0 @@ -//#filter substitution -//#define VIEW_NAME_SUFFIX TextView -//#define BASE_TYPE android.widget.TextView -//#define STYLE_CONSTRUCTOR 1 -//#include ThemedView.java.frag diff --git a/mobile/android/base/widget/ThemedView.java b/mobile/android/base/widget/ThemedView.java new file mode 100644 index 000000000000..d044f96127ee --- /dev/null +++ b/mobile/android/base/widget/ThemedView.java @@ -0,0 +1,155 @@ +// This file is generated by generate_themed_views.py; do not edit. + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.gecko.widget; + +import org.mozilla.gecko.GeckoApplication; +import org.mozilla.gecko.LightweightTheme; +import org.mozilla.gecko.R; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.drawable.ColorDrawable; +import android.util.AttributeSet; + +public class ThemedView extends android.view.View + implements LightweightTheme.OnChangeListener { + private LightweightTheme mTheme; + + private static final int[] STATE_PRIVATE_MODE = { R.attr.state_private }; + private static final int[] STATE_LIGHT = { R.attr.state_light }; + private static final int[] STATE_DARK = { R.attr.state_dark }; + + protected static final int[] PRIVATE_PRESSED_STATE_SET = { R.attr.state_private, android.R.attr.state_pressed }; + protected static final int[] PRIVATE_FOCUSED_STATE_SET = { R.attr.state_private, android.R.attr.state_focused }; + protected static final int[] PRIVATE_STATE_SET = { R.attr.state_private }; + + private boolean mIsPrivate; + private boolean mIsLight; + private boolean mIsDark; + private boolean mAutoUpdateTheme; // always false if there's no theme. + + public ThemedView(Context context, AttributeSet attrs) { + super(context, attrs); + initialize(context, attrs); + } + + public ThemedView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + initialize(context, attrs); + } + + private void initialize(final Context context, final AttributeSet attrs) { + // The theme can be null, particularly for webapps: Bug 1089266. + mTheme = ((GeckoApplication) context.getApplicationContext()).getLightweightTheme(); + + final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.LightweightTheme); + mAutoUpdateTheme = mTheme != null && a.getBoolean(R.styleable.LightweightTheme_autoUpdateTheme, true); + a.recycle(); + } + + @Override + public void onAttachedToWindow() { + super.onAttachedToWindow(); + + if (mAutoUpdateTheme) + mTheme.addListener(this); + } + + @Override + public void onDetachedFromWindow() { + super.onDetachedFromWindow(); + + if (mAutoUpdateTheme) + mTheme.removeListener(this); + } + + @Override + public int[] onCreateDrawableState(int extraSpace) { + final int[] drawableState = super.onCreateDrawableState(extraSpace + 1); + + if (mIsPrivate) + mergeDrawableStates(drawableState, STATE_PRIVATE_MODE); + else if (mIsLight) + mergeDrawableStates(drawableState, STATE_LIGHT); + else if (mIsDark) + mergeDrawableStates(drawableState, STATE_DARK); + + return drawableState; + } + + @Override + public void onLightweightThemeChanged() { + if (mAutoUpdateTheme && mTheme.isEnabled()) + setTheme(mTheme.isLightTheme()); + } + + @Override + public void onLightweightThemeReset() { + if (mAutoUpdateTheme) + resetTheme(); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + onLightweightThemeChanged(); + } + + public boolean isPrivateMode() { + return mIsPrivate; + } + + public void setPrivateMode(boolean isPrivate) { + if (mIsPrivate != isPrivate) { + mIsPrivate = isPrivate; + refreshDrawableState(); + } + } + + public void setTheme(boolean isLight) { + // Set the theme only if it is different from existing theme. + if ((isLight && mIsLight != isLight) || + (!isLight && mIsDark == isLight)) { + if (isLight) { + mIsLight = true; + mIsDark = false; + } else { + mIsLight = false; + mIsDark = true; + } + + refreshDrawableState(); + } + } + + public void resetTheme() { + if (mIsLight || mIsDark) { + mIsLight = false; + mIsDark = false; + refreshDrawableState(); + } + } + + public void setAutoUpdateTheme(boolean autoUpdateTheme) { + if (mTheme == null) { + return; + } + + if (mAutoUpdateTheme != autoUpdateTheme) { + mAutoUpdateTheme = autoUpdateTheme; + + if (mAutoUpdateTheme) + mTheme.addListener(this); + else + mTheme.removeListener(this); + } + } + + public ColorDrawable getColorDrawable(int id) { + return new ColorDrawable(getResources().getColor(id)); + } +} diff --git a/mobile/android/base/widget/ThemedView.java.frag b/mobile/android/base/widget/ThemedView.java.frag index d4726a3732d6..24986a941cf8 100644 --- a/mobile/android/base/widget/ThemedView.java.frag +++ b/mobile/android/base/widget/ThemedView.java.frag @@ -1,3 +1,6 @@ +//#filter substitution +// This file is generated by generate_themed_views.py; do not edit. + /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ diff --git a/mobile/android/base/widget/ThemedView.java.in b/mobile/android/base/widget/ThemedView.java.in deleted file mode 100644 index 77b8d4068383..000000000000 --- a/mobile/android/base/widget/ThemedView.java.in +++ /dev/null @@ -1,5 +0,0 @@ -//#filter substitution -//#define VIEW_NAME_SUFFIX View -//#define BASE_TYPE android.view.View -//#define STYLE_CONSTRUCTOR 1 -//#include ThemedView.java.frag diff --git a/mobile/android/base/widget/generate_themed_views.py b/mobile/android/base/widget/generate_themed_views.py new file mode 100644 index 000000000000..d36efa944f57 --- /dev/null +++ b/mobile/android/base/widget/generate_themed_views.py @@ -0,0 +1,66 @@ +#!/bin/python + +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +''' +Script to generate Themed*.java source files for Fennec. + +This script runs the preprocessor on a input template and writes +updated files into the source directory. + +To update the themed views, update the input template +(ThemedView.java.frag) and run the script. Use version control to +examine the differences, and don't forget to commit the changes to the +template and the outputs. +''' + +from __future__ import ( + print_function, + unicode_literals, +) + +import os + +from mozbuild.preprocessor import Preprocessor + +__DIR__ = os.path.dirname(os.path.abspath(__file__)) + +template = os.path.join(__DIR__, 'ThemedView.java.frag') +dest_format_string = 'Themed%(VIEW_NAME_SUFFIX)s.java' + +views = [ + dict(VIEW_NAME_SUFFIX='EditText', + BASE_TYPE='android.widget.EditText', + STYLE_CONSTRUCTOR=1), + dict(VIEW_NAME_SUFFIX='ImageButton', + BASE_TYPE='android.widget.ImageButton', + STYLE_CONSTRUCTOR=1), + dict(VIEW_NAME_SUFFIX='ImageView', + BASE_TYPE='android.widget.ImageView', + STYLE_CONSTRUCTOR=1), + dict(VIEW_NAME_SUFFIX='LinearLayout', + BASE_TYPE='android.widget.LinearLayout'), + dict(VIEW_NAME_SUFFIX='RelativeLayout', + BASE_TYPE='android.widget.RelativeLayout', + STYLE_CONSTRUCTOR=1), + dict(VIEW_NAME_SUFFIX='TextSwitcher', + BASE_TYPE='android.widget.TextSwitcher'), + dict(VIEW_NAME_SUFFIX='TextView', + BASE_TYPE='android.widget.TextView', + STYLE_CONSTRUCTOR=1), + dict(VIEW_NAME_SUFFIX='View', + BASE_TYPE='android.view.View', + STYLE_CONSTRUCTOR=1), +] + +for view in views: + pp = Preprocessor(defines=view, marker='//#') + + dest = os.path.join(__DIR__, dest_format_string % view) + with open(template, 'rU') as input: + with open(dest, 'wt') as output: + pp.processFile(input=input, output=output) + print('%s' % dest)