developing chameleon theme library - this library will be released in Apache license later

This commit is contained in:
Mariotaku Lee 2016-12-18 23:16:03 +08:00
parent ebc79bf6a2
commit 983bd4cb70
47 changed files with 1626 additions and 428 deletions

1
chameleon/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

34
chameleon/build.gradle Normal file
View File

@ -0,0 +1,34 @@
apply plugin: 'com.android.library'
android {
compileSdkVersion 25
buildToolsVersion "25.0.2"
defaultConfig {
minSdkVersion 14
targetSdkVersion 25
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:25.1.0'
compile 'com.android.support:design:25.1.0'
testCompile 'junit:junit:4.12'
androidTestCompile "com.android.support:support-annotations:$android_support_lib_version"
androidTestCompile 'com.android.support.test:runner:0.5'
androidTestCompile 'com.android.support.test:rules:0.5'
}

17
chameleon/proguard-rules.pro vendored Normal file
View File

@ -0,0 +1,17 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/mariotaku/Library/Android/sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

View File

@ -0,0 +1,26 @@
package org.mariotaku.chameleon;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumentation test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() throws Exception {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getTargetContext();
assertEquals("org.mariotaku.chameleon.test", appContext.getPackageName());
}
}

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="org.mariotaku.chameleon"/>

View File

