From 47f4c5c911a59f0639b9c75392bcee34f6ef98a2 Mon Sep 17 00:00:00 2001 From: aliveoutside <53598473+aliveoutside@users.noreply.github.com> Date: Tue, 22 Nov 2022 16:58:09 +0300 Subject: [PATCH] Init --- .../vtosters/lite/themes/ColorReferences.java | 43 ++++++ .../ru/vtosters/lite/themes/ThemesCore.java | 143 ++++++++++++++++++ .../ru/vtosters/lite/themes/ThemesHacks.java | 61 ++++++++ .../ru/vtosters/lite/themes/VTLResources.java | 92 +++++++++++ .../ru/vtosters/lite/themes/ViewInjector.java | 45 ++++++ .../vtosters/lite/themes/hooks/BaseHook.java | 7 + .../lite/themes/hooks/ButtonHook.java | 24 +++ .../lite/themes/hooks/ImageViewHook.java | 24 +++ .../lite/themes/hooks/ProgressWheelHook.java | 21 +++ .../lite/themes/hooks/TabLayoutHook.java | 21 +++ .../lite/themes/hooks/TextViewHook.java | 92 +++++++++++ .../lite/themes/hooks/ToolbarHook.java | 14 ++ .../lite/themes/hooks/VKImageViewHook.java | 29 ++++ .../lite/themes/hooks/WaveformViewHook.java | 16 ++ .../lite/themes/utils/VkUiThemer.java | 81 ++++++++++ .../ru/vtosters/lite/utils/ThemesUtils.java | 25 ++- .../androidx/core/content/ContextCompat.smali | 7 + .../com/vk/core/ui/VKLayoutInflater.smali | 64 ++++++++ .../vk/core/ui/themes/VKThemeHelper$b.smali | 28 ++++ .../com/vk/core/util/ContextExtKt.smali | 13 ++ 20 files changed, 834 insertions(+), 16 deletions(-) create mode 100644 app/src/main/java/ru/vtosters/lite/themes/ColorReferences.java create mode 100644 app/src/main/java/ru/vtosters/lite/themes/ThemesCore.java create mode 100644 app/src/main/java/ru/vtosters/lite/themes/ThemesHacks.java create mode 100644 app/src/main/java/ru/vtosters/lite/themes/VTLResources.java create mode 100644 app/src/main/java/ru/vtosters/lite/themes/ViewInjector.java create mode 100644 app/src/main/java/ru/vtosters/lite/themes/hooks/BaseHook.java create mode 100644 app/src/main/java/ru/vtosters/lite/themes/hooks/ButtonHook.java create mode 100644 app/src/main/java/ru/vtosters/lite/themes/hooks/ImageViewHook.java create mode 100644 app/src/main/java/ru/vtosters/lite/themes/hooks/ProgressWheelHook.java create mode 100644 app/src/main/java/ru/vtosters/lite/themes/hooks/TabLayoutHook.java create mode 100644 app/src/main/java/ru/vtosters/lite/themes/hooks/TextViewHook.java create mode 100644 app/src/main/java/ru/vtosters/lite/themes/hooks/ToolbarHook.java create mode 100644 app/src/main/java/ru/vtosters/lite/themes/hooks/VKImageViewHook.java create mode 100644 app/src/main/java/ru/vtosters/lite/themes/hooks/WaveformViewHook.java create mode 100644 app/src/main/java/ru/vtosters/lite/themes/utils/VkUiThemer.java diff --git a/app/src/main/java/ru/vtosters/lite/themes/ColorReferences.java b/app/src/main/java/ru/vtosters/lite/themes/ColorReferences.java new file mode 100644 index 000000000..1217785b3 --- /dev/null +++ b/app/src/main/java/ru/vtosters/lite/themes/ColorReferences.java @@ -0,0 +1,43 @@ +package ru.vtosters.lite.themes; + +import android.content.res.ColorStateList; +import android.graphics.Color; + +import java.util.Arrays; +import java.util.List; + +import ru.vtosters.lite.utils.ThemesUtils; + +public class ColorReferences { + public static List accentColors = Arrays.asList( + "5692d7", "528bcc", "7aa0cc", "518bcc", "6296d0", "2f68aa", "638ebf", "5181b8", "71aaeb", + "4774a8", "5baaf4", "4186c8", "add3ff", "4774a8", "718198", "5a9eff", "99a2ad", "74a2d6", + "e9eef3", "dfe3e7", "eff1f3", "5c9ce6", "4986cc", "4680c2", "3f8ae0" + ); + + public static boolean isAccentedColor(ColorStateList target) { + return target != null && isAccentedColor(target.getDefaultColor()); + } + + public static boolean isAccentedColor(int target) { + return accentColors.contains(ThemesUtils.hexx(target).toLowerCase()); + } + + public static boolean isColorRefAccented(int target) { + return target == com.vtosters.lite.R.color.accent_blue || // accent_blue + target == com.vtosters.lite.R.color.blue_gray || // light_blue_gray + target == com.vtosters.lite.R.color.azure_300 || + target == com.vtosters.lite.R.color.light_blue_old || // light_blue + target == com.vtosters.lite.R.color.blue_200 || // blue_200 + target == com.vtosters.lite.R.color.vk_blue_400 || // blue_200 + target == com.vtosters.lite.R.color.blue_200_muted; // blue_200_muted + } + + public static boolean isCslNeedToBeThemed(ColorStateList csl) { + 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); + return ThemesCore.isCachedAccents() && (isUnselAccent || isSelAccent); + } +} diff --git a/app/src/main/java/ru/vtosters/lite/themes/ThemesCore.java b/app/src/main/java/ru/vtosters/lite/themes/ThemesCore.java new file mode 100644 index 000000000..5e71bbdd8 --- /dev/null +++ b/app/src/main/java/ru/vtosters/lite/themes/ThemesCore.java @@ -0,0 +1,143 @@ +package ru.vtosters.lite.themes; + +import android.content.Context; +import android.util.Log; +import android.util.SparseBooleanArray; +import android.util.SparseIntArray; + +import androidx.annotation.IdRes; + +import ru.vtosters.lite.utils.ThemesUtils; + + +public class ThemesCore { + private static final String TAG = "ThemesCore"; + + public static SparseIntArray themedColors = new SparseIntArray(); + public static SparseBooleanArray ACCENT_THEME_ONLY_LIGHT = new SparseBooleanArray(); + public static SparseBooleanArray ACCENT_THEME_ONLY_NOMILK_LIGHT = new SparseBooleanArray(); + public static SparseBooleanArray ACCENT_THEME_ONLY_NOMILK = new SparseBooleanArray(); + public static SparseBooleanArray ACCENT_THEME_ONLY_MILK_LIGHT = new SparseBooleanArray(); + public static SparseBooleanArray ACCENT_THEME_ONLY_MILK_DARK = new SparseBooleanArray(); + + private static boolean cachedAccents = false; + + private static int wallDarkColor; + private static int wallLightColor; + + public static void setExceptions() { + ACCENT_THEME_ONLY_LIGHT.clear(); + ACCENT_THEME_ONLY_NOMILK_LIGHT.clear(); + ACCENT_THEME_ONLY_NOMILK.clear(); + ACCENT_THEME_ONLY_MILK_LIGHT.clear(); + ACCENT_THEME_ONLY_MILK_DARK.clear(); + + ACCENT_THEME_ONLY_LIGHT.put(com.vtosters.lite.R.attr.button_primary_background, false); + ACCENT_THEME_ONLY_LIGHT.put(com.vtosters.lite.R.attr.button_secondary_foreground, false); + ACCENT_THEME_ONLY_LIGHT.put(com.vtosters.lite.R.attr.button_tertiary_foreground, false); + ACCENT_THEME_ONLY_LIGHT.put(com.vtosters.lite.R.attr.button_muted_foreground, false); + ACCENT_THEME_ONLY_LIGHT.put(com.vtosters.lite.R.attr.button_outline_border, false); + ACCENT_THEME_ONLY_LIGHT.put(com.vtosters.lite.R.attr.text_name, false); + ACCENT_THEME_ONLY_LIGHT.put(com.vtosters.lite.R.attr.newsfeed_post_title_color, false); + ACCENT_THEME_ONLY_LIGHT.put(com.vtosters.lite.R.attr.attach_picker_tab_active_background, false); + ACCENT_THEME_ONLY_LIGHT.put(com.vtosters.lite.R.attr.attach_picker_tab_active_text, false); + ACCENT_THEME_ONLY_LIGHT.put(com.vtosters.lite.R.attr.newsfeed_action_color, false); + ACCENT_THEME_ONLY_LIGHT.put(com.vtosters.lite.R.attr.counter_primary_background, false); + ACCENT_THEME_ONLY_LIGHT.put(com.vtosters.lite.R.attr.action_sheet_action_foreground, false); + + ACCENT_THEME_ONLY_NOMILK_LIGHT.put(com.vtosters.lite.R.attr.header_background, false); + + ACCENT_THEME_ONLY_MILK_LIGHT.put(com.vtosters.lite.R.attr.header_tint, false); + ACCENT_THEME_ONLY_MILK_LIGHT.put(com.vtosters.lite.R.attr.header_tint_alternate, false); + ACCENT_THEME_ONLY_MILK_LIGHT.put(com.vtosters.lite.R.attr.toolbarIconsColor, false); + ACCENT_THEME_ONLY_MILK_LIGHT.put(com.vtosters.lite.R.attr.header_tab_active_indicator, false); + ACCENT_THEME_ONLY_MILK_LIGHT.put(com.vtosters.lite.R.attr.im_dropdown_arrow_tint, false); + + ACCENT_THEME_ONLY_NOMILK.put(com.vtosters.lite.R.attr.im_dropdown_icon_color, false); + } + + public static void setThemedColors(int accentColor) { + cachedAccents = true; + themedColors.clear(); + + themedColors.put(com.vtosters.lite.R.attr.text_link, accentColor); + themedColors.put(com.vtosters.lite.R.attr.accent, accentColor); + themedColors.put(com.vtosters.lite.R.attr.newsfeed_dropdown_color, accentColor); + + themedColors.put(com.vtosters.lite.R.attr.vk_accent, accentColor); + themedColors.put(com.vtosters.lite.R.attr.accentColor, accentColor); + themedColors.put(com.vtosters.lite.R.attr.text_name, accentColor); + themedColors.put(com.vtosters.lite.R.attr.tabbar_active_icon, accentColor); + //themedColors.put(com.vtosters.lite.R.attr.header_background, accentColor); + themedColors.put(com.vtosters.lite.R.attr.header_tint, accentColor); + themedColors.put(com.vtosters.lite.R.attr.header_tint_alternate, accentColor); + themedColors.put(com.vtosters.lite.R.attr.activity_indicator_tint, accentColor); + themedColors.put(com.vtosters.lite.R.attr.toolbarIconsColor, accentColor); + + themedColors.put(com.vtosters.lite.R.attr.button_primary_background, accentColor); + themedColors.put(com.vtosters.lite.R.attr.button_secondary_foreground, accentColor); + themedColors.put(com.vtosters.lite.R.attr.button_tertiary_foreground, accentColor); + themedColors.put(com.vtosters.lite.R.attr.button_outline_border, accentColor); + themedColors.put(com.vtosters.lite.R.attr.button_muted_foreground, accentColor); + themedColors.put(com.vtosters.lite.R.attr.header_tab_active_indicator, accentColor); + + themedColors.put(com.vtosters.lite.R.attr.im_dropdown_icon_color, accentColor); + themedColors.put(com.vtosters.lite.R.attr.im_dropdown_arrow_tint, accentColor); + + themedColors.put(com.vtosters.lite.R.attr.im_attach_tint, accentColor); + themedColors.put(com.vtosters.lite.R.attr.im_forward_line_tint, accentColor); + themedColors.put(com.vtosters.lite.R.attr.link_alternate, accentColor); + themedColors.put(com.vtosters.lite.R.attr.newsfeed_post_title_color, accentColor); + + themedColors.put(com.vtosters.lite.R.attr.attach_picker_tab_active_background, accentColor); + themedColors.put(com.vtosters.lite.R.attr.attach_picker_tab_active_text, accentColor); + themedColors.put(com.vtosters.lite.R.attr.newsfeed_action_color, accentColor); + themedColors.put(com.vtosters.lite.R.attr.counter_primary_background, accentColor); + + themedColors.put(com.vtosters.lite.R.attr.action_sheet_action_foreground, accentColor); + + // messages + wallDarkColor = ThemesUtils.darken(accentColor, 0.25f); + wallLightColor = ThemesUtils.lighten(accentColor, 0.25f); + + //themedColors.put(com.vtosters.lite.R.attr.im_ic_send_msg, accentColor); + themedColors.put(com.vtosters.lite.R.attr.im_bubble_wallpaper_outgoing, accentColor); + themedColors.put(com.vtosters.lite.R.attr.im_bubble_outgoing, accentColor); + themedColors.put(com.vtosters.lite.R.attr.im_bubble_outgoing_highlighted, accentColor); + } + + public static int getThemedAttr(Context context, @IdRes int attr) { + if (attr == com.vtosters.lite.R.attr.im_bubble_wallpaper_outgoing || attr == com.vtosters.lite.R.attr.im_bubble_outgoing) { + return (!ThemesUtils.isDarkTheme()) ? wallLightColor : wallDarkColor; + } + + if (attr == com.vtosters.lite.R.attr.im_bubble_outgoing_highlighted) { + return (!ThemesUtils.isDarkTheme()) ? ThemesUtils.lighten(wallLightColor, 0.2f) : ThemesUtils.darken(wallDarkColor, 0.2f); + } + + return themedColors.get(attr); + } + + public static boolean hasThemedAttr(Context context, int attrID) { + try { + Log.d(TAG, "Requesting color by attr " + context.getResources().getResourceName(attrID)); + } catch (Exception e) { + } + + if (isCachedAccents()) { + if (!ThemesUtils.isDarkTheme()) { + return (themedColors.get(attrID) != 0 + && ACCENT_THEME_ONLY_MILK_DARK.get(attrID, true) + && ACCENT_THEME_ONLY_NOMILK_LIGHT.get(attrID, true)); + } else { + return (themedColors.get(attrID) != 0 + && ACCENT_THEME_ONLY_MILK_LIGHT.get(attrID, true) + && ACCENT_THEME_ONLY_NOMILK_LIGHT.get(attrID, true)); + } + } else return themedColors.get(attrID) != 0; + } + + public static boolean isCachedAccents() { + return cachedAccents; + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/vtosters/lite/themes/ThemesHacks.java b/app/src/main/java/ru/vtosters/lite/themes/ThemesHacks.java new file mode 100644 index 000000000..1337101a1 --- /dev/null +++ b/app/src/main/java/ru/vtosters/lite/themes/ThemesHacks.java @@ -0,0 +1,61 @@ +package ru.vtosters.lite.themes; + +import android.content.Context; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.DrawableContainer; +import android.graphics.drawable.InsetDrawable; +import android.graphics.drawable.LayerDrawable; +import android.graphics.drawable.StateListDrawable; +import android.os.Build; +import android.util.Log; +import android.view.View; +import android.widget.TextView; + +import androidx.annotation.ColorRes; +import androidx.annotation.NonNull; + +import com.vtosters.lite.R; + +import ru.vtosters.lite.utils.AndroidUtils; +import ru.vtosters.lite.utils.ThemesUtils; + +public class ThemesHacks { + // Фиксит селектор (все/свои/архив) в профиле + public static void fixProfileSelector(View view) { + if (!ThemesUtils.isMilkshake()) return; + var id = view.getId(); + + if (id == R.id.profile_wall_owner_posts || id == R.id.profile_wall_all_posts || id == R.id.profile_wall_archived_posts) { + StateListDrawable states = (StateListDrawable) ((LayerDrawable) view.getBackground()).getDrawable(0); + LayerDrawable selected = (LayerDrawable) ((DrawableContainer.DrawableContainerState) states.getConstantState()).getChildren()[0]; + ((ColorDrawable) selected.getDrawable(0)).setColor(ThemesUtils.getAccentColor()); + } + } + + public static void fixThemedFeed(TextView textView, int i) { + if (i != R.layout.themed_feed_tab_2) { + return; + } + textView.setTextColor(ThemesUtils.recolorCSL(textView.getTextColors())); + ((InsetDrawable) ((DrawableContainer.DrawableContainerState) textView.getBackground().getConstantState()).getChildren()[0]).getDrawable().setTint(ThemesUtils.lighten(ThemesUtils.getAccentColor(), 0.15f)); + } + + public static void fixDropdown(Drawable drawable) { + if (!ThemesUtils.isDarkTheme() && ThemesUtils.isMilkshake() && ThemesCore.isCachedAccents()) { + StateListDrawable stateListDrawable = (StateListDrawable) drawable; + ((DrawableContainer.DrawableContainerState) stateListDrawable.getConstantState()).getChildren()[0].setTint(ThemesUtils.getAccentColor()); + ((DrawableContainer.DrawableContainerState) stateListDrawable.getConstantState()).getChildren()[1].setTint(ThemesUtils.halfAlpha(ThemesUtils.getAccentColor())); + } + } + + public static int getHackedColor(@NonNull Context context, @ColorRes int color) { + if (ThemesCore.isCachedAccents() && ColorReferences.isColorRefAccented(color)) { + Log.d("ThemesHacks", "getHackedColor: got accent color: " + AndroidUtils.getResources().getResourceName(color)); + return ThemesUtils.getAccentColor(); + } + if (Build.VERSION.SDK_INT > 23) { + return context.getColor(color); + } else return context.getResources().getColor(color); + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/vtosters/lite/themes/VTLResources.java b/app/src/main/java/ru/vtosters/lite/themes/VTLResources.java new file mode 100644 index 000000000..673a6b52c --- /dev/null +++ b/app/src/main/java/ru/vtosters/lite/themes/VTLResources.java @@ -0,0 +1,92 @@ +package ru.vtosters.lite.themes; + +import android.content.Context; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.util.Log; +import android.util.TypedValue; + +import androidx.annotation.Nullable; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; + +import ru.vtosters.lite.utils.ThemesUtils; + +public class VTLResources extends Resources { + private static final String TAG = "VTLResources"; + private final Context context; + private static Field typedArrayField; + + private static final List 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]; + 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] = ThemesUtils.getAccentColor(); + data[(i*6)+2] = 0; // clear reference content + } + } + } catch (Exception e) { + Log.e(TAG, "TAVzlom failed! (obtainAttributes)"); + e.printStackTrace(); + } + } + return typedArray; + } + + @Override + public Drawable getDrawable(int id, @Nullable Theme theme) throws NotFoundException { + var drawable = super.getDrawable(id, theme); + if (id == com.vtosters.lite.R.drawable.newsfeed_tab_dropdown_16) { + ThemesHacks.fixDropdown(drawable); + } + return drawable; + } + + private static boolean isAttrThemeable(int attrID) { + return !ThemesUtils.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]; + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/vtosters/lite/themes/ViewInjector.java b/app/src/main/java/ru/vtosters/lite/themes/ViewInjector.java new file mode 100644 index 000000000..774f316c4 --- /dev/null +++ b/app/src/main/java/ru/vtosters/lite/themes/ViewInjector.java @@ -0,0 +1,45 @@ +package ru.vtosters.lite.themes; + +import android.view.View; +import android.view.ViewGroup; + +import java.util.ArrayList; + +import ru.vtosters.lite.themes.hooks.BaseHook; +import ru.vtosters.lite.themes.hooks.ButtonHook; +import ru.vtosters.lite.themes.hooks.ImageViewHook; +import ru.vtosters.lite.themes.hooks.ProgressWheelHook; +import ru.vtosters.lite.themes.hooks.TabLayoutHook; +import ru.vtosters.lite.themes.hooks.TextViewHook; +import ru.vtosters.lite.themes.hooks.ToolbarHook; +import ru.vtosters.lite.themes.hooks.VKImageViewHook; +import ru.vtosters.lite.themes.hooks.WaveformViewHook; + +public class ViewInjector { + public static ArrayList hooks = new ArrayList<>(); + + static { + hooks.add(new ButtonHook()); + hooks.add(new ImageViewHook()); + hooks.add(new TabLayoutHook()); + hooks.add(new TextViewHook()); + hooks.add(new ToolbarHook()); + hooks.add(new VKImageViewHook()); + hooks.add(new WaveformViewHook()); + hooks.add(new ProgressWheelHook()); + } + + 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; + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/vtosters/lite/themes/hooks/BaseHook.java b/app/src/main/java/ru/vtosters/lite/themes/hooks/BaseHook.java new file mode 100644 index 000000000..d56a91414 --- /dev/null +++ b/app/src/main/java/ru/vtosters/lite/themes/hooks/BaseHook.java @@ -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); +} diff --git a/app/src/main/java/ru/vtosters/lite/themes/hooks/ButtonHook.java b/app/src/main/java/ru/vtosters/lite/themes/hooks/ButtonHook.java new file mode 100644 index 000000000..c8150c9d8 --- /dev/null +++ b/app/src/main/java/ru/vtosters/lite/themes/hooks/ButtonHook.java @@ -0,0 +1,24 @@ +package ru.vtosters.lite.themes.hooks; + +import android.content.res.ColorStateList; +import android.view.View; +import android.widget.Button; + +import ru.vtosters.lite.themes.ColorReferences; +import ru.vtosters.lite.utils.ThemesUtils; + +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 && ColorReferences.isAccentedColor(button.getBackgroundTintList())) { + button.setBackgroundTintList(ColorStateList.valueOf(ThemesUtils.getAccentColor())); + } + if (TextViewHook.isPositiveButton(button) || TextViewHook.isVkUiButton(view)) { + button.setBackgroundTintList(ColorStateList.valueOf(ThemesUtils.getAccentColor())); + } + } + } +} + diff --git a/app/src/main/java/ru/vtosters/lite/themes/hooks/ImageViewHook.java b/app/src/main/java/ru/vtosters/lite/themes/hooks/ImageViewHook.java new file mode 100644 index 000000000..b9b88ef8a --- /dev/null +++ b/app/src/main/java/ru/vtosters/lite/themes/hooks/ImageViewHook.java @@ -0,0 +1,24 @@ +package ru.vtosters.lite.themes.hooks; + +import android.content.res.ColorStateList; +import android.view.View; +import android.widget.ImageView; + +import ru.vtosters.lite.themes.ColorReferences; +import ru.vtosters.lite.themes.ThemesCore; +import ru.vtosters.lite.utils.ThemesUtils; + +public class ImageViewHook implements BaseHook { + @Override + public void inject(View view, int i, boolean z) { + if (view instanceof ImageView) { + if (ThemesCore.isCachedAccents()) { + var imageView = (ImageView) view; + if (imageView.getImageTintList() == null) return; + if (ColorReferences.isCslNeedToBeThemed(imageView.getImageTintList())) { + imageView.setImageTintList(ColorStateList.valueOf(ThemesUtils.getAccentColor())); + } + } + } + } +} diff --git a/app/src/main/java/ru/vtosters/lite/themes/hooks/ProgressWheelHook.java b/app/src/main/java/ru/vtosters/lite/themes/hooks/ProgressWheelHook.java new file mode 100644 index 000000000..01da24ac2 --- /dev/null +++ b/app/src/main/java/ru/vtosters/lite/themes/hooks/ProgressWheelHook.java @@ -0,0 +1,21 @@ +package ru.vtosters.lite.themes.hooks; + +import android.view.View; + +import com.pnikosis.materialishprogress.ProgressWheel; + +import ru.vtosters.lite.themes.ThemesCore; +import ru.vtosters.lite.utils.ThemesUtils; + +public class ProgressWheelHook implements BaseHook { + @Override + public void inject(View view, int i, boolean z) { + if (view instanceof ProgressWheel) { + if (ThemesCore.isCachedAccents()) { + var progressWheel = (ProgressWheel) view; + progressWheel.setBarColor(ThemesUtils.getAccentColor()); + } + } + } +} + diff --git a/app/src/main/java/ru/vtosters/lite/themes/hooks/TabLayoutHook.java b/app/src/main/java/ru/vtosters/lite/themes/hooks/TabLayoutHook.java new file mode 100644 index 000000000..c85402c26 --- /dev/null +++ b/app/src/main/java/ru/vtosters/lite/themes/hooks/TabLayoutHook.java @@ -0,0 +1,21 @@ +package ru.vtosters.lite.themes.hooks; + +import android.content.res.ColorStateList; +import android.view.View; + +import com.vk.core.view.VKTabLayout; + +import ru.vtosters.lite.utils.ThemesUtils; + + +public class TabLayoutHook implements BaseHook { + @Override + public void inject(View view, int i, boolean z) { + if (view instanceof VKTabLayout) { + var tabLayout = (VKTabLayout) view; + tabLayout.setSelectedTabIndicatorColor(ThemesUtils.getAccentColor()); + tabLayout.setTabRippleColor(ColorStateList.valueOf(ThemesUtils.getAccentColor())); + } + } +} + diff --git a/app/src/main/java/ru/vtosters/lite/themes/hooks/TextViewHook.java b/app/src/main/java/ru/vtosters/lite/themes/hooks/TextViewHook.java new file mode 100644 index 000000000..55d3f50bd --- /dev/null +++ b/app/src/main/java/ru/vtosters/lite/themes/hooks/TextViewHook.java @@ -0,0 +1,92 @@ +package ru.vtosters.lite.themes.hooks; + +import android.annotation.SuppressLint; +import android.content.res.ColorStateList; +import android.os.Build; +import android.view.View; +import android.widget.TextView; + +import androidx.appcompat.widget.AppCompatCheckedTextView; + +import com.vk.core.view.TintTextView; +import com.vk.core.view.links.LinkedTextView; +import com.vtosters.lite.R; + +import ru.vtosters.lite.themes.ColorReferences; +import ru.vtosters.lite.themes.ThemesHacks; +import ru.vtosters.lite.themes.utils.VkUiThemer; +import ru.vtosters.lite.utils.ThemesUtils; + +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 (ColorReferences.isAccentedColor(textView.getCurrentTextColor())) { + textView.setTextColor(ThemesUtils.getAccentColor()); + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && textView.getCompoundDrawableTintList() != null) { + if (ColorReferences.isAccentedColor(textView.getCompoundDrawableTintList())) { + textView.setCompoundDrawableTintList(ColorStateList.valueOf(ThemesUtils.getAccentColor())); + } + } + + if (textView.getLinkTextColors() != null && ColorReferences.isAccentedColor(textView.getLinkTextColors())) { + textView.setLinkTextColor(ThemesUtils.getAccentColor()); + } + + if (isPositiveButton(textView) || isVkUiButton(textView)) { + view.setBackgroundTintList(ColorStateList.valueOf(ThemesUtils.getAccentColor())); + } + + if (view instanceof TintTextView) { + if (eligibleForStartTinting((TintTextView) view)) { + ((TintTextView) view).setDrawableStartTint(ThemesUtils.getAccentColor()); + } + if (eligibleForTinting(view.getId())) { + ((TintTextView) view).setDrawableTint(ThemesUtils.getAccentColor()); + } + } + + if (view instanceof LinkedTextView) { + ((LinkedTextView) view).setLinkTextColor(ThemesUtils.getAccentColor()); + } + + if (view instanceof AppCompatCheckedTextView) { + var check = (AppCompatCheckedTextView) view; + if (ColorReferences.isAccentedColor(check.getCheckMarkTintList())) { + check.setCheckMarkTintList(ColorStateList.valueOf(ThemesUtils.getAccentColor())); + } + } + + VkUiThemer.autoThemeVkuiButtons(textView); + ThemesHacks.fixProfileSelector(textView); + ThemesHacks.fixThemedFeed(textView, i); + } + } + + public static boolean eligibleForStartTinting(TintTextView view) { + return ColorReferences.isAccentedColor(view.getTextColors()) || view.getTextColors().getDefaultColor() == ThemesUtils.getAccentColor(); + } + + public static boolean eligibleForTinting(int id) { + return !ThemesUtils.isDarkTheme() && (id == R.id.attach_title); + } + + public static boolean isPositiveButton(View view) { + return !ThemesUtils.isDarkTheme() && (view.getId() == R.id.positive || view.getId() == R.id.positive_button); + } + + public static boolean isVkUiButton(View view) { + return !ThemesUtils.isDarkTheme() && ( + view.getId() == R.id.empty_button + ); + } +} + diff --git a/app/src/main/java/ru/vtosters/lite/themes/hooks/ToolbarHook.java b/app/src/main/java/ru/vtosters/lite/themes/hooks/ToolbarHook.java new file mode 100644 index 000000000..b4275e0de --- /dev/null +++ b/app/src/main/java/ru/vtosters/lite/themes/hooks/ToolbarHook.java @@ -0,0 +1,14 @@ +package ru.vtosters.lite.themes.hooks; + +import android.view.View; +import android.widget.Toolbar; + +public class ToolbarHook implements BaseHook { + @Override + public void inject(View view, int i, boolean z) { + if (view instanceof Toolbar) { + // wtf? + } + } +} + diff --git a/app/src/main/java/ru/vtosters/lite/themes/hooks/VKImageViewHook.java b/app/src/main/java/ru/vtosters/lite/themes/hooks/VKImageViewHook.java new file mode 100644 index 000000000..38a4f6c09 --- /dev/null +++ b/app/src/main/java/ru/vtosters/lite/themes/hooks/VKImageViewHook.java @@ -0,0 +1,29 @@ +package ru.vtosters.lite.themes.hooks; + +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.view.View; + +import com.vk.imageloader.view.VKImageView; + +import ru.vtosters.lite.utils.ThemesUtils; + +public class VKImageViewHook implements BaseHook { + private static Paint paint = new Paint(); + static { + paint.setColorFilter(new PorterDuffColorFilter(ThemesUtils.getAccentColor(), PorterDuff.Mode.SRC_IN)); + } + + @Override + public void inject(View view, int i, boolean z) { + if (view instanceof VKImageView) { + var imageView = (VKImageView) view; + if (imageView.getImageTintList() != null) { + imageView.setImageTintList(ThemesUtils.recolorCSL(imageView.getImageTintList())); + imageView.setColorFilter(ThemesUtils.getAccentColor(), PorterDuff.Mode.SRC_IN); + } + } + } +} + diff --git a/app/src/main/java/ru/vtosters/lite/themes/hooks/WaveformViewHook.java b/app/src/main/java/ru/vtosters/lite/themes/hooks/WaveformViewHook.java new file mode 100644 index 000000000..50d554859 --- /dev/null +++ b/app/src/main/java/ru/vtosters/lite/themes/hooks/WaveformViewHook.java @@ -0,0 +1,16 @@ +package ru.vtosters.lite.themes.hooks; + +import android.view.View; + +import com.vtosters.lite.ui.widget.WaveformView; + +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(ThemesUtils.getAccentColor()); + } + } +} + diff --git a/app/src/main/java/ru/vtosters/lite/themes/utils/VkUiThemer.java b/app/src/main/java/ru/vtosters/lite/themes/utils/VkUiThemer.java new file mode 100644 index 000000000..d18e7f478 --- /dev/null +++ b/app/src/main/java/ru/vtosters/lite/themes/utils/VkUiThemer.java @@ -0,0 +1,81 @@ +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 ru.vtosters.lite.themes.ColorReferences; +import ru.vtosters.lite.utils.ThemesUtils; + +public class VkUiThemer { + public static void autoThemeVkuiButtons(TextView view) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) return; + if (ThemesUtils.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 | " + csl); + } + + if (ColorReferences.isAccentedColor(spd.getColor())) { + Log.d("TSCore", "[tsh] theming CSL"); + spd.setTint(ThemesUtils.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 | " + csl); + } + + if (ColorReferences.isAccentedColor(spd.getColor())) { + Log.d("TSCore", "[tsh:disabled] theming CSL"); + spd.setTint(ThemesUtils.getAccentColor()); + } + } + } catch (Exception e) { + // This must go wrong, so we ignore any exceptions that may occur + e.printStackTrace(); // REMOVE THIS! + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/vtosters/lite/utils/ThemesUtils.java b/app/src/main/java/ru/vtosters/lite/utils/ThemesUtils.java index ea51fb091..28e0d5da9 100644 --- a/app/src/main/java/ru/vtosters/lite/utils/ThemesUtils.java +++ b/app/src/main/java/ru/vtosters/lite/utils/ThemesUtils.java @@ -40,14 +40,9 @@ import com.vk.core.ui.themes.VKThemeHelper; import com.vtosters.lite.R; import com.vtosters.lite.data.ThemeTracker; -import java.util.Arrays; -import java.util.List; +import ru.vtosters.lite.themes.ColorReferences; public class ThemesUtils { - public static List accentColors = Arrays.asList( - "5692d7", "528bcc", "7aa0cc", "518bcc", "6296d0", "2f68aa", "638ebf", "5181b8", "71aaeb", "4774a8", "5baaf4", "4186c8", "add3ff", "4774a8", "718198", "5a9eff", "99a2ad", "74a2d6", "e9eef3", "dfe3e7", "eff1f3", "5c9ce6", "4986cc", "4680c2" - ); - public static void applyTheme(VKTheme theme) { var currentActivity = LifecycleUtils.getCurrentActivity(); if (currentActivity != null) { @@ -100,7 +95,7 @@ public class ThemesUtils { } public static int getAccentColor() { - return getColorFromAttr(R.attr.accent); + return getColor2(R.color.red); } // Color accent public static int getTextAttr() { @@ -267,8 +262,8 @@ public class ThemesUtils { 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); + boolean isUnselAccent = ColorReferences.isAccentedColor(unsel); + boolean isSelAccent = ColorReferences.isAccentedColor(sel); if (isUnselAccent || isSelAccent) { @@ -311,13 +306,6 @@ public class ThemesUtils { return ColorUtils.setAlphaComponent(src, 169); } - public static boolean isAccentedColor(ColorStateList target) { - return target != null && isAccentedColor(target.getDefaultColor()); - } - - public static boolean isAccentedColor(int target) { - return accentColors.contains(hex(target).toLowerCase()); - } public static int getColor(Context context, int i) { if (isColorRefAccented(i) && isAndroidMonet()) { @@ -384,6 +372,11 @@ public class ThemesUtils { return String.format("#%06X", i & 16777215); } // Get color as hex string + // hex, but without # at the beginning + public static String hexx(int i) { + return String.format("%06X", i & 16777215); + } + public static int getNavigationHeight(int Default) { int VKME = R.dimen.design_bottom_sheet_peek_height_min; diff --git a/smali/smali/androidx/core/content/ContextCompat.smali b/smali/smali/androidx/core/content/ContextCompat.smali index d9fa6879e..15f36416b 100644 --- a/smali/smali/androidx/core/content/ContextCompat.smali +++ b/smali/smali/androidx/core/content/ContextCompat.smali @@ -245,6 +245,13 @@ .annotation build Landroidx/annotation/ColorInt; .end annotation + # ru.vtosters.lite.themes.ThemesHacks.getHackedColor(Landroid/content/Context;I)I + invoke-static {p0, p1}, Lru/vtosters/lite/themes/ThemesHacks;->getHackedColor(Landroid/content/Context;I)I + + move-result v0 + + return v0 + .line 1 sget v0, Landroid/os/Build$VERSION;->SDK_INT:I diff --git a/smali/smali_classes2/com/vk/core/ui/VKLayoutInflater.smali b/smali/smali_classes2/com/vk/core/ui/VKLayoutInflater.smali index 81d7409bf..0caaa64cc 100644 --- a/smali/smali_classes2/com/vk/core/ui/VKLayoutInflater.smali +++ b/smali/smali_classes2/com/vk/core/ui/VKLayoutInflater.smali @@ -337,3 +337,67 @@ return-object p1 .end method + +.method public inflate(ILandroid/view/ViewGroup;)Landroid/view/View; + .registers 4 + + invoke-super {p0, p1, p2}, Landroid/view/LayoutInflater;->inflate(ILandroid/view/ViewGroup;)Landroid/view/View; + + move-result-object p2 + + const/4 v0, 0x1 + + invoke-static {p2, p1, v0}, Lru/vtosters/lite/themes/ViewInjector;->inject(Landroid/view/View;IZ)Landroid/view/View; + + move-result-object p1 + + return-object p1 +.end method + +.method public inflate(ILandroid/view/ViewGroup;Z)Landroid/view/View; + .registers 4 + + invoke-super {p0, p1, p2, p3}, Landroid/view/LayoutInflater;->inflate(ILandroid/view/ViewGroup;Z)Landroid/view/View; + + move-result-object p2 + + invoke-static {p2, p1, p3}, Lru/vtosters/lite/themes/ViewInjector;->inject(Landroid/view/View;IZ)Landroid/view/View; + + move-result-object p1 + + return-object p1 +.end method + +.method public inflate(Lorg/xmlpull/v1/XmlPullParser;Landroid/view/ViewGroup;)Landroid/view/View; + .registers 4 + + invoke-super {p0, p1, p2}, Landroid/view/LayoutInflater;->inflate(Lorg/xmlpull/v1/XmlPullParser;Landroid/view/ViewGroup;)Landroid/view/View; + + move-result-object p1 + + const/4 p2, -0x1 + + const/4 v0, 0x1 + + invoke-static {p1, p2, v0}, Lru/vtosters/lite/themes/ViewInjector;->inject(Landroid/view/View;IZ)Landroid/view/View; + + move-result-object p1 + + return-object p1 +.end method + +.method public inflate(Lorg/xmlpull/v1/XmlPullParser;Landroid/view/ViewGroup;Z)Landroid/view/View; + .registers 4 + + invoke-super {p0, p1, p2, p3}, Landroid/view/LayoutInflater;->inflate(Lorg/xmlpull/v1/XmlPullParser;Landroid/view/ViewGroup;Z)Landroid/view/View; + + move-result-object p1 + + const/4 p2, -0x1 + + invoke-static {p1, p2, p3}, Lru/vtosters/lite/themes/ViewInjector;->inject(Landroid/view/View;IZ)Landroid/view/View; + + move-result-object p1 + + return-object p1 +.end method \ No newline at end of file diff --git a/smali/smali_classes2/com/vk/core/ui/themes/VKThemeHelper$b.smali b/smali/smali_classes2/com/vk/core/ui/themes/VKThemeHelper$b.smali index 7f4c2bdff..1d4b829fe 100644 --- a/smali/smali_classes2/com/vk/core/ui/themes/VKThemeHelper$b.smali +++ b/smali/smali_classes2/com/vk/core/ui/themes/VKThemeHelper$b.smali @@ -27,6 +27,8 @@ .end annotation .end field +.field private resources:Landroid/content/res/Resources; + # direct methods .method public constructor (Landroid/content/Context;I)V @@ -151,3 +153,29 @@ return-object p1 .end method + +.method public getResources()Landroid/content/res/Resources; + .registers 3 + + iget-object v0, p0, Lcom/vk/core/ui/themes/VKThemeHelper$b;->resources:Landroid/content/res/Resources; + + if-eqz v0, :cond_7 + + iget-object v0, p0, Lcom/vk/core/ui/themes/VKThemeHelper$b;->resources:Landroid/content/res/Resources; + + :goto_6 + return-object v0 + + :cond_7 + new-instance v0, Lru/vtosters/lite/themes/VTLResources; + + invoke-static {}, Lru/vtosters/lite/utils/AndroidUtils;->getResources()Landroid/content/res/Resources; + + move-result-object v1 + + invoke-direct {v0, p0, v1}, Lru/vtosters/lite/themes/VTLResources;->(Landroid/content/Context;Landroid/content/res/Resources;)V + + iput-object v0, p0, Lcom/vk/core/ui/themes/VKThemeHelper$b;->resources:Landroid/content/res/Resources; + + goto :goto_6 +.end method diff --git a/smali/smali_classes2/com/vk/core/util/ContextExtKt.smali b/smali/smali_classes2/com/vk/core/util/ContextExtKt.smali index f536180e8..b86782ede 100644 --- a/smali/smali_classes2/com/vk/core/util/ContextExtKt.smali +++ b/smali/smali_classes2/com/vk/core/util/ContextExtKt.smali @@ -920,6 +920,19 @@ .end annotation .end param + invoke-static {p0, p1}, Lru/vtosters/lite/themes/ThemesCore;->hasThemedAttr(Landroid/content/Context;I)Z + + move-result v1 + + if-eqz v1, :cond_b + + invoke-static {p0, p1}, Lru/vtosters/lite/themes/ThemesCore;->getThemedAttr(Landroid/content/Context;I)I + + move-result v1 + + return v1 + :cond_b + .line 1 invoke-virtual {p0}, Landroid/content/Context;->getTheme()Landroid/content/res/Resources$Theme;