init theming stuff
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
package ru.vtosters.lite.themes;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ru.vtosters.lite.utils.Themes;
|
||||
|
||||
public class VTLResources extends Resources {
|
||||
private static final String TAG = "VTLResources";
|
||||
private final Context context;
|
||||
private static Field typedArrayField;
|
||||
|
||||
private static final List<Integer> attributesToTheme = new ArrayList<>();
|
||||
|
||||
static {
|
||||
try {
|
||||
typedArrayField = TypedArray.class.getDeclaredField("mData");
|
||||
typedArrayField.setAccessible(true);
|
||||
} catch (NoSuchFieldException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
attributesToTheme.add(16843041);
|
||||
attributesToTheme.add(android.R.attr.backgroundTint);
|
||||
attributesToTheme.add(16843173);
|
||||
}
|
||||
|
||||
public VTLResources(Context context, Resources parent) {
|
||||
super(parent.getAssets(), parent.getDisplayMetrics(), parent.getConfiguration());
|
||||
this.context = context;
|
||||
Log.d(TAG, "VTLResources: init");
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypedArray obtainAttributes(AttributeSet set, int[] attrs) {
|
||||
var typedArray = super.obtainAttributes(set, attrs);
|
||||
var data = getArrayData(typedArray);
|
||||
for (int i = 0; i < attrs.length; i++) {
|
||||
try {
|
||||
int attrID = attrs[i];
|
||||
try {
|
||||
} catch (Exception ignored) {}
|
||||
if (attributesToTheme.contains(attrID)) {
|
||||
int type = data[i*6];
|
||||
int cnt = data[(i*6)+1];
|
||||
|
||||
if (type == TypedValue.TYPE_ATTRIBUTE && isAttrThemeable(cnt)) {
|
||||
data[i*6] = TypedValue.TYPE_INT_COLOR_RGB8;
|
||||
data[(i*6)+1] = Themes.getAccentColor();
|
||||
data[(i*6)+2] = 0; // clear reference content
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "TAVzlom failed! (obtainAttributes)");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return typedArray;
|
||||
}
|
||||
|
||||
private static boolean isAttrThemeable(int attrID) {
|
||||
return !Themes.isDarkTheme() && attrID == com.vtosters.lite.R.attr.button_primary_background;
|
||||
}
|
||||
|
||||
|
||||
private static int[] getArrayData(TypedArray arr) {
|
||||
try {
|
||||
return (int[]) typedArrayField.get(arr);
|
||||
} catch (IllegalAccessException e) {
|
||||
Log.e(TAG, "getArrayData: ", e);
|
||||
e.printStackTrace();
|
||||
return new int[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package ru.vtosters.lite.themes;
|
||||
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import ru.vtosters.lite.themes.hooks.*;
|
||||
|
||||
public class ViewInjector {
|
||||
public static ArrayList<BaseHook> hooks = new ArrayList<>();
|
||||
|
||||
static {
|
||||
hooks.add(new ButtonHook());
|
||||
hooks.add(new ImageViewHook());
|
||||
hooks.add(new TextViewHook());
|
||||
hooks.add(new ToolbarHook());
|
||||
hooks.add(new VKImageViewHook());
|
||||
hooks.add(new WaveformViewHook());
|
||||
}
|
||||
|
||||
public static View inject(View view, int i, boolean z) {
|
||||
for (BaseHook hook : hooks) {
|
||||
hook.inject(view, i, z);
|
||||
}
|
||||
if (view instanceof ViewGroup) {
|
||||
var viewGroup = (ViewGroup) view;
|
||||
for (int i2 = 0; i2 < viewGroup.getChildCount(); i2++) {
|
||||
inject(viewGroup.getChildAt(i2), i, false);
|
||||
}
|
||||
}
|
||||
|
||||
return view;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package ru.vtosters.lite.themes.hooks;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
public interface BaseHook {
|
||||
void inject(View view, int i, boolean z);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package ru.vtosters.lite.themes.hooks;
|
||||
|
||||
import android.content.res.ColorStateList;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
|
||||
import ru.vtosters.lite.utils.Themes;
|
||||
|
||||
public class ButtonHook implements BaseHook {
|
||||
@Override
|
||||
public void inject(View view, int i, boolean z) {
|
||||
if (view instanceof Button) {
|
||||
var button = (Button) view;
|
||||
if (button.getBackgroundTintList() != null && Themes.isAccentedColor(button.getBackgroundTintList())) {
|
||||
button.setBackgroundTintList(ColorStateList.valueOf(Themes.getAccentColor()));
|
||||
}
|
||||
if (TextViewHook.isPositiveButton(button) || TextViewHook.isVkUiButton(view)) {
|
||||
button.setBackgroundTintList(ColorStateList.valueOf(Themes.getAccentColor()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package ru.vtosters.lite.themes.hooks;
|
||||
|
||||
import android.content.res.ColorStateList;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.appcompat.widget.AppCompatImageView;
|
||||
|
||||
import ru.vtosters.lite.utils.Themes;
|
||||
|
||||
public class ImageViewHook implements BaseHook {
|
||||
@Override
|
||||
public void inject(View view, int i, boolean z) {
|
||||
if (view instanceof AppCompatImageView) {
|
||||
var imageView = (AppCompatImageView) view;
|
||||
if (imageView.getImageTintList() != null) {
|
||||
imageView.setImageTintList(Themes.themeCSL(imageView.getImageTintList()));
|
||||
}
|
||||
}
|
||||
|
||||
if (view instanceof ImageView) {
|
||||
var imageView = (ImageView) view;
|
||||
if (imageView.getImageTintList() != null) {
|
||||
imageView.setImageTintList(Themes.themeCSL(imageView.getImageTintList()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package ru.vtosters.lite.themes.hooks;
|
||||
|
||||
import android.content.res.ColorStateList;
|
||||
import android.view.View;
|
||||
|
||||
import com.google.android.material.tabs.TabLayout;
|
||||
|
||||
import ru.vtosters.lite.utils.Themes;
|
||||
|
||||
public class TabLayoutHook implements BaseHook {
|
||||
@Override
|
||||
public void inject(View view, int i, boolean z) {
|
||||
if (view instanceof TabLayout) {
|
||||
var tabLayout = (TabLayout) view;
|
||||
tabLayout.setSelectedTabIndicatorColor(Themes.getAccentColor());
|
||||
tabLayout.setTabRippleColor(ColorStateList.valueOf(Themes.getAccentColor()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
package ru.vtosters.lite.themes.hooks;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.appcompat.widget.AppCompatCheckedTextView;
|
||||
|
||||
import com.vk.core.view.TintTextView;
|
||||
import com.vtosters.lite.R;
|
||||
|
||||
import ru.vtosters.lite.themes.utils.VkUiThemer;
|
||||
import ru.vtosters.lite.utils.Themes;
|
||||
|
||||
public class TextViewHook implements BaseHook {
|
||||
|
||||
private static final String TAG = "TextViewHook";
|
||||
|
||||
@SuppressLint("RestrictedApi")
|
||||
@Override
|
||||
public void inject(View view, int i, boolean z) {
|
||||
if (view instanceof TextView) {
|
||||
var textView = (TextView) view;
|
||||
|
||||
if (Themes.isAccentedColor(textView.getCurrentTextColor())) {
|
||||
textView.setTextColor(Themes.getAccentColor());
|
||||
}
|
||||
|
||||
if (isPositiveButton(textView) || isVkUiButton(textView)) {
|
||||
view.setBackgroundTintList(ColorStateList.valueOf(Themes.getAccentColor()));
|
||||
}
|
||||
|
||||
if (view instanceof TintTextView) {
|
||||
if (eligibleForStartTinting(view.getId())) {
|
||||
((TintTextView) view).setDrawableStartTint(Themes.getAccentColor());
|
||||
} else if (eligibleForTinting(view.getId())) {
|
||||
((TintTextView) view).setDrawableTint(Themes.getAccentColor());
|
||||
}
|
||||
}
|
||||
|
||||
if (view instanceof AppCompatCheckedTextView) {
|
||||
var check = (AppCompatCheckedTextView) view;
|
||||
if (Themes.isAccentedColor(check.getCheckMarkTintList())) {
|
||||
check.setCheckMarkTintList(ColorStateList.valueOf(Themes.getAccentColor()));
|
||||
}
|
||||
}
|
||||
|
||||
VkUiThemer.autoThemeVkuiButtons(textView);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean eligibleForStartTinting(int id) {
|
||||
return !Themes.isDarkTheme() && id == com.vtosters.lite.R.id.post_retweet_name;
|
||||
}
|
||||
|
||||
public static boolean eligibleForTinting(int id) {
|
||||
return !Themes.isDarkTheme() && id == com.vtosters.lite.R.id.attach_title;
|
||||
}
|
||||
|
||||
public static boolean isPositiveButton(View view) {
|
||||
return !Themes.isDarkTheme() && (view.getId() == R.id.positive || view.getId() == R.id.positive_button);
|
||||
}
|
||||
|
||||
public static boolean isVkUiButton(View view) {
|
||||
return !Themes.isDarkTheme() && (
|
||||
view.getId() == R.id.empty_button
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package ru.vtosters.lite.themes.hooks;
|
||||
|
||||
import android.content.res.ColorStateList;
|
||||
import android.view.View;
|
||||
import android.widget.Toolbar;
|
||||
|
||||
import ru.vtosters.lite.utils.Themes;
|
||||
|
||||
public class ToolbarHook implements BaseHook {
|
||||
@Override
|
||||
public void inject(View view, int i, boolean z) {
|
||||
if (view instanceof Toolbar) {
|
||||
var toolbar = (Toolbar) view;
|
||||
if (!Themes.isDarkTheme()) {
|
||||
toolbar.setBackgroundColor(Themes.getAccentColor());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package ru.vtosters.lite.themes.hooks;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import com.vk.imageloader.view.VKImageView;
|
||||
|
||||
import ru.vtosters.lite.utils.Themes;
|
||||
|
||||
public class VKImageViewHook implements BaseHook {
|
||||
@Override
|
||||
public void inject(View view, int i, boolean z) {
|
||||
if (view instanceof VKImageView) {
|
||||
var imageView = (VKImageView) view;
|
||||
if (imageView.getImageTintList() != null) {
|
||||
imageView.setImageTintList(Themes.themeCSL(imageView.getImageTintList()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package ru.vtosters.lite.themes.hooks;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
|
||||
import com.vtosters.lite.ui.widget.WaveformView;
|
||||
|
||||
import ru.vtosters.lite.utils.Themes;
|
||||
|
||||
public class WaveformViewHook implements BaseHook {
|
||||
@Override
|
||||
public void inject(View view, int i, boolean z) {
|
||||
if (view instanceof WaveformView) {
|
||||
var waveformView = (WaveformView) view;
|
||||
//waveformView.set(Themes.getAccentColor());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
package ru.vtosters.lite.themes.utils;
|
||||
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.DrawableContainer;
|
||||
import android.graphics.drawable.GradientDrawable;
|
||||
import android.graphics.drawable.RippleDrawable;
|
||||
import android.graphics.drawable.StateListDrawable;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.vk.core.ui.themes.VKTheme;
|
||||
|
||||
import ru.vtosters.lite.utils.Themes;
|
||||
|
||||
public class VkUiThemer {
|
||||
public static void autoThemeVkuiButtons(TextView view) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) return;
|
||||
if (Themes.isDarkTheme())
|
||||
return; // no need to theme b/w buttons, well it should skip, but I do it for extra performance
|
||||
|
||||
try {
|
||||
if (!(view.getBackground() instanceof StateListDrawable)) return;
|
||||
|
||||
StateListDrawable states = ((StateListDrawable) view.getBackground());
|
||||
if (states.getConstantState() == null) return;
|
||||
DrawableContainer.DrawableContainerState dStates = (DrawableContainer.DrawableContainerState) states.getConstantState();
|
||||
|
||||
if (dStates.getChildCount() == 2) {
|
||||
Drawable target = dStates.getChildren()[0];
|
||||
if (!(target instanceof RippleDrawable)) return;
|
||||
|
||||
RippleDrawable ripple = (RippleDrawable) target;
|
||||
for (int i = 0; i < ripple.getNumberOfLayers(); i++) {
|
||||
if (ripple.getId(i) == android.R.id.mask) continue;
|
||||
|
||||
Drawable currentTarget = ripple.getDrawable(i);
|
||||
|
||||
if (!(currentTarget instanceof GradientDrawable)) continue;
|
||||
|
||||
//if (TSColorRefs.TS_DEBUG_MODE) Log.d("TSCore", "[tsh] finally, obamium = ["+currentTarget.toString()+"]");
|
||||
|
||||
GradientDrawable spd = (GradientDrawable) currentTarget;
|
||||
if (spd.getShape() != GradientDrawable.RECTANGLE) continue;
|
||||
|
||||
Log.d("TSCore", "[tsh] got a color!");
|
||||
|
||||
ColorStateList csl = spd.getColor();
|
||||
if (csl != null) {
|
||||
Log.d("TSCore", "[tsh] csl2 <def: " + csl.getDefaultColor() + " = " + Themes.hex(csl.getDefaultColor()) + "> | " + csl);
|
||||
}
|
||||
|
||||
if (Themes.isAccentedColor(spd.getColor())) {
|
||||
Log.d("TSCore", "[tsh] theming CSL");
|
||||
spd.setTint(Themes.getAccentColor());
|
||||
}
|
||||
}
|
||||
|
||||
Drawable draw2 = dStates.getChildren()[0];
|
||||
if (!(draw2 instanceof GradientDrawable)) return;
|
||||
GradientDrawable spd = (GradientDrawable) draw2;
|
||||
if (spd.getShape() != GradientDrawable.RECTANGLE) return;
|
||||
|
||||
Log.d("TSCore", "[tsh:disabled] got a color!");
|
||||
|
||||
ColorStateList csl = spd.getColor();
|
||||
if (csl != null) {
|
||||
Log.d("TSCore", "[tsh:disabled] csl2 <def: " + csl.getDefaultColor() + " = " + Themes.hex(csl.getDefaultColor()) + "> | " + csl);
|
||||
}
|
||||
|
||||
if (Themes.isAccentedColor(spd.getColor())) {
|
||||
Log.d("TSCore", "[tsh:disabled] theming CSL");
|
||||
spd.setTint(Themes.getAccentColor());
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// This must go wrong, so we ignore any exceptions that may occur
|
||||
e.printStackTrace(); // REMOVE THIS!
|
||||
}
|
||||
}
|
||||
|
||||
public static void fixDropdown(Drawable drawable) {
|
||||
if (Themes.getCurrentTheme() == VKTheme.VKAPP_MILK_LIGHT) {
|
||||
var stateListDrawable = (StateListDrawable) drawable;
|
||||
((DrawableContainer.DrawableContainerState) stateListDrawable.getConstantState()).getChildren()[0].setTint(Themes.getAccentColor());
|
||||
((DrawableContainer.DrawableContainerState) stateListDrawable.getConstantState()).getChildren()[1].setTint(Themes.halfAlpha(Themes.getAccentColor()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -47,6 +47,7 @@ import java.util.List;
|
||||
|
||||
public class Themes{
|
||||
public static List<String> accentColors = Arrays.asList(
|
||||
"3f8ae0",
|
||||
"5692d7", "528bcc", "7aa0cc", "518bcc", "6296d0", "2f68aa", "638ebf", "5181b8", "71aaeb", "4774a8", "5baaf4", "4186c8", "add3ff", "4774a8", "718198", "5a9eff", "99a2ad", "74a2d6", "e9eef3", "dfe3e7", "eff1f3", "5c9ce6", "4986cc", "4680c2"
|
||||
);
|
||||
|
||||
@@ -77,7 +78,8 @@ public class Themes{
|
||||
}
|
||||
|
||||
public static int getAccentColor(){
|
||||
return getColorFromAttr(getAttrId("accent"));
|
||||
return getResources().getColor(R.color.red);
|
||||
//return getColorFromAttr(getAttrId("accent"));
|
||||
} // Color accent
|
||||
|
||||
public static int getTextAttr(){
|
||||
@@ -221,14 +223,12 @@ public class Themes{
|
||||
public static ColorStateList recolorCSL(ColorStateList colorStateList){
|
||||
if (colorStateList == null) return null;
|
||||
|
||||
if (!isAndroidMonet()) return colorStateList;
|
||||
|
||||
return ColorStateList.valueOf(getAccentColor());
|
||||
} // Recolor ColorStateList to accent color
|
||||
|
||||
@SuppressLint("UseCompatLoadingForColorStateLists")
|
||||
public static ColorStateList themeCSL(Context context, int color){
|
||||
if (isColorRefAccented(color) && isAndroidMonet()) {
|
||||
if (isColorRefAccented(color)) {
|
||||
return ColorStateList.valueOf(getAccentColor());
|
||||
}
|
||||
|
||||
@@ -262,6 +262,26 @@ public class Themes{
|
||||
}
|
||||
} // Recolor ColorStateList
|
||||
|
||||
public static ColorStateList themeCSL(ColorStateList csl){
|
||||
try {
|
||||
int unsel = csl.getColorForState(new int[] {-android.R.attr.state_selected}, Color.BLACK);
|
||||
int sel = csl.getColorForState(new int[] {android.R.attr.state_selected}, Color.BLACK);
|
||||
|
||||
boolean isUnselAccent = isAccentedColor(unsel);
|
||||
boolean isSelAccent = isAccentedColor(sel);
|
||||
|
||||
if (isUnselAccent || isSelAccent) {
|
||||
return new ColorStateList(new int[][] {
|
||||
new int[] {android.R.attr.state_selected}, new int[] {-android.R.attr.state_selected}
|
||||
}, new int[] {isSelAccent ? getAccentColor() : sel, isUnselAccent ? getAccentColor() : unsel});
|
||||
}
|
||||
return csl;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static int darken(int color, float by){
|
||||
float[] hsl = new float[3];
|
||||
ColorUtils.colorToHSL(color, hsl);
|
||||
@@ -293,11 +313,11 @@ public class Themes{
|
||||
}
|
||||
|
||||
public static boolean isAccentedColor(int target){
|
||||
return accentColors.contains(hex(target).toLowerCase());
|
||||
return accentColors.contains(hex2(target).toLowerCase());
|
||||
}
|
||||
|
||||
public static int getColor(Context context, int i){
|
||||
if (isColorRefAccented(i) && isAndroidMonet()) {
|
||||
if (isColorRefAccented(i)) {
|
||||
return getAccentColor();
|
||||
}
|
||||
return context.getColor(i);
|
||||
@@ -357,6 +377,10 @@ public class Themes{
|
||||
return String.format("#%06X", Integer.valueOf(i & 16777215));
|
||||
} // Get color as hex string
|
||||
|
||||
public static String hex2(int i){
|
||||
return String.format("%06X", Integer.valueOf(i & 16777215));
|
||||
} // Get color as hex without #
|
||||
|
||||
public static int getNavigationHeight(int Default){
|
||||
int VKME = R.dimen.design_bottom_sheet_peek_height_min;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user