@ -0,0 +1,205 @@
package org.mariotaku.chameleon;
import android.app.Activity;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.util.ArrayMap;
import android.support.v4.view.LayoutInflaterCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.app.AppCompatDelegate;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import org.mariotaku.chameleon.internal.ChameleonInflationFactory;
/**
* Created by mariotaku on 2016/12/18.
*/
public class Chameleon {
private final Activity activity;
private final Theme theme;
private final ArrayMap<ChameleonView, ChameleonView.Appearance> postApplyViews;
private Chameleon(Activity activity) {
this.activity = activity;
this.theme = getOverrideTheme(activity, activity);
this.postApplyViews = new ArrayMap<>();
}
public static Chameleon getInstance(Activity activity) {
return new Chameleon(activity);
}
public void preApply() {
final LayoutInflater inflater = activity.getLayoutInflater();
AppCompatDelegate delegate = null;
if (activity instanceof AppCompatActivity) {
delegate = ((AppCompatActivity) activity).getDelegate();
}
final ChameleonInflationFactory factory = new ChameleonInflationFactory(inflater, activity,
delegate, theme, postApplyViews);
LayoutInflaterCompat.setFactory(inflater, factory);
}
public void postApply() {
for (int i = 0, j = postApplyViews.size(); i < j; i++) {
postApplyViews.keyAt(i).applyAppearance(postApplyViews.valueAt(i));
}
postApplyViews.clear();
boolean statusBarColorHandled = false;
final View rootView = getRootView();
if (rootView instanceof ChameleonView.StatusBarThemeable) {
if (((ChameleonView.StatusBarThemeable) rootView).isStatusBarColorHandled()) {
statusBarColorHandled = true;
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && !statusBarColorHandled) {
activity.getWindow().setStatusBarColor(theme.getStatusBarColor());
}
}
private View getRootView() {
return ((ViewGroup) activity.findViewById(android.R.id.content)).getChildAt(0);
}
public void invalidateActivity() {
}
public void cleanUp() {
postApplyViews.clear();
}
public void themeOverflow() {
}
@NonNull
public static Theme getOverrideTheme(Context context, Object obj) {
if (obj instanceof Themeable) {
final Theme theme = ((Themeable) obj).getOverrideTheme();
if (theme != null) {
return theme;
}
}
return Theme.from(context);
}
/**
* Created by mariotaku on 2016/12/18.
*/
public static class Theme {
private int colorPrimary;
private int colorAccent;
private int colorToolbar;
private int colorBackground;
private int colorForeground;
private boolean toolbarColored;
private int textColorPrimary;
private int statusBarColor;
Theme() {
}
public int getColorAccent() {
return colorAccent;
}
public void setColorAccent(int colorAccent) {
this.colorAccent = colorAccent;
}
public int getColorPrimary() {
return colorPrimary;
}
public void setColorPrimary(int colorPrimary) {
this.colorPrimary = colorPrimary;
}
public int getColorToolbar() {
if (colorToolbar == 0) return colorPrimary;
return colorToolbar;
}
public void setColorToolbar(int colorToolbar) {
this.colorToolbar = colorToolbar;
}
public boolean isToolbarColored() {
return toolbarColored;
}
public void setToolbarColored(boolean toolbarColored) {
this.toolbarColored = toolbarColored;
}
public int getTextColorPrimary() {
return textColorPrimary;
}
public void setTextColorPrimary(int textColorPrimary) {
this.textColorPrimary = textColorPrimary;
}
public int getColorBackground() {
return colorBackground;
}
public void setColorBackground(int colorBackground) {
this.colorBackground = colorBackground;
}
public int getColorForeground() {
return colorForeground;
}
public void setColorForeground(int colorForeground) {
this.colorForeground = colorForeground;
}
@NonNull
public static Theme from(Context context) {
Theme theme = new Theme();
TypedArray a = context.obtainStyledAttributes(R.styleable.ChameleonTheme);
theme.setColorPrimary(a.getColor(R.styleable.ChameleonTheme_colorPrimary, 0));
theme.setColorAccent(a.getColor(R.styleable.ChameleonTheme_colorAccent, 0));
theme.setColorToolbar(a.getColor(R.styleable.ChameleonTheme_colorToolbar, theme.getColorPrimary()));
theme.setColorBackground(a.getColor(R.styleable.ChameleonTheme_android_colorBackground, 0));
theme.setColorForeground(a.getColor(R.styleable.ChameleonTheme_android_colorForeground, 0));
theme.setToolbarColored(a.getBoolean(R.styleable.ChameleonTheme_isToolbarColored, true));
a.recycle();
return theme;
}
public int getStatusBarColor() {
if (statusBarColor == 0) {
return ChameleonUtils.darkenColor(getColorToolbar());
}
return statusBarColor;
}
public void setStatusBarColor(int statusBarColor) {
this.statusBarColor = statusBarColor;
}
}
/**
* Created by mariotaku on 2016/12/18.
*/
public interface Themeable {
@Nullable
Theme getOverrideTheme();
}
}

View File

@ -0,0 +1,77 @@
package org.mariotaku.chameleon;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
/**
* Created by mariotaku on 2016/12/18.
*/
public class ChameleonTypedArray {
private final TypedArray wrapped;
private final boolean[] hasAttributeStates;
private final int[] attributeReferences;
private final Chameleon.Theme theme;
private ChameleonTypedArray(TypedArray wrapped, boolean[] hasAttributeStates, int[] attributeReferences, Chameleon.Theme theme) {
this.wrapped = wrapped;
this.hasAttributeStates = hasAttributeStates;
this.attributeReferences = attributeReferences;
this.theme = theme;
}
public void recycle() {
wrapped.recycle();
}
public static ChameleonTypedArray obtain(Context context, AttributeSet set, int[] attrs, Chameleon.Theme theme) {
@SuppressLint("Recycle") TypedArray array = context.obtainStyledAttributes(set, attrs);
boolean[] hasAttribute = new boolean[attrs.length];
int[] attributeReferences = new int[attrs.length];
for (int i = 0; i < attrs.length; i++) {
final int index = ChameleonUtils.findAttributeIndex(set, attrs[i]);
if (index != -1) {
hasAttribute[i] = true;
String value = set.getAttributeValue(index);
if (value != null && value.startsWith("?")) {
attributeReferences[i] = Integer.parseInt(value.substring(1));
}
}
}
return new ChameleonTypedArray(array, hasAttribute, attributeReferences, theme);
}
public int getColor(int index) {
return wrapped.getColor(index, 0);
}
public int getColor(int index, int defValue) {
final int ref = attributeReferences[index];
if (ref == android.support.design.R.attr.colorPrimary) {
return theme.getColorPrimary();
} else if (ref == android.support.design.R.attr.colorAccent) {
return theme.getColorAccent();
} else if (ref == R.attr.colorToolbar) {
return theme.getColorToolbar();
}
if (!hasAttributeStates[index]) return defValue;
return wrapped.getColor(index, defValue);
}
public Drawable getDrawable(int index) {
final int ref = attributeReferences[index];
if (ref == android.support.design.R.attr.colorPrimary) {
return new ColorDrawable(theme.getColorPrimary());
} else if (ref == android.support.design.R.attr.colorAccent) {
return new ColorDrawable(theme.getColorAccent());
} else if (ref == R.attr.colorToolbar) {
return new ColorDrawable(theme.getColorToolbar());
}
if (!hasAttributeStates[index]) return null;
return wrapped.getDrawable(index);
}
}

View File

@ -0,0 +1,51 @@
package org.mariotaku.chameleon;
import android.app.Activity;
import android.content.Context;
import android.content.ContextWrapper;
import android.graphics.Color;
import android.support.annotation.ColorInt;
import android.support.annotation.FloatRange;
import android.util.AttributeSet;
/**
* Created by mariotaku on 2016/12/18.
*/
public class ChameleonUtils {
public static int findAttributeIndex(AttributeSet attributeSet, int attributeNameResource) {
for (int i = 0, j = attributeSet.getAttributeCount(); i < j; i++) {
if (attributeSet.getAttributeNameResource(i) == attributeNameResource) return i;
}
return -1;
}
public static boolean isColorLight(@ColorInt int color) {
if (color == Color.BLACK) return false;
else if (color == Color.WHITE || color == Color.TRANSPARENT) return true;
final double darkness = 1 - (0.299 * Color.red(color) + 0.587 * Color.green(color) + 0.114 * Color.blue(color)) / 255;
return darkness < 0.4;
}
@ColorInt
public static int shiftColor(@ColorInt int color, @FloatRange(from = 0.0f, to = 2.0f) float by) {
if (by == 1f) return color;
float[] hsv = new float[3];
Color.colorToHSV(color, hsv);
hsv[2] *= by; // value component
return Color.HSVToColor(hsv);
}
@ColorInt
public static int darkenColor(@ColorInt int color) {
return shiftColor(color, 0.9f);
}
public static Activity getActivity(Context context) {
if (context instanceof Activity) return (Activity) context;
if (context instanceof ContextWrapper) {
return getActivity(((ContextWrapper) context).getBaseContext());
}
return null;
}
}

View File

@ -0,0 +1,28 @@
package org.mariotaku.chameleon;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
/**
* Created by mariotaku on 2016/12/18.
*/
public interface ChameleonView {
boolean isPostApplyTheme();
@Nullable
Appearance createAppearance(Context context, AttributeSet attributeSet, Chameleon.Theme theme);
void applyAppearance(@NonNull Appearance appearance);
interface Appearance {
}
interface StatusBarThemeable {
boolean isStatusBarColorHandled();
}
}

View File

@ -0,0 +1,176 @@
package org.mariotaku.chameleon.internal;
import android.app.Activity;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.util.ArrayMap;
import android.support.v4.view.LayoutInflaterFactory;
import android.support.v7.app.AppCompatDelegate;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import org.mariotaku.chameleon.Chameleon;
import org.mariotaku.chameleon.ChameleonView;
import org.mariotaku.chameleon.view.ChameleonAutoCompleteTextView;
import org.mariotaku.chameleon.view.ChameleonEditText;
import org.mariotaku.chameleon.view.ChameleonFloatingActionButton;
import org.mariotaku.chameleon.view.ChameleonMultiAutoCompleteTextView;
import org.mariotaku.chameleon.view.ChameleonSwipeRefreshLayout;
import org.mariotaku.chameleon.view.ChameleonTextView;
import org.mariotaku.chameleon.view.ChameleonToolbar;
/**
* Created by mariotaku on 2016/12/18.
*/
public class ChameleonInflationFactory implements LayoutInflaterFactory {
@NonNull
private final LayoutInflater mInflater;
@Nullable
private final Activity mActivity;
@Nullable
private final AppCompatDelegate mDelegate;
@Nullable
private final Chameleon.Theme mTheme;
private final ArrayMap<ChameleonView, ChameleonView.Appearance> mPostApplyViews;
public ChameleonInflationFactory(@NonNull LayoutInflater inflater,
@Nullable Activity activity,
@Nullable AppCompatDelegate delegate,
@Nullable Chameleon.Theme theme,
@NonNull ArrayMap<ChameleonView, ChameleonView.Appearance> postApplyViews) {
this.mInflater = inflater;
this.mActivity = activity;
this.mDelegate = delegate;
this.mTheme = theme;
this.mPostApplyViews = postApplyViews;
}
@Override
public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
View view = null;
if (shouldSkipTheming(parent)) {
} else switch (name) {
case "TextView":
case "android.support.v7.widget.AppCompatTextView": {
view = new ChameleonTextView(context, attrs);
break;
}
case "EditText":
case "android.support.v7.widget.AppCompatEditText": {
view = new ChameleonEditText(context, attrs);
break;
}
case "AutoCompleteTextView":
case "android.support.v7.widget.AppCompatAutoCompleteTextView": {
view = new ChameleonAutoCompleteTextView(context, attrs);
break;
}
case "MultiAutoCompleteTextView":
case "android.support.v7.widget.AppCompatMultiAutoCompleteTextView": {
view = new ChameleonMultiAutoCompleteTextView(context, attrs);
break;
}
case "android.support.design.widget.FloatingActionButton": {
view = new ChameleonFloatingActionButton(context, attrs);
break;
}
case "android.support.v7.widget.Toolbar": {
view = new ChameleonToolbar(context, attrs);
break;
}
case "android.support.v4.widget.SwipeRefreshLayout": {
view = new ChameleonSwipeRefreshLayout(context, attrs);
break;
}
}
if (view == null) {
// First, check if the AppCompatDelegate will give us a view, usually (maybe always) null.
if (mDelegate != null) {
view = mDelegate.createView(parent, name, context, attrs);
if (view == null && mActivity != null)
view = mActivity.onCreateView(parent, name, context, attrs);
else view = null;
} else {
view = null;
}
if (isExcluded(name))
return view;
// Mimic code of LayoutInflater using reflection tricks (this would normally be run when this factory returns null).
// We need to intercept the default behavior rather than allowing the LayoutInflater to handle it after this method returns.
if (view == null) {
try {
Context viewContext;
final boolean inheritContext = false; // TODO will this ever need to be true?
//noinspection PointlessBooleanExpression,ConstantConditions
if (parent != null && inheritContext) {
viewContext = parent.getContext();
} else {
viewContext = mInflater.getContext();
}
Context wrappedContext = LayoutInflaterInternal.getThemeWrapper(viewContext, attrs);
if (wrappedContext != null) {
viewContext = wrappedContext;
}
Object[] mConstructorArgs = LayoutInflaterInternal.getConstructorArgs(mInflater);
final Object lastContext = mConstructorArgs[0];
mConstructorArgs[0] = viewContext;
try {
if (-1 == name.indexOf('.')) {
view = LayoutInflaterInternal.onCreateView(mInflater, parent, name, attrs);
} else {
view = LayoutInflaterInternal.createView(mInflater, name, null, attrs);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
mConstructorArgs[0] = lastContext;
}
} catch (Throwable t) {
throw new RuntimeException(String.format("An error occurred while inflating View %s: %s", name, t.getMessage()), t);
}
}
}
if (view instanceof ChameleonView) {
final ChameleonView cv = (ChameleonView) view;
ChameleonView.Appearance appearance = cv.createAppearance(view.getContext(), attrs, mTheme);
if (appearance != null) {
if (cv.isPostApplyTheme()) {
mPostApplyViews.put(cv, appearance);
} else {
cv.applyAppearance(appearance);
}
}
}
return view;
}
private boolean isExcluded(@NonNull String name) {
switch (name) {
case "android.support.design.internal.NavigationMenuItemView":
case "ViewStub":
case "fragment":
case "include":
return true;
default:
return false;
}
}
private boolean shouldSkipTheming(View parent) {
if (parent == null) return false;
return "ignore".equals(parent.getTag());
}
}

View File

@ -0,0 +1,106 @@
package org.mariotaku.chameleon.internal;
import android.content.Context;
import android.content.res.TypedArray;
import android.support.v7.view.ContextThemeWrapper;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* Created by mariotaku on 2016/12/18.
*/
public class LayoutInflaterInternal {
private static Method mOnCreateViewMethod;
private static Method mCreateViewMethod;
private static Field mConstructorArgsField;
private static int[] ATTRS_THEME;
public static View onCreateView(LayoutInflater inflater, View view, String name, AttributeSet attrs) {
ensureAvailable();
try {
return (View) mOnCreateViewMethod.invoke(inflater, view, name, attrs);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
}
public static View createView(LayoutInflater inflater, String name, String prefix, AttributeSet attrs) {
ensureAvailable();
try {
return (View) mCreateViewMethod.invoke(inflater, name, null, attrs);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
}
public static Context getThemeWrapper(Context viewContext, AttributeSet attrs) {
ensureAvailable();
// Apply a theme wrapper, if requested.
if (ATTRS_THEME != null) {
final TypedArray ta = viewContext.obtainStyledAttributes(attrs, ATTRS_THEME);
try {
final int themeResId = ta.getResourceId(0, 0);
if (themeResId != 0) return new ContextThemeWrapper(viewContext, themeResId);
} finally {
ta.recycle();
}
}
return null;
}
public static Object[] getConstructorArgs(LayoutInflater inflater) {
try {
return (Object[]) mConstructorArgsField.get(inflater);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
private static void ensureAvailable() {
if (mOnCreateViewMethod == null) {
try {
mOnCreateViewMethod = LayoutInflater.class.getDeclaredMethod("onCreateView",
View.class, String.class, AttributeSet.class);
mOnCreateViewMethod.setAccessible(true);
} catch (NoSuchMethodException e) {
throw new RuntimeException("Failed to retrieve the onCreateView method.", e);
}
}
if (mCreateViewMethod == null) {
try {
mCreateViewMethod = LayoutInflater.class.getDeclaredMethod("createView",
String.class, String.class, AttributeSet.class);
mCreateViewMethod.setAccessible(true);
} catch (NoSuchMethodException e) {
throw new RuntimeException("Failed to retrieve the createView method.", e);
}
}
if (mConstructorArgsField == null) {
try {
mConstructorArgsField = LayoutInflater.class.getDeclaredField("mConstructorArgs");
mConstructorArgsField.setAccessible(true);
} catch (NoSuchFieldException e) {
throw new RuntimeException("Failed to retrieve the mConstructorArgs field.", e);
}
}
if (ATTRS_THEME == null) {
try {
final Field attrsThemeField = LayoutInflater.class.getDeclaredField("ATTRS_THEME");
attrsThemeField.setAccessible(true);
ATTRS_THEME = (int[]) attrsThemeField.get(null);
} catch (Throwable t) {
// Ignore
}
}
}
}

View File

@ -0,0 +1,47 @@
package org.mariotaku.chameleon.view;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.widget.AppCompatAutoCompleteTextView;
import android.util.AttributeSet;
import org.mariotaku.chameleon.Chameleon;
import org.mariotaku.chameleon.ChameleonView;
/**
* Created by mariotaku on 2016/12/18.
*/
public class ChameleonAutoCompleteTextView extends AppCompatAutoCompleteTextView implements ChameleonView {
public ChameleonAutoCompleteTextView(Context context) {
super(context);
}
public ChameleonAutoCompleteTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ChameleonAutoCompleteTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean isPostApplyTheme() {
return false;
}
@Nullable
@Override
public ChameleonEditText.Appearance createAppearance(Context context, AttributeSet attributeSet, Chameleon.Theme theme) {
return ChameleonEditText.Appearance.create(context, attributeSet, theme);
}
@Override
public void applyAppearance(@NonNull Appearance appearance) {
final ChameleonEditText.Appearance a = (ChameleonEditText.Appearance) appearance;
ChameleonEditText.Appearance.apply(this, a);
}
}

View File

@ -0,0 +1,78 @@
package org.mariotaku.chameleon.view;
import android.content.Context;
import android.content.res.ColorStateList;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.view.ViewCompat;
import android.support.v7.widget.AppCompatEditText;
import android.util.AttributeSet;
import android.widget.TextView;
import org.mariotaku.chameleon.Chameleon;
import org.mariotaku.chameleon.ChameleonTypedArray;
import org.mariotaku.chameleon.ChameleonView;
import org.mariotaku.chameleon.R;
/**
* Created by mariotaku on 2016/12/18.
*/
public class ChameleonEditText extends AppCompatEditText implements ChameleonView {
public ChameleonEditText(Context context) {
super(context);
}
public ChameleonEditText(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ChameleonEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean isPostApplyTheme() {
return false;
}
@Nullable
@Override
public Appearance createAppearance(Context context, AttributeSet attributeSet, Chameleon.Theme theme) {
return Appearance.create(context, attributeSet, theme);
}
@Override
public void applyAppearance(@NonNull ChameleonView.Appearance appearance) {
final Appearance a = (Appearance) appearance;
Appearance.apply(this, a);
}
public static class Appearance extends ChameleonTextView.Appearance {
private int backgroundColor;
public int getBackgroundColor() {
return backgroundColor;
}
public void setBackgroundColor(int backgroundColor) {
this.backgroundColor = backgroundColor;
}
public static void apply(TextView view, Appearance appearance) {
view.setLinkTextColor(appearance.getLinkTextColor());
ViewCompat.setBackgroundTintList(view, ColorStateList.valueOf(appearance.getBackgroundColor()));
}
public static Appearance create(Context context, AttributeSet attributeSet, Chameleon.Theme theme) {
Appearance appearance = new Appearance();
ChameleonTypedArray a = ChameleonTypedArray.obtain(context, attributeSet,
R.styleable.ChameleonEditText, theme);
appearance.setLinkTextColor(a.getColor(R.styleable.ChameleonEditText_android_textColorLink, theme.getColorAccent()));
appearance.setBackgroundColor(a.getColor(R.styleable.ChameleonEditText_backgroundTint, theme.getColorAccent()));
a.recycle();
return appearance;
}
}
}

View File

@ -0,0 +1,58 @@
package org.mariotaku.chameleon.view;
import android.content.Context;
import android.content.res.ColorStateList;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.widget.FloatingActionButton;
import android.util.AttributeSet;
import org.mariotaku.chameleon.Chameleon;
import org.mariotaku.chameleon.ChameleonTypedArray;
import org.mariotaku.chameleon.ChameleonView;
import org.mariotaku.chameleon.R;
/**
* Created by mariotaku on 2016/12/18.
*/
public class ChameleonFloatingActionButton extends FloatingActionButton implements ChameleonView {
public ChameleonFloatingActionButton(Context context) {
super(context);
}
public ChameleonFloatingActionButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ChameleonFloatingActionButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean isPostApplyTheme() {
return false;
}
@Nullable
@Override
public Appearance createAppearance(Context context, AttributeSet attributeSet, Chameleon.Theme theme) {
Appearance appearance = new Appearance();
ChameleonTypedArray a = ChameleonTypedArray.obtain(context, attributeSet,
R.styleable.ChameleonFloatingActionButton, theme);
appearance.backgroundTint = a.getColor(R.styleable.ChameleonFloatingActionButton_backgroundTint, theme.getColorAccent());
a.recycle();
return appearance;
}
@Override
public void applyAppearance(@NonNull ChameleonView.Appearance appearance) {
Appearance a = (Appearance) appearance;
setBackgroundTintList(ColorStateList.valueOf(a.backgroundTint));
}
public static class Appearance implements ChameleonView.Appearance {
int backgroundTint;
}
}

View File

@ -0,0 +1,47 @@
package org.mariotaku.chameleon.view;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.widget.AppCompatMultiAutoCompleteTextView;
import android.util.AttributeSet;
import org.mariotaku.chameleon.Chameleon;
import org.mariotaku.chameleon.ChameleonView;
/**
* Created by mariotaku on 2016/12/18.
*/
public class ChameleonMultiAutoCompleteTextView extends AppCompatMultiAutoCompleteTextView implements ChameleonView {
public ChameleonMultiAutoCompleteTextView(Context context) {
super(context);
}
public ChameleonMultiAutoCompleteTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ChameleonMultiAutoCompleteTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean isPostApplyTheme() {
return false;
}
@Nullable
@Override
public ChameleonEditText.Appearance createAppearance(Context context, AttributeSet attributeSet, Chameleon.Theme theme) {
return ChameleonEditText.Appearance.create(context, attributeSet, theme);
}
@Override
public void applyAppearance(@NonNull ChameleonView.Appearance appearance) {
final ChameleonEditText.Appearance a = (ChameleonEditText.Appearance) appearance;
ChameleonEditText.Appearance.apply(this, a);
}
}

View File

@ -0,0 +1,52 @@
package org.mariotaku.chameleon.view;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.widget.SwipeRefreshLayout;
import android.util.AttributeSet;
import org.mariotaku.chameleon.Chameleon;
import org.mariotaku.chameleon.ChameleonView;
/**
* Created by mariotaku on 2016/12/18.
*/
public class ChameleonSwipeRefreshLayout extends SwipeRefreshLayout implements ChameleonView {
public ChameleonSwipeRefreshLayout(Context context) {
super(context);
}
public ChameleonSwipeRefreshLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean isPostApplyTheme() {
return false;
}
@Nullable
@Override
public Appearance createAppearance(Context context, AttributeSet attributeSet, Chameleon.Theme theme) {
Appearance appearance = new Appearance();
appearance.indicatorColor = theme.getColorAccent();
appearance.progressBackgroundColor = theme.getColorBackground();
return appearance;
}
@Override
public void applyAppearance(@NonNull ChameleonView.Appearance appearance) {
final Appearance a = (Appearance) appearance;
setColorSchemeColors(a.indicatorColor);
setProgressBackgroundColorSchemeColor(a.progressBackgroundColor);
}
public static class Appearance implements ChameleonView.Appearance {
int indicatorColor;
int progressBackgroundColor;
}
}

View File

@ -0,0 +1,65 @@
package org.mariotaku.chameleon.view;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.widget.AppCompatTextView;
import android.util.AttributeSet;
import org.mariotaku.chameleon.Chameleon;
import org.mariotaku.chameleon.ChameleonTypedArray;
import org.mariotaku.chameleon.ChameleonView;
import org.mariotaku.chameleon.R;
/**
* Created by mariotaku on 2016/12/18.
*/
public class ChameleonTextView extends AppCompatTextView implements ChameleonView {
public ChameleonTextView(Context context) {
super(context);
}
public ChameleonTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ChameleonTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean isPostApplyTheme() {
return false;
}
@Nullable
@Override
public Appearance createAppearance(Context context, AttributeSet attributeSet, Chameleon.Theme theme) {
Appearance appearance = new Appearance();
ChameleonTypedArray a = ChameleonTypedArray.obtain(context, attributeSet,
R.styleable.ChameleonTextView, theme);
appearance.setLinkTextColor(a.getColor(R.styleable.ChameleonTextView_android_textColorLink, theme.getColorAccent()));
a.recycle();
return appearance;
}
@Override
public void applyAppearance(@NonNull ChameleonView.Appearance appearance) {
final Appearance a = (Appearance) appearance;
setLinkTextColor(a.getLinkTextColor());
}
public static class Appearance implements ChameleonView.Appearance {
private int linkTextColor;
public int getLinkTextColor() {
return linkTextColor;
}
public void setLinkTextColor(int linkTextColor) {
this.linkTextColor = linkTextColor;
}
}
}

View File

@ -0,0 +1,94 @@
package org.mariotaku.chameleon.view;
import android.content.Context;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.widget.Toolbar;
import android.util.AttributeSet;
import org.mariotaku.chameleon.Chameleon;
import org.mariotaku.chameleon.ChameleonTypedArray;
import org.mariotaku.chameleon.ChameleonView;
import org.mariotaku.chameleon.R;
/**
* Created by mariotaku on 2016/12/18.
*/
public class ChameleonToolbar extends Toolbar implements ChameleonView {
public ChameleonToolbar(Context context) {
super(context);
}
public ChameleonToolbar(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public ChameleonToolbar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean isPostApplyTheme() {
return false;
}
@Nullable
@Override
public Appearance createAppearance(Context context, AttributeSet attributeSet, Chameleon.Theme theme) {
Appearance appearance = new Appearance();
ChameleonTypedArray a = ChameleonTypedArray.obtain(context, attributeSet,
R.styleable.ChameleonToolbar, theme);
final Drawable background = a.getDrawable(R.styleable.ChameleonToolbar_android_background);
if (background != null) {
appearance.setBackground(background);
} else {
appearance.setBackground(new ColorDrawable(theme.getColorToolbar()));
}
appearance.setTitleTextColor(a.getColor(R.styleable.ChameleonToolbar_titleTextColor));
appearance.setSubTitleTextColor(a.getColor(R.styleable.ChameleonToolbar_subtitleTextColor));
a.recycle();
return appearance;
}
@Override
public void applyAppearance(@NonNull ChameleonView.Appearance appearance) {
Appearance a = (Appearance) appearance;
setBackgroundDrawable(a.getDrawable());
setTitleTextColor(a.getTitleTextColor());
setSubtitleTextColor(a.getSubTitleTextColor());
}
public static class Appearance implements ChameleonView.Appearance {
private int titleTextColor;
private int subTitleTextColor;
private Drawable background;
public void setTitleTextColor(int titleTextColor) {
this.titleTextColor = titleTextColor;
}
public int getTitleTextColor() {
return titleTextColor;
}
public void setSubTitleTextColor(int subTitleTextColor) {
this.subTitleTextColor = subTitleTextColor;
}
public int getSubTitleTextColor() {
return subTitleTextColor;
}
public void setBackground(Drawable background) {
this.background = background;
}
public Drawable getDrawable() {
return background;
}
}
}

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ChameleonTheme">
<attr name="colorPrimary"/>
<attr name="colorAccent"/>
<attr name="colorToolbar" format="color"/>
<attr name="android:colorForeground"/>
<attr name="android:colorBackground"/>
<attr name="isToolbarColored" format="boolean"/>
</declare-styleable>
<declare-styleable name="ChameleonTextView">
<attr name="android:textColorLink"/>
</declare-styleable>
<declare-styleable name="ChameleonEditText">
<attr name="android:textColorLink"/>
<attr name="backgroundTint"/>
</declare-styleable>
<declare-styleable name="ChameleonFloatingActionButton">
<attr name="backgroundTint"/>
</declare-styleable>
<declare-styleable name="ChameleonToolbar">
<attr name="android:background"/>
<attr name="titleTextColor"/>
<attr name="subtitleTextColor"/>
</declare-styleable>
</resources>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="color_reference_primary_color">#00100001</color>
<color name="color_reference_accent_color">#00100002</color>
<color name="color_reference_toolbar_color">#00100003</color>
</resources>

View File

@ -0,0 +1,17 @@
package org.mariotaku.chameleon;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
}

View File

@ -1,4 +1,4 @@
include ':twidere'
include ':twidere', ':chameleon'
include ':twidere.component.common'
include ':twidere.library.extension'
include ':twidere.wear'

View File

@ -73,10 +73,6 @@ public interface SharedPreferenceConstants {
String VALUE_TAB_DISPLAY_OPTION_ICON = "icon";
String VALUE_TAB_DISPLAY_OPTION_LABEL = "label";
String VALUE_TAB_DISPLAY_OPTION_BOTH = "both";
int VALUE_TAB_DISPLAY_OPTION_CODE_LABEL = 0x1;
int VALUE_TAB_DISPLAY_OPTION_CODE_ICON = 0x2;
int VALUE_TAB_DISPLAY_OPTION_CODE_BOTH = VALUE_TAB_DISPLAY_OPTION_CODE_ICON
| VALUE_TAB_DISPLAY_OPTION_CODE_LABEL;
String VALUE_THEME_BACKGROUND_DEFAULT = "default";
String VALUE_THEME_BACKGROUND_SOLID = "solid";

View File

@ -95,6 +95,7 @@ dependencies {
compile project(':twidere.component.common')
compile project(':twidere.component.nyan')
compile project(':chameleon')
// START Non-FOSS component
googleCompile "com.google.android.gms:play-services-maps:$play_services_version"

View File

@ -1,151 +0,0 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.activity;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.util.KeyboardShortcutsHandler;
import org.mariotaku.twidere.util.KeyboardShortcutsHandler.KeyboardShortcutSpec;
/**
* Created by mariotaku on 15/4/20.
*/
public class KeyboardShortcutPreferenceCompatActivity extends BaseActivity implements
OnClickListener {
public static final String EXTRA_CONTEXT_TAG = "context_tag";
public static final String EXTRA_KEY_ACTION = "key_action";
private TextView mKeysLabel, mConflictLabel;
private KeyboardShortcutSpec mKeySpec;
private Button mButtonPositive, mButtonNegative, mButtonNeutral;
private int mMetaState;
@NonNull
@Override
public String getThemeBackgroundOption() {
return VALUE_THEME_BACKGROUND_DEFAULT;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_keyboard_shortcut_input);
setTitle(KeyboardShortcutsHandler.getActionLabel(this, getKeyAction()));
mButtonPositive.setOnClickListener(this);
mButtonNegative.setOnClickListener(this);
mButtonNeutral.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.button_positive: {
if (mKeySpec == null) return;
keyboardShortcutsHandler.register(mKeySpec, getKeyAction());
finish();
break;
}
case R.id.button_neutral: {
keyboardShortcutsHandler.unregister(getKeyAction());
finish();
break;
}
case R.id.button_negative: {
finish();
break;
}
}
}
@Override
public boolean onKeyDown(int keyCode, @NonNull KeyEvent event) {
if (KeyEvent.isModifierKey(keyCode)) {
mMetaState |= KeyboardShortcutsHandler.getMetaStateForKeyCode(keyCode);
}
return super.onKeyDown(keyCode, event);
}
@Override
public boolean onKeyUp(int keyCode, @NonNull KeyEvent event) {
if (KeyEvent.isModifierKey(keyCode)) {
mMetaState &= ~KeyboardShortcutsHandler.getMetaStateForKeyCode(keyCode);
}
final String keyAction = getKeyAction();
if (keyAction == null) return false;
final KeyboardShortcutSpec spec = KeyboardShortcutsHandler.getKeyboardShortcutSpec(getContextTag(),
keyCode, event, KeyEvent.normalizeMetaState(mMetaState | event.getMetaState()));
if (spec == null || !spec.isValid()) {
return super.onKeyUp(keyCode, event);
}
mKeySpec = spec;
mKeysLabel.setText(spec.toKeyString());
final String oldAction = keyboardShortcutsHandler.findAction(spec);
final KeyboardShortcutSpec copyOfSpec = spec.copy();
copyOfSpec.setContextTag(null);
final String oldGeneralAction = keyboardShortcutsHandler.findAction(copyOfSpec);
if (!TextUtils.isEmpty(oldAction) && !keyAction.equals(oldAction)) {
// Conflicts with keys in same context tag
mConflictLabel.setVisibility(View.VISIBLE);
final String label = KeyboardShortcutsHandler.getActionLabel(this, oldAction);
mConflictLabel.setText(getString(R.string.conflicts_with_name, label));
//noinspection UnnecessaryParentheses
mButtonPositive.setText((R.string.overwrite));
} else if (!TextUtils.isEmpty(oldGeneralAction) && !keyAction.equals(oldGeneralAction)) {
// Conflicts with keys in root context
mConflictLabel.setVisibility(View.VISIBLE);
final String label = KeyboardShortcutsHandler.getActionLabel(this, oldGeneralAction);
mConflictLabel.setText(getString(R.string.conflicts_with_name, label));
mButtonPositive.setText((R.string.overwrite));
} else {
mConflictLabel.setVisibility(View.GONE);
mButtonPositive.setText(android.R.string.ok);
}
return true;
}
@Override
public void onContentChanged() {
super.onContentChanged();
mKeysLabel = (TextView) findViewById(R.id.keys_label);
mConflictLabel = (TextView) findViewById(R.id.conflict_label);
mButtonPositive = (Button) findViewById(R.id.button_positive);
mButtonNegative = (Button) findViewById(R.id.button_negative);
mButtonNeutral = (Button) findViewById(R.id.button_neutral);
}
private String getContextTag() {
return getIntent().getStringExtra(EXTRA_CONTEXT_TAG);
}
private String getKeyAction() {
return getIntent().getStringExtra(EXTRA_KEY_ACTION);
}
}

View File

@ -64,6 +64,13 @@ import java.util.ArrayList;
import java.util.List;
import static android.text.TextUtils.isEmpty;
import static org.mariotaku.twidere.TwidereConstants.LOGTAG;
import static org.mariotaku.twidere.constant.IntentConstants.EXTRA_ACCOUNT_KEY;
import static org.mariotaku.twidere.constant.IntentConstants.EXTRA_IS_MY_ACCOUNT;
import static org.mariotaku.twidere.constant.IntentConstants.EXTRA_SCREEN_NAME;
import static org.mariotaku.twidere.constant.IntentConstants.EXTRA_USER;
import static org.mariotaku.twidere.constant.IntentConstants.EXTRA_USER_LIST;
import static org.mariotaku.twidere.constant.IntentConstants.INTENT_ACTION_SELECT_USER;
import static org.mariotaku.twidere.util.DataStoreUtils.getAccountScreenName;
public class UserListSelectorActivity extends BaseActivity implements OnClickListener,

View File

@ -102,8 +102,8 @@ public class KeyboardShortcutsFragment extends BasePreferenceFragment implements
protected void onClick() {
final Context context = getContext();
final Intent intent = new Intent(context, KeyboardShortcutPreferenceCompatActivity.class);
intent.putExtra(KeyboardShortcutPreferenceCompatActivity.EXTRA_CONTEXT_TAG, mContextTag);
intent.putExtra(KeyboardShortcutPreferenceCompatActivity.EXTRA_KEY_ACTION, mAction);
intent.putExtra(KeyboardShortcutPreferenceCompatActivity.Companion.getEXTRA_CONTEXT_TAG(), mContextTag);
intent.putExtra(KeyboardShortcutPreferenceCompatActivity.Companion.getEXTRA_KEY_ACTION(), mAction);
context.startActivity(intent);
}

View File

@ -128,6 +128,7 @@ import org.mariotaku.twidere.util.TwidereLinkify.HighlightStyle;
import org.mariotaku.twidere.view.CardMediaContainer.PreviewStyle;
import org.mariotaku.twidere.view.ShapedImageView;
import org.mariotaku.twidere.view.ShapedImageView.ShapeStyle;
import org.mariotaku.twidere.view.TabPagerIndicator;
import java.io.Closeable;
import java.io.File;
@ -807,10 +808,10 @@ public final class Utils implements Constants {
public static int getTabDisplayOptionInt(final String option) {
if (VALUE_TAB_DISPLAY_OPTION_ICON.equals(option))
return VALUE_TAB_DISPLAY_OPTION_CODE_ICON;
return TabPagerIndicator.DisplayOption.ICON;
else if (VALUE_TAB_DISPLAY_OPTION_LABEL.equals(option))
return VALUE_TAB_DISPLAY_OPTION_CODE_LABEL;
return VALUE_TAB_DISPLAY_OPTION_CODE_BOTH;
return TabPagerIndicator.DisplayOption.LABEL;
return TabPagerIndicator.DisplayOption.BOTH;
}
public static boolean hasNavBar(@NonNull Context context) {

View File

@ -22,16 +22,16 @@ package org.mariotaku.twidere.view;
import android.content.Context;
import android.graphics.Rect;
import android.support.annotation.NonNull;
import android.support.v4.widget.SwipeRefreshLayout;
import android.util.AttributeSet;
import android.view.MotionEvent;
import org.mariotaku.chameleon.view.ChameleonSwipeRefreshLayout;
import org.mariotaku.twidere.view.iface.IExtendedView;
/**
* Created by mariotaku on 15/4/25.
*/
public class ExtendedSwipeRefreshLayout extends SwipeRefreshLayout implements IExtendedView {
public class ExtendedSwipeRefreshLayout extends ChameleonSwipeRefreshLayout implements IExtendedView {
private TouchInterceptor mTouchInterceptor;
private OnSizeChangedListener mOnSizeChangedListener;

View File

@ -1,15 +1,22 @@
package org.mariotaku.twidere.view;
import android.content.Context;
import android.graphics.Color;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.util.AttributeSet;
import android.view.MotionEvent;
public class HomeDrawerLayout extends DrawerLayout {
import org.mariotaku.chameleon.Chameleon;
import org.mariotaku.chameleon.ChameleonUtils;
import org.mariotaku.chameleon.ChameleonView;
import org.mariotaku.twidere.util.support.WindowSupport;
public class HomeDrawerLayout extends DrawerLayout implements ChameleonView, ChameleonView.StatusBarThemeable {
private ShouldDisableDecider mShouldDisableDecider;
private int state;
private int mStartLockMode, mEndLockMode;
public HomeDrawerLayout(Context context) {
@ -49,6 +56,43 @@ public class HomeDrawerLayout extends DrawerLayout {
return super.dispatchTouchEvent(ev);
}
@Override
public boolean isPostApplyTheme() {
return false;
}
@Nullable
@Override
public Appearance createAppearance(Context context, AttributeSet attributeSet, Chameleon.Theme theme) {
Appearance appearance = new Appearance();
WindowSupport.setStatusBarColor(ChameleonUtils.getActivity(context).getWindow(), Color.TRANSPARENT);
appearance.setStatusBarBackgroundColor(ChameleonUtils.darkenColor(theme.getColorToolbar()));
return appearance;
}
@Override
public void applyAppearance(@NonNull ChameleonView.Appearance appearance) {
Appearance a = (Appearance) appearance;
setStatusBarBackgroundColor(a.getStatusBarBackgroundColor());
}
@Override
public boolean isStatusBarColorHandled() {
return true;
}
public static class Appearance implements ChameleonView.Appearance {
int statusBarBackgroundColor;
public int getStatusBarBackgroundColor() {
return statusBarBackgroundColor;
}
public void setStatusBarBackgroundColor(int statusBarBackgroundColor) {
this.statusBarBackgroundColor = statusBarBackgroundColor;
}
}
public interface ShouldDisableDecider {
boolean shouldDisableTouch(MotionEvent e);
}

View File

@ -4,10 +4,14 @@ import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.support.annotation.ColorInt;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewCompat;
import android.support.v4.view.ViewPager;
@ -24,7 +28,9 @@ import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import org.mariotaku.twidere.Constants;
import org.mariotaku.chameleon.Chameleon;
import org.mariotaku.chameleon.ChameleonUtils;
import org.mariotaku.chameleon.ChameleonView;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.adapter.decorator.DividerItemDecoration;
import org.mariotaku.twidere.util.ThemeUtils;
@ -36,11 +42,8 @@ import java.lang.annotation.RetentionPolicy;
/**
* Created by mariotaku on 14/10/21.
*/
public class TabPagerIndicator extends RecyclerView implements PagerIndicator, Constants {
public class TabPagerIndicator extends RecyclerView implements PagerIndicator, ChameleonView {
public static final int LABEL = VALUE_TAB_DISPLAY_OPTION_CODE_LABEL;
public static final int ICON = VALUE_TAB_DISPLAY_OPTION_CODE_ICON;
public static final int BOTH = VALUE_TAB_DISPLAY_OPTION_CODE_BOTH;
private final int mStripHeight;
private final TabPagerIndicatorAdapter mIndicatorAdapter;
@ -61,7 +64,7 @@ public class TabPagerIndicator extends RecyclerView implements PagerIndicator, C
mIndicatorAdapter = new TabPagerIndicatorAdapter(this);
mItemDecoration = new DividerItemDecoration(context, HORIZONTAL);
mStripHeight = res.getDimensionPixelSize(R.dimen.element_spacing_small);
ViewCompat.setOverScrollMode(this, ViewCompat.OVER_SCROLL_NEVER);
setOverScrollMode(OVER_SCROLL_NEVER);
setHorizontalScrollBarEnabled(false);
setVerticalScrollBarEnabled(false);
setLayoutManager(mLayoutManager = new TabLayoutManager(context, this));
@ -74,7 +77,8 @@ public class TabPagerIndicator extends RecyclerView implements PagerIndicator, C
setStripColor(a.getColor(R.styleable.TabPagerIndicator_tabStripColor, 0));
setIconColor(a.getColor(R.styleable.TabPagerIndicator_tabIconColor, 0));
setLabelColor(a.getColor(R.styleable.TabPagerIndicator_tabLabelColor, ThemeUtils.getTextColorPrimary(context)));
setTabDisplayOption(a.getInt(R.styleable.TabPagerIndicator_tabDisplayOption, ICON));
//noinspection WrongConstant
setTabDisplayOption(a.getInt(R.styleable.TabPagerIndicator_tabDisplayOption, DisplayOption.ICON));
setTabShowDivider(a.getBoolean(R.styleable.TabPagerIndicator_tabShowDivider, false));
final int dividerVerticalPadding = a.getDimensionPixelSize(R.styleable.TabPagerIndicator_tabDividerVerticalPadding, 0);
final int dividerHorizontalPadding = a.getDimensionPixelSize(R.styleable.TabPagerIndicator_tabDividerHorizontalPadding, 0);
@ -102,9 +106,6 @@ public class TabPagerIndicator extends RecyclerView implements PagerIndicator, C
return mIndicatorAdapter.getItemContext();
}
public void getTabSpecs() {
}
public void setColumns(int columns) {
mColumns = columns;
notifyDataSetChanged();
@ -221,6 +222,43 @@ public class TabPagerIndicator extends RecyclerView implements PagerIndicator, C
mIndicatorAdapter.setTabProvider((TabProvider) adapter);
}
@Override
public boolean isPostApplyTheme() {
return false;
}
@Nullable
@Override
public Appearance createAppearance(Context context, AttributeSet attributeSet, Chameleon.Theme theme) {
final Appearance appearance = new Appearance();
final int toolbarColor = theme.getColorToolbar();
final boolean isLight = ChameleonUtils.isColorLight(toolbarColor);
final int itemColor = isLight ? Color.BLACK : Color.WHITE;
appearance.setLabelColor(itemColor);
appearance.setIconColor(itemColor);
if (theme.isToolbarColored()) {
appearance.setStripColor(itemColor);
} else {
appearance.setStripColor(theme.getColorAccent());
}
return appearance;
}
@Override
public void applyAppearance(@NonNull ChameleonView.Appearance appearance) {
Appearance a = (Appearance) appearance;
setIconColor(a.getIconColor());
setLabelColor(a.getLabelColor());
setStripColor(a.getStripColor());
updateAppearance();
}
public void updateAppearance() {
final int positionStart = mLayoutManager.findFirstVisibleItemPosition();
final int itemCount = mLayoutManager.findLastVisibleItemPosition() - positionStart + 1;
mIndicatorAdapter.notifyItemRangeChanged(positionStart, itemCount);
}
private int getTabHorizontalPadding() {
return mHorizontalPadding;
}
@ -230,11 +268,11 @@ public class TabPagerIndicator extends RecyclerView implements PagerIndicator, C
}
private boolean isIconDisplayed() {
return (mOption & ICON) != 0;
return (mOption & DisplayOption.ICON) != 0;
}
private boolean isLabelDisplayed() {
return (mOption & LABEL) != 0;
return (mOption & DisplayOption.LABEL) != 0;
}
private boolean isTabExpandEnabled() {
@ -261,9 +299,56 @@ public class TabPagerIndicator extends RecyclerView implements PagerIndicator, C
mVerticalPadding = padding;
}
@IntDef({ICON, LABEL, BOTH})
private boolean isTabSelected(int position) {
final int current = getCurrentItem();
final int columns = getColumns();
final int count = getCount();
if (current + columns > count) {
return position >= count - columns;
}
return position >= current && position < current + columns;
}
private int getColumns() {
if (mColumns > 0) return mColumns;
return 1;
}
public static class Appearance implements ChameleonView.Appearance {
@ColorInt
int iconColor, labelColor, stripColor;
public int getIconColor() {
return iconColor;
}
public void setIconColor(int iconColor) {
this.iconColor = iconColor;
}
public int getLabelColor() {
return labelColor;
}
public void setLabelColor(int labelColor) {
this.labelColor = labelColor;
}
public int getStripColor() {
return stripColor;
}
public void setStripColor(int stripColor) {
this.stripColor = stripColor;
}
}
@IntDef({DisplayOption.ICON, DisplayOption.LABEL, DisplayOption.BOTH})
@Retention(RetentionPolicy.SOURCE)
public @interface DisplayOption {
int LABEL = 0x1;
int ICON = 0x2;
int BOTH = LABEL | ICON;
}
public static final class SampleView extends LinearLayout {
@ -299,7 +384,7 @@ public class TabPagerIndicator extends RecyclerView implements PagerIndicator, C
this.setStripColor(a.getColor(R.styleable.TabPagerIndicator_tabStripColor, 0));
this.setIconColor(a.getColor(R.styleable.TabPagerIndicator_tabIconColor, 0));
this.setLabelColor(a.getColor(R.styleable.TabPagerIndicator_tabLabelColor, ThemeUtils.getTextColorPrimary(context)));
this.setTabDisplayOption(a.getInt(R.styleable.TabPagerIndicator_tabDisplayOption, ICON));
this.setTabDisplayOption(a.getInt(R.styleable.TabPagerIndicator_tabDisplayOption, DisplayOption.ICON));
this.setTabShowDivider(a.getBoolean(R.styleable.TabPagerIndicator_tabShowDivider, false));
a.recycle();
@ -346,11 +431,11 @@ public class TabPagerIndicator extends RecyclerView implements PagerIndicator, C
tabIcon.setImageResource(icon);
tabIcon.setColorFilter(iconColor, PorterDuff.Mode.SRC_ATOP);
tabIcon.setVisibility((tabDisplayOption & ICON) != 0 ? VISIBLE : GONE);
tabIcon.setVisibility((tabDisplayOption & DisplayOption.ICON) != 0 ? VISIBLE : GONE);
tabLabel.setText(label);
tabLabel.setTextColor(labelColor);
tabLabel.setVisibility((tabDisplayOption & LABEL) != 0 ? VISIBLE : GONE);
tabLabel.setVisibility((tabDisplayOption & DisplayOption.LABEL) != 0 ? VISIBLE : GONE);
badgeView.setText(String.valueOf(unread));
badgeView.setVisibility(unread != 0 ? VISIBLE : GONE);
@ -420,7 +505,7 @@ public class TabPagerIndicator extends RecyclerView implements PagerIndicator, C
private final TextView labelView;
private final BadgeView badgeView;
public TabItemHolder(TabPagerIndicator indicator, View itemView) {
TabItemHolder(TabPagerIndicator indicator, View itemView) {
super(itemView);
this.indicator = indicator;
this.itemView = (ItemLayout) itemView;
@ -443,17 +528,17 @@ public class TabPagerIndicator extends RecyclerView implements PagerIndicator, C
return indicator.dispatchTabLongClick(position);
}
public void setBadge(int count, boolean display) {
void setBadge(int count, boolean display) {
badgeView.setText(String.valueOf(count));
badgeView.setVisibility(display && count > 0 ? VISIBLE : GONE);
}
public void setDisplayOption(boolean iconDisplayed, boolean labelDisplayed) {
void setDisplayOption(boolean iconDisplayed, boolean labelDisplayed) {
iconView.setVisibility(iconDisplayed ? VISIBLE : GONE);
labelView.setVisibility(labelDisplayed ? VISIBLE : GONE);
}
public void setIconColor(int color) {
void setIconColor(int color) {
if (color != 0) {
iconView.setColorFilter(color);
} else {
@ -461,23 +546,23 @@ public class TabPagerIndicator extends RecyclerView implements PagerIndicator, C
}
}
public void setLabelColor(int color) {
void setLabelColor(int color) {
labelView.setTextColor(color);
}
public void setPadding(int horizontalPadding, int verticalPadding) {
void setPadding(int horizontalPadding, int verticalPadding) {
itemView.setPadding(horizontalPadding, verticalPadding, horizontalPadding, verticalPadding);
}
public void setStripColor(int color) {
void setStripColor(int color) {
itemView.setStripColor(color);
}
public void setStripHeight(int stripHeight) {
void setStripHeight(int stripHeight) {
itemView.setStripHeight(stripHeight);
}
public void setTabData(Drawable icon, CharSequence title, boolean activated) {
void setTabData(Drawable icon, CharSequence title, boolean activated) {
itemView.setContentDescription(title);
iconView.setImageDrawable(icon);
labelView.setText(title);
@ -490,7 +575,7 @@ public class TabPagerIndicator extends RecyclerView implements PagerIndicator, C
private boolean mTabExpandEnabled;
private final RecyclerView mRecyclerView;
public TabLayoutManager(Context context, RecyclerView recyclerView) {
TabLayoutManager(Context context, RecyclerView recyclerView) {
super(context, HORIZONTAL, false);
mRecyclerView = recyclerView;
setAutoMeasureEnabled(true);
@ -518,21 +603,15 @@ public class TabPagerIndicator extends RecyclerView implements PagerIndicator, C
return false;
}
public boolean isTabExpandEnabled() {
boolean isTabExpandEnabled() {
return mTabExpandEnabled;
}
public void setTabExpandEnabled(boolean tabExpandEnabled) {
void setTabExpandEnabled(boolean tabExpandEnabled) {
mTabExpandEnabled = tabExpandEnabled;
}
}
public void updateAppearance() {
final int positionStart = mLayoutManager.findFirstVisibleItemPosition();
final int itemCount = mLayoutManager.findLastVisibleItemPosition() - positionStart + 1;
mIndicatorAdapter.notifyItemRangeChanged(positionStart, itemCount);
}
private static class TabPagerIndicatorAdapter extends Adapter<TabItemHolder> {
private final TabPagerIndicator mIndicator;
@ -544,16 +623,16 @@ public class TabPagerIndicator extends RecyclerView implements PagerIndicator, C
private int mStripColor, mIconColor, mLabelColor;
private boolean mDisplayBadge;
public TabPagerIndicatorAdapter(TabPagerIndicator indicator) {
TabPagerIndicatorAdapter(TabPagerIndicator indicator) {
mIndicator = indicator;
mUnreadCounts = new SparseIntArray();
}
public Context getItemContext() {
Context getItemContext() {
return mItemContext;
}
public void setItemContext(Context itemContext) {
void setItemContext(Context itemContext) {
mItemContext = itemContext;
mInflater = LayoutInflater.from(itemContext);
}
@ -587,49 +666,34 @@ public class TabPagerIndicator extends RecyclerView implements PagerIndicator, C
return mTabProvider.getCount();
}
public void setBadge(int position, int count) {
void setBadge(int position, int count) {
mUnreadCounts.put(position, count);
notifyItemChanged(position);
}
public void clearBadge() {
void clearBadge() {
mUnreadCounts.clear();
notifyDataSetChanged();
}
public void setDisplayBadge(boolean display) {
void setDisplayBadge(boolean display) {
mDisplayBadge = display;
}
public void setIconColor(int color) {
void setIconColor(int color) {
mIconColor = color;
}
public void setLabelColor(int color) {
void setLabelColor(int color) {
mLabelColor = color;
}
public void setStripColor(int color) {
void setStripColor(int color) {
mStripColor = color;
}
public void setTabProvider(TabProvider tabProvider) {
void setTabProvider(TabProvider tabProvider) {
mTabProvider = tabProvider;
}
}
private boolean isTabSelected(int position) {
final int current = getCurrentItem();
final int columns = getColumns();
final int count = getCount();
if (current + columns > count) {
return position >= count - columns;
}
return position >= current && position < current + columns;
}
private int getColumns() {
if (mColumns > 0) return mColumns;
return 1;
}
}

View File

@ -27,18 +27,23 @@ import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.view.ViewCompat;
import android.support.v4.view.WindowInsetsCompat;
import android.util.AttributeSet;
import android.view.View;
import org.mariotaku.chameleon.Chameleon;
import org.mariotaku.chameleon.ChameleonUtils;
import org.mariotaku.chameleon.ChameleonView;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.view.iface.TintedStatusLayout;
/**
* Created by mariotaku on 14/11/26.
*/
public class TintedStatusFrameLayout extends ExtendedFrameLayout implements TintedStatusLayout {
public class TintedStatusFrameLayout extends ExtendedFrameLayout implements TintedStatusLayout,
ChameleonView, ChameleonView.StatusBarThemeable {
private final Paint mColorPaint;
private boolean mSetPadding;
@ -119,6 +124,42 @@ public class TintedStatusFrameLayout extends ExtendedFrameLayout implements Tint
mWindowInsetsListener = listener;
}
@Override
public boolean isPostApplyTheme() {
return false;
}
@Nullable
@Override
public Appearance createAppearance(Context context, AttributeSet attributeSet, Chameleon.Theme theme) {
Appearance appearance = new Appearance();
appearance.setColor(ChameleonUtils.darkenColor(theme.getColorToolbar()));
return appearance;
}
@Override
public void applyAppearance(@NonNull ChameleonView.Appearance appearance) {
Appearance a = (Appearance) appearance;
setStatusBarColor(a.getColor());
}
@Override
public boolean isStatusBarColorHandled() {
return true;
}
public static class Appearance implements ChameleonView.Appearance {
int color;
public int getColor() {
return color;
}
public void setColor(int color) {
this.color = color;
}
}
public interface WindowInsetsListener {
void onApplyWindowInsets(int left, int top, int right, int bottom);
}

View File

@ -1,124 +0,0 @@
package org.mariotaku.chameleon;
import android.app.Activity;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.view.LayoutInflaterCompat;
import android.view.LayoutInflater;
import org.mariotaku.chameleon.internal.ChameleonInflationFactory;
/**
* Created by mariotaku on 2016/12/18.
*/
public class Chameleon {
private final Activity activity;
private Chameleon(Activity activity) {
this.activity = activity;
}
public static Chameleon getInstance(Activity activity) {
return new Chameleon(activity);
}
public void preApply() {
final LayoutInflater inflater = activity.getLayoutInflater();
final ChameleonInflationFactory factory = new ChameleonInflationFactory();
LayoutInflaterCompat.setFactory(inflater, factory);
}
public void postApply() {
}
public void invalidateActivity() {
}
public void cleanUp() {
}
public void themeOverflow() {
}
@NonNull
public static Theme getOverrideTheme(Context context, Object obj) {
if (obj instanceof Themeable) {
return ((Themeable) obj).getOverrideTheme();
}
return Theme.from(context);
}
/**
* Created by mariotaku on 2016/12/18.
*/
public static class Theme {
int primaryColor;
int accentColor;
int toolbarColor;
boolean toolbarColored;
int textColorPrimary;
public int getAccentColor() {
return accentColor;
}
public void setAccentColor(int accentColor) {
this.accentColor = accentColor;
}
public int getPrimaryColor() {
return primaryColor;
}
public void setPrimaryColor(int primaryColor) {
this.primaryColor = primaryColor;
}
public int getToolbarColor() {
return toolbarColor;
}
public void setToolbarColor(int toolbarColor) {
this.toolbarColor = toolbarColor;
}
public boolean isToolbarColored() {
return toolbarColored;
}
public void setToolbarColored(boolean toolbarColored) {
this.toolbarColored = toolbarColored;
}
public int getTextColorPrimary() {
return textColorPrimary;
}
public void setTextColorPrimary(int textColorPrimary) {
this.textColorPrimary = textColorPrimary;
}
@NonNull
public static Theme from(Context context) {
Theme theme = new Theme();
return theme;
}
}
/**
* Created by mariotaku on 2016/12/18.
*/
public interface Themeable {
@Nullable
Theme getOverrideTheme();
}
}

View File

@ -1,17 +0,0 @@
package org.mariotaku.chameleon.internal;
import android.content.Context;
import android.support.v4.view.LayoutInflaterFactory;
import android.util.AttributeSet;
import android.view.View;
/**
* Created by mariotaku on 2016/12/18.
*/
public class ChameleonInflationFactory implements LayoutInflaterFactory {
@Override
public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
return null;
}
}

View File

@ -42,7 +42,6 @@ import org.mariotaku.chameleon.Chameleon
import org.mariotaku.chameleon.ChameleonActivity
import org.mariotaku.kpreferences.KPreferences
import org.mariotaku.twidere.BuildConfig
import org.mariotaku.twidere.Constants
import org.mariotaku.twidere.TwidereConstants.SHARED_PREFERENCES_NAME
import org.mariotaku.twidere.activity.iface.IControlBarActivity
import org.mariotaku.twidere.activity.iface.IExtendedActivity
@ -58,7 +57,7 @@ import java.util.*
import javax.inject.Inject
@SuppressLint("Registered")
open class BaseActivity : ChameleonActivity(), Constants, IExtendedActivity, IThemedActivity,
open class BaseActivity : ChameleonActivity(), IExtendedActivity, IThemedActivity,
IControlBarActivity, OnFitSystemWindowsListener, SystemWindowsInsetsCallback,
KeyboardShortcutCallback, OnPreferenceDisplayDialogCallback {
@Inject
@ -341,7 +340,13 @@ open class BaseActivity : ChameleonActivity(), Constants, IExtendedActivity, ITh
}
override fun getOverrideTheme(): Chameleon.Theme {
return Chameleon.Theme()
val theme = Chameleon.Theme.from(this)
theme.colorAccent = ThemeUtils.getUserAccentColor(this)
theme.colorPrimary = ThemeUtils.getUserAccentColor(this)
if (theme.isToolbarColored) {
theme.colorToolbar = theme.colorPrimary
}
return theme
}
companion object {

View File

@ -341,10 +341,10 @@ class HomeActivity : BaseActivity(), OnClickListener, OnPageChangeListener, Supp
mainTabs.setOnPageChangeListener(this)
mainTabs.setColumns(tabColumns)
if (tabDisplayOptionInt == 0) {
tabDisplayOptionInt = TabPagerIndicator.ICON
tabDisplayOptionInt = TabPagerIndicator.DisplayOption.ICON
}
mainTabs.setTabDisplayOption(tabDisplayOptionInt)
mainTabs.setTabExpandEnabled(tabDisplayOptionInt and TabPagerIndicator.LABEL == 0)
mainTabs.setTabExpandEnabled(tabDisplayOptionInt and TabPagerIndicator.DisplayOption.LABEL == 0)
mainTabs.setDisplayBadge(preferences.getBoolean(SharedPreferenceConstants.KEY_UNREAD_COUNT, true))
mainTabs.updateAppearance()

View File

@ -0,0 +1,125 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.activity
import android.os.Bundle
import android.text.TextUtils
import android.view.KeyEvent
import android.view.View
import android.view.View.OnClickListener
import kotlinx.android.synthetic.main.activity_keyboard_shortcut_input.*
import org.mariotaku.twidere.R
import org.mariotaku.twidere.constant.SharedPreferenceConstants.VALUE_THEME_BACKGROUND_DEFAULT
import org.mariotaku.twidere.util.KeyboardShortcutsHandler
import org.mariotaku.twidere.util.KeyboardShortcutsHandler.KeyboardShortcutSpec
/**
* Created by mariotaku on 15/4/20.
*/
class KeyboardShortcutPreferenceCompatActivity : BaseActivity(), OnClickListener {
private var keySpec: KeyboardShortcutSpec? = null
private var metaState: Int = 0
override val themeBackgroundOption: String
get() = VALUE_THEME_BACKGROUND_DEFAULT
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_keyboard_shortcut_input)
title = KeyboardShortcutsHandler.getActionLabel(this, keyAction)
buttonPositive.setOnClickListener(this)
buttonNegative.setOnClickListener(this)
buttonNeutral.setOnClickListener(this)
}
override fun onClick(v: View) {
when (v.id) {
R.id.buttonPositive -> {
if (keySpec == null) return
keyboardShortcutsHandler.register(keySpec, keyAction)
finish()
}
R.id.buttonNeutral -> {
keyboardShortcutsHandler.unregister(keyAction)
finish()
}
R.id.buttonNegative -> {
finish()
}
}
}
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
if (KeyEvent.isModifierKey(keyCode)) {
metaState = metaState or KeyboardShortcutsHandler.getMetaStateForKeyCode(keyCode)
}
return super.onKeyDown(keyCode, event)
}
override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean {
if (KeyEvent.isModifierKey(keyCode)) {
metaState = metaState and KeyboardShortcutsHandler.getMetaStateForKeyCode(keyCode).inv()
}
val keyAction = keyAction ?: return false
val spec = KeyboardShortcutsHandler.getKeyboardShortcutSpec(contextTag,
keyCode, event, KeyEvent.normalizeMetaState(metaState or event.metaState))
if (spec == null || !spec.isValid) {
return super.onKeyUp(keyCode, event)
}
keySpec = spec
keysLabel.text = spec.toKeyString()
val oldAction = keyboardShortcutsHandler.findAction(spec)
val copyOfSpec = spec.copy()
copyOfSpec.contextTag = null
val oldGeneralAction = keyboardShortcutsHandler.findAction(copyOfSpec)
if (!TextUtils.isEmpty(oldAction) && keyAction != oldAction) {
// Conflicts with keys in same context tag
conflictLabel.visibility = View.VISIBLE
val label = KeyboardShortcutsHandler.getActionLabel(this, oldAction)
conflictLabel.text = getString(R.string.conflicts_with_name, label)
buttonPositive.setText(R.string.overwrite)
} else if (!TextUtils.isEmpty(oldGeneralAction) && keyAction != oldGeneralAction) {
// Conflicts with keys in root context
conflictLabel.visibility = View.VISIBLE
val label = KeyboardShortcutsHandler.getActionLabel(this, oldGeneralAction)
conflictLabel.text = getString(R.string.conflicts_with_name, label)
buttonPositive.setText(R.string.overwrite)
} else {
conflictLabel.visibility = View.GONE
buttonPositive.setText(android.R.string.ok)
}
return true
}
private val contextTag: String
get() = intent.getStringExtra(EXTRA_CONTEXT_TAG)
private val keyAction: String?
get() = intent.getStringExtra(EXTRA_KEY_ACTION)
companion object {
val EXTRA_CONTEXT_TAG = "context_tag"
val EXTRA_KEY_ACTION = "key_action"
}
}

View File

@ -48,7 +48,7 @@ abstract class AbsToolbarTabPagesFragment : BaseSupportFragment(), RefreshScroll
viewPager.offscreenPageLimit = 2
viewPager.addOnPageChangeListener(this)
toolbarTabs.setViewPager(viewPager)
toolbarTabs.setTabDisplayOption(TabPagerIndicator.LABEL)
toolbarTabs.setTabDisplayOption(TabPagerIndicator.DisplayOption.LABEL)
addTabs(pagerAdapter!!)

View File

@ -79,6 +79,7 @@ import nl.komponents.kovenant.ui.promiseOnUi
import nl.komponents.kovenant.ui.successUi
import org.apache.commons.lang3.ObjectUtils
import org.mariotaku.chameleon.Chameleon
import org.mariotaku.chameleon.ChameleonUtils
import org.mariotaku.ktextension.Bundle
import org.mariotaku.ktextension.empty
import org.mariotaku.ktextension.set
@ -430,7 +431,7 @@ class UserFragment : BaseSupportFragment(), OnClickListener, OnLinkClickListener
profileImage.visibility = View.GONE
profileType.visibility = View.GONE
val theme = Chameleon.getOverrideTheme(activity, activity)
setUiColor(theme.primaryColor)
setUiColor(theme.colorPrimary)
return
}
val adapter = pagerAdapter
@ -496,7 +497,7 @@ class UserFragment : BaseSupportFragment(), OnClickListener, OnLinkClickListener
setUiColor(user.link_color)
} else {
val theme = Chameleon.getOverrideTheme(activity, activity)
setUiColor(theme.primaryColor)
setUiColor(theme.colorPrimary)
}
val defWidth = resources.displayMetrics.widthPixels
val width = if (bannerWidth > 0) bannerWidth else defWidth
@ -678,7 +679,7 @@ class UserFragment : BaseSupportFragment(), OnClickListener, OnLinkClickListener
viewPager.offscreenPageLimit = 3
viewPager.adapter = pagerAdapter
toolbarTabs.setViewPager(viewPager)
toolbarTabs.setTabDisplayOption(TabPagerIndicator.LABEL)
toolbarTabs.setTabDisplayOption(TabPagerIndicator.DisplayOption.LABEL)
toolbarTabs.setOnPageChangeListener(this)
followContainer.follow.setOnClickListener(this)
@ -1277,15 +1278,12 @@ class UserFragment : BaseSupportFragment(), OnClickListener, OnLinkClickListener
}
val activity = activity as BaseActivity
val theme = Chameleon.getOverrideTheme(activity, activity)
primaryColor = theme.primaryColor
primaryColorDark = ThemeUtils.computeDarkColor(primaryColor)
if (theme.isToolbarColored) {
primaryColor = color
primaryColorDark = ThemeUtils.computeDarkColor(color)
} else {
primaryColor = theme.primaryColor
primaryColorDark = Color.BLACK
primaryColor = theme.colorToolbar
}
primaryColorDark = ChameleonUtils.darkenColor(primaryColor)
if (actionBarBackground != null) {
actionBarBackground!!.color = primaryColor
}
@ -1293,7 +1291,7 @@ class UserFragment : BaseSupportFragment(), OnClickListener, OnLinkClickListener
if (theme.isToolbarColored) {
taskColor = color
} else {
taskColor = theme.toolbarColor
taskColor = theme.colorToolbar
}
if (user != null) {
val name = userColorNameManager.getDisplayName(user, nameFirst)

View File

@ -20,13 +20,13 @@
package org.mariotaku.twidere.view
import android.content.Context
import android.support.v7.widget.AppCompatMultiAutoCompleteTextView
import android.text.InputType
import android.text.Selection
import android.text.method.ArrowKeyMovementMethod
import android.text.method.MovementMethod
import android.util.AttributeSet
import android.widget.AdapterView
import org.mariotaku.chameleon.view.ChameleonMultiAutoCompleteTextView
import org.mariotaku.twidere.adapter.ComposeAutoCompleteAdapter
import org.mariotaku.twidere.model.UserKey
import org.mariotaku.twidere.util.EmojiSupportUtils
@ -35,7 +35,7 @@ import org.mariotaku.twidere.util.widget.StatusTextTokenizer
class ComposeEditText @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null
) : AppCompatMultiAutoCompleteTextView(context, attrs) {
) : ChameleonMultiAutoCompleteTextView(context, attrs) {
private var adapter: ComposeAutoCompleteAdapter? = null
var accountKey: UserKey? = null

View File

@ -20,7 +20,6 @@
package org.mariotaku.twidere.view
import android.content.Context
import android.support.v7.widget.AppCompatTextView
import android.text.Spannable
import android.text.method.MovementMethod
import android.text.style.ClickableSpan
@ -28,23 +27,20 @@ import android.util.AttributeSet
import android.view.KeyEvent
import android.view.MotionEvent
import android.widget.TextView
import org.mariotaku.chameleon.view.ChameleonTextView
import org.mariotaku.twidere.util.EmojiSupportUtils
/**
* Returns true when not clicking links
* Created by mariotaku on 15/11/20.
*/
class TimelineContentTextView : AppCompatTextView {
class TimelineContentTextView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyle: Int = 0
) : ChameleonTextView(context, attrs, defStyle) {
constructor(context: Context) : super(context) {
EmojiSupportUtils.initForTextView(this)
}
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
EmojiSupportUtils.initForTextView(this)
}
constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) {
init {
EmojiSupportUtils.initForTextView(this)
}

View File

@ -38,7 +38,6 @@
android:layout_width="match_parent"
android:layout_height="?actionBarSize"
android:layout_alignParentTop="true"
android:background="?colorPrimary"
android:elevation="@dimen/toolbar_elevation"
app:contentInsetEnd="0dp"
app:contentInsetStart="0dp">
@ -87,7 +86,7 @@
android:layout_margin="@dimen/element_spacing_large"
android:clickable="true"
android:src="@drawable/ic_action_status_compose"
android:tag="background|primary_color,tint|primary_color_dependent"
app:backgroundTint="?colorToolbar"
app:elevation="6dp"
app:pressedTranslationZ="12dp"/>

View File

@ -19,13 +19,13 @@
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:divider="?android:attr/dividerHorizontal"
android:dividerPadding="0dip"
android:orientation="vertical"
android:showDividers="middle">
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:divider="?android:attr/dividerHorizontal"
android:dividerPadding="0dip"
android:orientation="vertical"
android:showDividers="middle">
<LinearLayout
android:id="@+id/keyboard_shortcut_input"
@ -37,17 +37,17 @@
android:padding="@dimen/element_spacing_xlarge">
<TextView
android:id="@+id/keys_label"
android:id="@+id/keysLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/keyboard_shortcut_hint"
android:textAppearance="?android:textAppearanceMedium" />
android:textAppearance="?android:textAppearanceMedium"/>
<TextView
android:id="@+id/conflict_label"
android:id="@+id/conflictLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone" />
android:visibility="gone"/>
</LinearLayout>
@ -61,7 +61,7 @@
tools:ignore="UselessParent">
<Button
android:id="@+id/button_negative"
android:id="@+id/buttonNegative"
style="?android:attr/buttonBarButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@ -70,10 +70,10 @@
android:maxLines="2"
android:minHeight="@dimen/element_size_normal"
android:text="@android:string/cancel"
android:textSize="14sp" />
android:textSize="14sp"/>
<Button
android:id="@+id/button_neutral"
android:id="@+id/buttonNeutral"
style="?android:attr/buttonBarButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@ -82,10 +82,10 @@
android:maxLines="2"
android:minHeight="@dimen/element_size_normal"
android:text="@string/clear"
android:textSize="14sp" />
android:textSize="14sp"/>
<Button
android:id="@+id/button_positive"
android:id="@+id/buttonPositive"
style="?android:attr/buttonBarButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@ -94,6 +94,6 @@
android:maxLines="2"
android:minHeight="@dimen/element_size_normal"
android:text="@android:string/ok"
android:textSize="14sp" />
android:textSize="14sp"/>
</LinearLayout>
</LinearLayout>

View File

@ -19,8 +19,8 @@
<item name="quoteIndicatorBackgroundColor">@color/quote_indicator_background_light</item>
<item name="mediaLabelBackground">#dddddd</item>
<!-- ATE attributes -->
<item name="ateThemeKey">light</item>
<item name="colorToolbar">?colorPrimary</item>
<item name="isToolbarColored">true</item>
<item name="actionBarTheme">@null</item>
<item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item>
@ -47,8 +47,8 @@
<item name="messageBubbleColor">@color/message_bubble_color_light</item>
<item name="quoteIndicatorBackgroundColor">@color/quote_indicator_background_light</item>
<!-- ATE attributes -->
<item name="ateThemeKey">light</item>
<item name="colorToolbar">?colorPrimary</item>
<item name="isToolbarColored">true</item>
<item name="actionBarTheme">@null</item>
<item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item>
@ -74,8 +74,8 @@
<item name="messageBubbleColor">@color/message_bubble_color_light</item>
<item name="quoteIndicatorBackgroundColor">@color/quote_indicator_background_light</item>
<!-- ATE attributes -->
<item name="ateThemeKey">light</item>
<item name="colorToolbar">?colorPrimary</item>
<item name="isToolbarColored">true</item>
<item name="actionBarTheme">@null</item>
<item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item>

View File

@ -13,8 +13,8 @@
<attr name="quoteIndicatorBackgroundColor" format="color"/>
<attr name="linePageIndicatorStyle" format="reference"/>
<attr name="mediaLabelBackground" format="color"/>
<attr name="ateThemeKey" format="string"/>
</declare-styleable>
<declare-styleable name="ColorLabelView">
<attr name="ignorePadding" format="boolean"/>
<attr name="backgroundColor" format="color"/>

View File

@ -20,8 +20,8 @@
<item name="quoteIndicatorBackgroundColor">@color/quote_indicator_background_dark</item>
<item name="mediaLabelBackground">#505050</item>
<!-- ATE attributes -->
<item name="ateThemeKey">dark</item>
<item name="colorToolbar">@color/background_color_action_bar_dark</item>
<item name="isToolbarColored">false</item>
<item name="actionBarTheme">@null</item>
<item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item>
@ -55,8 +55,8 @@
<item name="messageBubbleColor">@color/message_bubble_color_dark</item>
<item name="quoteIndicatorBackgroundColor">@color/quote_indicator_background_dark</item>
<!-- ATE attributes -->
<item name="ateThemeKey">dark</item>
<item name="colorToolbar">@color/background_color_action_bar_dark</item>
<item name="isToolbarColored">false</item>
<item name="actionBarTheme">@null</item>
<item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item>
@ -84,8 +84,8 @@
<item name="messageBubbleColor">@color/message_bubble_color_dark</item>
<item name="quoteIndicatorBackgroundColor">@color/quote_indicator_background_dark</item>
<!-- ATE attributes -->
<item name="ateThemeKey">dark</item>
<item name="colorToolbar">@color/background_color_action_bar_dark</item>
<item name="isToolbarColored">false</item>
<item name="actionBarTheme">@null</item>
<item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item>
@ -154,8 +154,8 @@
<item name="messageBubbleColor">@color/message_bubble_color_dark</item>
<item name="quoteIndicatorBackgroundColor">@color/quote_indicator_background_dark</item>
<!-- ATE attributes -->
<item name="ateThemeKey">dark</item>
<item name="colorToolbar">@color/background_color_action_bar_dark</item>
<item name="isToolbarColored">false</item>
<item name="actionBarTheme">@null</item>
<item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item>