From 983bd4cb704f39ef0f458679a04171f982c050e8 Mon Sep 17 00:00:00 2001 From: Mariotaku Lee Date: Sun, 18 Dec 2016 23:16:03 +0800 Subject: [PATCH] developing chameleon theme library - this library will be released in Apache license later --- chameleon/.gitignore | 1 + chameleon/build.gradle | 34 +++ chameleon/proguard-rules.pro | 17 ++ .../chameleon/ExampleInstrumentedTest.java | 26 +++ chameleon/src/main/AndroidManifest.xml | 2 + .../org/mariotaku/chameleon/Chameleon.java | 205 ++++++++++++++++++ .../chameleon/ChameleonActivity.java | 0 .../chameleon/ChameleonTypedArray.java | 77 +++++++ .../mariotaku/chameleon/ChameleonUtils.java | 51 +++++ .../mariotaku/chameleon/ChameleonView.java | 28 +++ .../internal/ChameleonInflationFactory.java | 176 +++++++++++++++ .../internal/LayoutInflaterInternal.java | 106 +++++++++ .../view/ChameleonAutoCompleteTextView.java | 47 ++++ .../chameleon/view/ChameleonEditText.java | 78 +++++++ .../view/ChameleonFloatingActionButton.java | 58 +++++ .../ChameleonMultiAutoCompleteTextView.java | 47 ++++ .../view/ChameleonSwipeRefreshLayout.java | 52 +++++ .../chameleon/view/ChameleonTextView.java | 65 ++++++ .../chameleon/view/ChameleonToolbar.java | 94 ++++++++ chameleon/src/main/res/values/attrs.xml | 26 +++ chameleon/src/main/res/values/colors.xml | 6 + .../mariotaku/chameleon/ExampleUnitTest.java | 17 ++ settings.gradle | 2 +- .../constant/SharedPreferenceConstants.java | 4 - twidere/build.gradle | 1 + ...boardShortcutPreferenceCompatActivity.java | 151 ------------- .../activity/UserListSelectorActivity.java | 7 + .../fragment/KeyboardShortcutsFragment.java | 4 +- .../org/mariotaku/twidere/util/Utils.java | 7 +- .../view/ExtendedSwipeRefreshLayout.java | 4 +- .../twidere/view/HomeDrawerLayout.java | 48 +++- .../twidere/view/TabPagerIndicator.java | 182 +++++++++++----- .../twidere/view/TintedStatusFrameLayout.java | 43 +++- .../org/mariotaku/chameleon/Chameleon.java | 124 ----------- .../internal/ChameleonInflationFactory.java | 17 -- .../twidere/activity/BaseActivity.kt | 11 +- .../twidere/activity/HomeActivity.kt | 4 +- ...eyboardShortcutPreferenceCompatActivity.kt | 125 +++++++++++ .../fragment/AbsToolbarTabPagesFragment.kt | 2 +- .../twidere/fragment/UserFragment.kt | 16 +- .../mariotaku/twidere/view/ComposeEditText.kt | 4 +- .../twidere/view/TimelineContentTextView.kt | 18 +- .../main/res/layout/activity_home_content.xml | 3 +- .../activity_keyboard_shortcut_input.xml | 34 +-- .../src/main/res/values-notnight/themes.xml | 12 +- twidere/src/main/res/values/attrs.xml | 2 +- twidere/src/main/res/values/themes.xml | 16 +- 47 files changed, 1626 insertions(+), 428 deletions(-) create mode 100644 chameleon/.gitignore create mode 100644 chameleon/build.gradle create mode 100644 chameleon/proguard-rules.pro create mode 100644 chameleon/src/androidTest/java/org/mariotaku/chameleon/ExampleInstrumentedTest.java create mode 100644 chameleon/src/main/AndroidManifest.xml create mode 100644 chameleon/src/main/java/org/mariotaku/chameleon/Chameleon.java rename {twidere/src/main/kotlin => chameleon/src/main/java}/org/mariotaku/chameleon/ChameleonActivity.java (100%) create mode 100644 chameleon/src/main/java/org/mariotaku/chameleon/ChameleonTypedArray.java create mode 100644 chameleon/src/main/java/org/mariotaku/chameleon/ChameleonUtils.java create mode 100644 chameleon/src/main/java/org/mariotaku/chameleon/ChameleonView.java create mode 100644 chameleon/src/main/java/org/mariotaku/chameleon/internal/ChameleonInflationFactory.java create mode 100644 chameleon/src/main/java/org/mariotaku/chameleon/internal/LayoutInflaterInternal.java create mode 100644 chameleon/src/main/java/org/mariotaku/chameleon/view/ChameleonAutoCompleteTextView.java create mode 100644 chameleon/src/main/java/org/mariotaku/chameleon/view/ChameleonEditText.java create mode 100644 chameleon/src/main/java/org/mariotaku/chameleon/view/ChameleonFloatingActionButton.java create mode 100644 chameleon/src/main/java/org/mariotaku/chameleon/view/ChameleonMultiAutoCompleteTextView.java create mode 100644 chameleon/src/main/java/org/mariotaku/chameleon/view/ChameleonSwipeRefreshLayout.java create mode 100644 chameleon/src/main/java/org/mariotaku/chameleon/view/ChameleonTextView.java create mode 100644 chameleon/src/main/java/org/mariotaku/chameleon/view/ChameleonToolbar.java create mode 100644 chameleon/src/main/res/values/attrs.xml create mode 100644 chameleon/src/main/res/values/colors.xml create mode 100644 chameleon/src/test/java/org/mariotaku/chameleon/ExampleUnitTest.java delete mode 100644 twidere/src/main/java/org/mariotaku/twidere/activity/KeyboardShortcutPreferenceCompatActivity.java delete mode 100644 twidere/src/main/kotlin/org/mariotaku/chameleon/Chameleon.java delete mode 100644 twidere/src/main/kotlin/org/mariotaku/chameleon/internal/ChameleonInflationFactory.java create mode 100644 twidere/src/main/kotlin/org/mariotaku/twidere/activity/KeyboardShortcutPreferenceCompatActivity.kt diff --git a/chameleon/.gitignore b/chameleon/.gitignore new file mode 100644 index 000000000..796b96d1c --- /dev/null +++ b/chameleon/.gitignore @@ -0,0 +1 @@ +/build diff --git a/chameleon/build.gradle b/chameleon/build.gradle new file mode 100644 index 000000000..ff0911dfb --- /dev/null +++ b/chameleon/build.gradle @@ -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' +} diff --git a/chameleon/proguard-rules.pro b/chameleon/proguard-rules.pro new file mode 100644 index 000000000..a3abb5fc4 --- /dev/null +++ b/chameleon/proguard-rules.pro @@ -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 *; +#} diff --git a/chameleon/src/androidTest/java/org/mariotaku/chameleon/ExampleInstrumentedTest.java b/chameleon/src/androidTest/java/org/mariotaku/chameleon/ExampleInstrumentedTest.java new file mode 100644 index 000000000..e26a2eb37 --- /dev/null +++ b/chameleon/src/androidTest/java/org/mariotaku/chameleon/ExampleInstrumentedTest.java @@ -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 Testing documentation + */ +@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()); + } +} diff --git a/chameleon/src/main/AndroidManifest.xml b/chameleon/src/main/AndroidManifest.xml new file mode 100644 index 000000000..bb57eb4fc --- /dev/null +++ b/chameleon/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + diff --git a/chameleon/src/main/java/org/mariotaku/chameleon/Chameleon.java b/chameleon/src/main/java/org/mariotaku/chameleon/Chameleon.java new file mode 100644 index 000000000..01176ca53 --- /dev/null +++ b/chameleon/src/main/java/org/mariotaku/chameleon/Chameleon.java @@ -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 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(); + } +} diff --git a/twidere/src/main/kotlin/org/mariotaku/chameleon/ChameleonActivity.java b/chameleon/src/main/java/org/mariotaku/chameleon/ChameleonActivity.java similarity index 100% rename from twidere/src/main/kotlin/org/mariotaku/chameleon/ChameleonActivity.java rename to chameleon/src/main/java/org/mariotaku/chameleon/ChameleonActivity.java diff --git a/chameleon/src/main/java/org/mariotaku/chameleon/ChameleonTypedArray.java b/chameleon/src/main/java/org/mariotaku/chameleon/ChameleonTypedArray.java new file mode 100644 index 000000000..dc8ffc8e8 --- /dev/null +++ b/chameleon/src/main/java/org/mariotaku/chameleon/ChameleonTypedArray.java @@ -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); + } +} diff --git a/chameleon/src/main/java/org/mariotaku/chameleon/ChameleonUtils.java b/chameleon/src/main/java/org/mariotaku/chameleon/ChameleonUtils.java new file mode 100644 index 000000000..9d4ed3a91 --- /dev/null +++ b/chameleon/src/main/java/org/mariotaku/chameleon/ChameleonUtils.java @@ -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; + } +} diff --git a/chameleon/src/main/java/org/mariotaku/chameleon/ChameleonView.java b/chameleon/src/main/java/org/mariotaku/chameleon/ChameleonView.java new file mode 100644 index 000000000..6ff8fc2b8 --- /dev/null +++ b/chameleon/src/main/java/org/mariotaku/chameleon/ChameleonView.java @@ -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(); + } +} diff --git a/chameleon/src/main/java/org/mariotaku/chameleon/internal/ChameleonInflationFactory.java b/chameleon/src/main/java/org/mariotaku/chameleon/internal/ChameleonInflationFactory.java new file mode 100644 index 000000000..3d1cbe030 --- /dev/null +++ b/chameleon/src/main/java/org/mariotaku/chameleon/internal/ChameleonInflationFactory.java @@ -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 mPostApplyViews; + + + public ChameleonInflationFactory(@NonNull LayoutInflater inflater, + @Nullable Activity activity, + @Nullable AppCompatDelegate delegate, + @Nullable Chameleon.Theme theme, + @NonNull ArrayMap 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()); + } +} diff --git a/chameleon/src/main/java/org/mariotaku/chameleon/internal/LayoutInflaterInternal.java b/chameleon/src/main/java/org/mariotaku/chameleon/internal/LayoutInflaterInternal.java new file mode 100644 index 000000000..d95d7197f --- /dev/null +++ b/chameleon/src/main/java/org/mariotaku/chameleon/internal/LayoutInflaterInternal.java @@ -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 + } + } + } +} diff --git a/chameleon/src/main/java/org/mariotaku/chameleon/view/ChameleonAutoCompleteTextView.java b/chameleon/src/main/java/org/mariotaku/chameleon/view/ChameleonAutoCompleteTextView.java new file mode 100644 index 000000000..a8af476a4 --- /dev/null +++ b/chameleon/src/main/java/org/mariotaku/chameleon/view/ChameleonAutoCompleteTextView.java @@ -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); + } + +} diff --git a/chameleon/src/main/java/org/mariotaku/chameleon/view/ChameleonEditText.java b/chameleon/src/main/java/org/mariotaku/chameleon/view/ChameleonEditText.java new file mode 100644 index 000000000..8737af0a4 --- /dev/null +++ b/chameleon/src/main/java/org/mariotaku/chameleon/view/ChameleonEditText.java @@ -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; + } + } +} diff --git a/chameleon/src/main/java/org/mariotaku/chameleon/view/ChameleonFloatingActionButton.java b/chameleon/src/main/java/org/mariotaku/chameleon/view/ChameleonFloatingActionButton.java new file mode 100644 index 000000000..f099c22a5 --- /dev/null +++ b/chameleon/src/main/java/org/mariotaku/chameleon/view/ChameleonFloatingActionButton.java @@ -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; + } + +} diff --git a/chameleon/src/main/java/org/mariotaku/chameleon/view/ChameleonMultiAutoCompleteTextView.java b/chameleon/src/main/java/org/mariotaku/chameleon/view/ChameleonMultiAutoCompleteTextView.java new file mode 100644 index 000000000..f2b28af75 --- /dev/null +++ b/chameleon/src/main/java/org/mariotaku/chameleon/view/ChameleonMultiAutoCompleteTextView.java @@ -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); + } + +} diff --git a/chameleon/src/main/java/org/mariotaku/chameleon/view/ChameleonSwipeRefreshLayout.java b/chameleon/src/main/java/org/mariotaku/chameleon/view/ChameleonSwipeRefreshLayout.java new file mode 100644 index 000000000..6ba0603dd --- /dev/null +++ b/chameleon/src/main/java/org/mariotaku/chameleon/view/ChameleonSwipeRefreshLayout.java @@ -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; + } +} diff --git a/chameleon/src/main/java/org/mariotaku/chameleon/view/ChameleonTextView.java b/chameleon/src/main/java/org/mariotaku/chameleon/view/ChameleonTextView.java new file mode 100644 index 000000000..3f4059f72 --- /dev/null +++ b/chameleon/src/main/java/org/mariotaku/chameleon/view/ChameleonTextView.java @@ -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; + } + } +} diff --git a/chameleon/src/main/java/org/mariotaku/chameleon/view/ChameleonToolbar.java b/chameleon/src/main/java/org/mariotaku/chameleon/view/ChameleonToolbar.java new file mode 100644 index 000000000..b06e5689c --- /dev/null +++ b/chameleon/src/main/java/org/mariotaku/chameleon/view/ChameleonToolbar.java @@ -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; + } + } +} diff --git a/chameleon/src/main/res/values/attrs.xml b/chameleon/src/main/res/values/attrs.xml new file mode 100644 index 000000000..def4cf391 --- /dev/null +++ b/chameleon/src/main/res/values/attrs.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/chameleon/src/main/res/values/colors.xml b/chameleon/src/main/res/values/colors.xml new file mode 100644 index 000000000..3ae9fa08f --- /dev/null +++ b/chameleon/src/main/res/values/colors.xml @@ -0,0 +1,6 @@ + + + #00100001 + #00100002 + #00100003 + \ No newline at end of file diff --git a/chameleon/src/test/java/org/mariotaku/chameleon/ExampleUnitTest.java b/chameleon/src/test/java/org/mariotaku/chameleon/ExampleUnitTest.java new file mode 100644 index 000000000..253f3c2da --- /dev/null +++ b/chameleon/src/test/java/org/mariotaku/chameleon/ExampleUnitTest.java @@ -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 Testing documentation + */ +public class ExampleUnitTest { + @Test + public void addition_isCorrect() throws Exception { + assertEquals(4, 2 + 2); + } +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 5a0cacc22..12e0414fb 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,4 +1,4 @@ -include ':twidere' +include ':twidere', ':chameleon' include ':twidere.component.common' include ':twidere.library.extension' include ':twidere.wear' diff --git a/twidere.component.common/src/main/java/org/mariotaku/twidere/constant/SharedPreferenceConstants.java b/twidere.component.common/src/main/java/org/mariotaku/twidere/constant/SharedPreferenceConstants.java index f40cdb7e8..3b1412a1a 100644 --- a/twidere.component.common/src/main/java/org/mariotaku/twidere/constant/SharedPreferenceConstants.java +++ b/twidere.component.common/src/main/java/org/mariotaku/twidere/constant/SharedPreferenceConstants.java @@ -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"; diff --git a/twidere/build.gradle b/twidere/build.gradle index 91848bb0f..3e56ecc98 100644 --- a/twidere/build.gradle +++ b/twidere/build.gradle @@ -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" diff --git a/twidere/src/main/java/org/mariotaku/twidere/activity/KeyboardShortcutPreferenceCompatActivity.java b/twidere/src/main/java/org/mariotaku/twidere/activity/KeyboardShortcutPreferenceCompatActivity.java deleted file mode 100644 index ee83fb7f1..000000000 --- a/twidere/src/main/java/org/mariotaku/twidere/activity/KeyboardShortcutPreferenceCompatActivity.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Twidere - Twitter client for Android - * - * Copyright (C) 2012-2015 Mariotaku Lee - * - * 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 . - */ - -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); - } -} diff --git a/twidere/src/main/java/org/mariotaku/twidere/activity/UserListSelectorActivity.java b/twidere/src/main/java/org/mariotaku/twidere/activity/UserListSelectorActivity.java index db38f708d..6520f6fc6 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/activity/UserListSelectorActivity.java +++ b/twidere/src/main/java/org/mariotaku/twidere/activity/UserListSelectorActivity.java @@ -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, diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/KeyboardShortcutsFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/KeyboardShortcutsFragment.java index 23d3f90fa..6d69144e9 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/KeyboardShortcutsFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/KeyboardShortcutsFragment.java @@ -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); } diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/Utils.java b/twidere/src/main/java/org/mariotaku/twidere/util/Utils.java index 3d347b38f..e1f9b664d 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/Utils.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/Utils.java @@ -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) { diff --git a/twidere/src/main/java/org/mariotaku/twidere/view/ExtendedSwipeRefreshLayout.java b/twidere/src/main/java/org/mariotaku/twidere/view/ExtendedSwipeRefreshLayout.java index 37b919eca..cad5459fe 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/view/ExtendedSwipeRefreshLayout.java +++ b/twidere/src/main/java/org/mariotaku/twidere/view/ExtendedSwipeRefreshLayout.java @@ -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; diff --git a/twidere/src/main/java/org/mariotaku/twidere/view/HomeDrawerLayout.java b/twidere/src/main/java/org/mariotaku/twidere/view/HomeDrawerLayout.java index a0ecfb7ac..761a6c204 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/view/HomeDrawerLayout.java +++ b/twidere/src/main/java/org/mariotaku/twidere/view/HomeDrawerLayout.java @@ -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); } diff --git a/twidere/src/main/java/org/mariotaku/twidere/view/TabPagerIndicator.java b/twidere/src/main/java/org/mariotaku/twidere/view/TabPagerIndicator.java index cc7644df8..4cf9e1344 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/view/TabPagerIndicator.java +++ b/twidere/src/main/java/org/mariotaku/twidere/view/TabPagerIndicator.java @@ -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 { 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; - } } diff --git a/twidere/src/main/java/org/mariotaku/twidere/view/TintedStatusFrameLayout.java b/twidere/src/main/java/org/mariotaku/twidere/view/TintedStatusFrameLayout.java index 76ad43609..db049a22a 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/view/TintedStatusFrameLayout.java +++ b/twidere/src/main/java/org/mariotaku/twidere/view/TintedStatusFrameLayout.java @@ -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); } diff --git a/twidere/src/main/kotlin/org/mariotaku/chameleon/Chameleon.java b/twidere/src/main/kotlin/org/mariotaku/chameleon/Chameleon.java deleted file mode 100644 index bf1de53cd..000000000 --- a/twidere/src/main/kotlin/org/mariotaku/chameleon/Chameleon.java +++ /dev/null @@ -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(); - } -} diff --git a/twidere/src/main/kotlin/org/mariotaku/chameleon/internal/ChameleonInflationFactory.java b/twidere/src/main/kotlin/org/mariotaku/chameleon/internal/ChameleonInflationFactory.java deleted file mode 100644 index fe8a0ab7a..000000000 --- a/twidere/src/main/kotlin/org/mariotaku/chameleon/internal/ChameleonInflationFactory.java +++ /dev/null @@ -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; - } -} diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/activity/BaseActivity.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/activity/BaseActivity.kt index a0dd84eed..5d5d8e6d7 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/activity/BaseActivity.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/activity/BaseActivity.kt @@ -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 { diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/activity/HomeActivity.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/activity/HomeActivity.kt index 1c974a2cb..d32103402 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/activity/HomeActivity.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/activity/HomeActivity.kt @@ -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() diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/activity/KeyboardShortcutPreferenceCompatActivity.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/activity/KeyboardShortcutPreferenceCompatActivity.kt new file mode 100644 index 000000000..786cbeac4 --- /dev/null +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/activity/KeyboardShortcutPreferenceCompatActivity.kt @@ -0,0 +1,125 @@ +/* + * Twidere - Twitter client for Android + * + * Copyright (C) 2012-2015 Mariotaku Lee + * + * 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 . + */ + +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" + } +} diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/AbsToolbarTabPagesFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/AbsToolbarTabPagesFragment.kt index 2c138f5d9..3c30f39e8 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/AbsToolbarTabPagesFragment.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/AbsToolbarTabPagesFragment.kt @@ -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!!) diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/UserFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/UserFragment.kt index 5c63a5db7..471670db1 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/UserFragment.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/UserFragment.kt @@ -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) diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/view/ComposeEditText.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/view/ComposeEditText.kt index e3a080104..aafcd05a9 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/view/ComposeEditText.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/view/ComposeEditText.kt @@ -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 diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/view/TimelineContentTextView.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/view/TimelineContentTextView.kt index b902379a5..cd270c38c 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/view/TimelineContentTextView.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/view/TimelineContentTextView.kt @@ -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) } diff --git a/twidere/src/main/res/layout/activity_home_content.xml b/twidere/src/main/res/layout/activity_home_content.xml index db17a67bd..d99a29d59 100644 --- a/twidere/src/main/res/layout/activity_home_content.xml +++ b/twidere/src/main/res/layout/activity_home_content.xml @@ -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"/> diff --git a/twidere/src/main/res/layout/activity_keyboard_shortcut_input.xml b/twidere/src/main/res/layout/activity_keyboard_shortcut_input.xml index 2f481cf32..dd38096cb 100644 --- a/twidere/src/main/res/layout/activity_keyboard_shortcut_input.xml +++ b/twidere/src/main/res/layout/activity_keyboard_shortcut_input.xml @@ -19,13 +19,13 @@ ~ along with this program. If not, see . --> + 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"> + android:textAppearance="?android:textAppearanceMedium"/> + android:visibility="gone"/> @@ -61,7 +61,7 @@ tools:ignore="UselessParent">