Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cdaf7909d4 | ||
|
|
c70ab388c1 | ||
|
|
50c66c2703 | ||
|
|
357e520bb0 | ||
|
|
6379ce5741 | ||
|
|
02807739a1 | ||
|
|
40635ab75a | ||
|
|
32e1bfe6fd |
@@ -28,6 +28,7 @@ import com.vtosters.lite.data.ThemeTracker;
|
||||
import ru.vtosters.lite.deviceinfo.OEMDetector;
|
||||
import ru.vtosters.lite.themes.ThemesCore;
|
||||
import ru.vtosters.lite.themes.ThemesHacks;
|
||||
import ru.vtosters.lite.themes.ThemesManager;
|
||||
import ru.vtosters.lite.ui.wallpapers.WallpapersHooks;
|
||||
import ru.vtosters.lite.utils.LifecycleUtils;
|
||||
import ru.vtosters.lite.utils.WebViewColoringUtils;
|
||||
@@ -59,7 +60,7 @@ public class ThemesUtils {
|
||||
activity = LifecycleUtils.getCurrentActivity();
|
||||
}
|
||||
VKThemeHelper.theme(theme, activity, fl);
|
||||
if (isMonetTheme()) ThemesCore.clear();
|
||||
if (isMonetTheme() || ThemesManager.canApplyCustomAccent()) ThemesCore.clear();
|
||||
if (restartActivity) activity.recreate();
|
||||
ThemeTracker.a();
|
||||
WebViewColoringUtils.isLoaded = false;
|
||||
|
||||
@@ -11,15 +11,15 @@ public class ImBanner {
|
||||
private static final String TAG = "ImBanner";
|
||||
|
||||
public static JSONObject convertToBanner(JSONObject original) {
|
||||
final String PEER = "peer";
|
||||
final String ID = "id";
|
||||
final String PICTURE = "picture";
|
||||
final String TEXT = "text";
|
||||
final String LINK = "link";
|
||||
final String LINK_TEXT = "link_text";
|
||||
final String NAME = "name";
|
||||
final String BUTTONS = "buttons";
|
||||
final String ICON = "icon";
|
||||
String PEER = "peer";
|
||||
String ID = "id";
|
||||
String PICTURE = "picture";
|
||||
String TEXT = "text";
|
||||
String LINK = "link";
|
||||
String LINK_TEXT = "link_text";
|
||||
String NAME = "name";
|
||||
String BUTTONS = "buttons";
|
||||
String ICON = "icon";
|
||||
|
||||
Optional<JSONObject> user = Optional.ofNullable(original.optJSONObject(PEER));
|
||||
int userId = user.map(u -> u.optInt(ID)).orElse(0);
|
||||
|
||||
@@ -166,26 +166,25 @@ public class ThemesCore {
|
||||
}
|
||||
|
||||
public static boolean hasThemedAttr(Context context, int attrID) {
|
||||
if (ThemesUtils.isMonetTheme()) {
|
||||
if (Preferences.getBoolValue("logColors", false)) {
|
||||
try {
|
||||
Log.d(TAG, "Requesting color by attr " + context.getResources().getResourceName(attrID));
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
if (Preferences.getBoolValue("logColors", false)) {
|
||||
try {
|
||||
Log.d(TAG, "Requesting color by attr " + context.getResources().getResourceName(attrID));
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
if (ThemesUtils.isMonetTheme() || ThemesManager.canApplyCustomAccent()) {
|
||||
if (isCachedAccents()) {
|
||||
if (ThemesUtils.isDarkTheme()) {
|
||||
return (themedColors.get(attrID) != 0 && ACCENT_THEME_ONLY_DARK.get(attrID, true) && (
|
||||
ThemesUtils.isMilkshake() ?
|
||||
ACCENT_THEME_ONLY_MILK_DARK.get(attrID, true) :
|
||||
ACCENT_THEME_ONLY_NOMILK_DARK.get(attrID, true)));
|
||||
} else {
|
||||
return (themedColors.get(attrID) != 0 && ACCENT_THEME_ONLY_LIGHT.get(attrID, true) && (
|
||||
ThemesUtils.isMilkshake() ?
|
||||
ACCENT_THEME_ONLY_MILK_LIGHT.get(attrID, true) :
|
||||
ACCENT_THEME_ONLY_NOMILK_LIGHT.get(attrID, true)));
|
||||
}
|
||||
boolean isDark = ThemesUtils.isDarkTheme();
|
||||
boolean isMilkshake = ThemesUtils.isMilkshake();
|
||||
SparseBooleanArray themeOnlyMap = isDark ? ACCENT_THEME_ONLY_DARK : ACCENT_THEME_ONLY_LIGHT;
|
||||
SparseBooleanArray milkshakeMap = isDark ? ACCENT_THEME_ONLY_MILK_DARK : ACCENT_THEME_ONLY_MILK_LIGHT;
|
||||
SparseBooleanArray noMilkshakeMap = isDark ? ACCENT_THEME_ONLY_NOMILK_DARK : ACCENT_THEME_ONLY_NOMILK_LIGHT;
|
||||
|
||||
return (themedColors.get(attrID) != 0 && themeOnlyMap.get(attrID, true) && (
|
||||
isMilkshake ?
|
||||
milkshakeMap.get(attrID, true) :
|
||||
noMilkshakeMap.get(attrID, true)));
|
||||
} else {
|
||||
return themedColors.get(attrID) != 0;
|
||||
}
|
||||
@@ -194,7 +193,6 @@ public class ThemesCore {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void clear() {
|
||||
cachedAccents = false;
|
||||
themedColors.clear();
|
||||
|
||||
@@ -20,7 +20,7 @@ import java.util.zip.ZipOutputStream;
|
||||
|
||||
public class ThemesManager {
|
||||
|
||||
private final static int[] ACCENT_COLORS = {
|
||||
private static final int[] ACCENT_COLORS = {
|
||||
R.color.vk_sky_300,
|
||||
R.color.sky_300,
|
||||
R.color.viewer_retry_button_text_color,
|
||||
@@ -96,7 +96,7 @@ public class ThemesManager {
|
||||
R.color.header_blue_opacity40
|
||||
};
|
||||
// modify it if "doNotCompress" in apktool.yml has been edited
|
||||
private final static String[] DO_NOT_COMPRESS = {
|
||||
private static final String[] DO_NOT_COMPRESS = {
|
||||
".png",
|
||||
".jpg",
|
||||
".mp3",
|
||||
@@ -124,14 +124,13 @@ public class ThemesManager {
|
||||
ResourcesLoader.load(app, modApk.getAbsolutePath(), false);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e("ThemesManager", e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
// validate modded apk before loading
|
||||
private static void validateModApk()
|
||||
throws IOException {
|
||||
final var apk = new ZipFile(modApk);
|
||||
private static void validateModApk() throws IOException {
|
||||
var apk = new ZipFile(modApk);
|
||||
apk.close();
|
||||
|
||||
validated = true;
|
||||
@@ -153,23 +152,18 @@ public class ThemesManager {
|
||||
return validated && modApk.exists() && ThemesUtils.getReservedAccent() != Color.TRANSPARENT;
|
||||
}
|
||||
|
||||
public static void generateModApk(int accentColor)
|
||||
throws Throwable {
|
||||
try (
|
||||
final var apk = new ZipFile(baseApkPath)
|
||||
) {
|
||||
final var arscEntry = apk.getEntry("resources.arsc");
|
||||
final var arscBis = new BufferedInputStream(apk.getInputStream(arscEntry));
|
||||
final var arsc = BinaryResourceFile.fromInputStream(arscBis);
|
||||
public static void generateModApk(int accentColor) throws Throwable {
|
||||
try (var apk = new ZipFile(baseApkPath)) {
|
||||
var arscEntry = apk.getEntry("resources.arsc");
|
||||
var arscBis = new BufferedInputStream(apk.getInputStream(arscEntry));
|
||||
var arsc = BinaryResourceFile.fromInputStream(arscBis);
|
||||
ArscEditor.changeColors(arsc, ACCENT_COLORS, accentColor);
|
||||
|
||||
try (
|
||||
final var zos = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(modApk)))
|
||||
) {
|
||||
final var arscBuff = arsc.toByteArray();
|
||||
final var newArscEntry = new ZipEntry("resources.arsc");
|
||||
try (var zos = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(modApk)))) {
|
||||
var arscBuff = arsc.toByteArray();
|
||||
var newArscEntry = new ZipEntry("resources.arsc");
|
||||
|
||||
final var crc = new CRC32();
|
||||
var crc = new CRC32();
|
||||
crc.update(arscBuff);
|
||||
|
||||
newArscEntry.setSize(arscBuff.length);
|
||||
@@ -180,10 +174,10 @@ public class ThemesManager {
|
||||
zos.write(arscBuff);
|
||||
zos.closeEntry();
|
||||
|
||||
final var entries = apk.entries();
|
||||
var entries = apk.entries();
|
||||
while (entries.hasMoreElements()) {
|
||||
final var entry = entries.nextElement();
|
||||
final var name = entry.getName();
|
||||
var entry = entries.nextElement();
|
||||
var name = entry.getName();
|
||||
|
||||
if (!name.startsWith("res/") && !name.startsWith("assets/") || name.equals("resources.arsc"))
|
||||
continue;
|
||||
@@ -196,8 +190,8 @@ public class ThemesManager {
|
||||
}
|
||||
}
|
||||
|
||||
final var entryBis = new BufferedInputStream(apk.getInputStream(entry));
|
||||
final var newEntry = new ZipEntry(name);
|
||||
var entryBis = new BufferedInputStream(apk.getInputStream(entry));
|
||||
var newEntry = new ZipEntry(name);
|
||||
|
||||
if (doNotCompress) {
|
||||
newEntry.setMethod(ZipEntry.STORED);
|
||||
|
||||
@@ -1,59 +1,44 @@
|
||||
package ru.vtosters.lite.themes.loaders;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.res.AssetManager;
|
||||
import android.util.Log;
|
||||
import ru.vtosters.lite.utils.ReflectionUtils;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
@SuppressLint("PrivateApi")
|
||||
public class AssetManagerHelper {
|
||||
|
||||
|
||||
// methods
|
||||
private final static Method getAssetPathMtd;
|
||||
private final static Method getApkAssetsMtd;
|
||||
private final static Method addAssetPathMtd;
|
||||
private final static Method addAssetPathAsSharedLibraryMtd;
|
||||
private final static Method addOverlayPathMtd;
|
||||
|
||||
static {
|
||||
try {
|
||||
getAssetPathMtd = ReflectionUtils.findMethod(Class.forName("android.content.res.ApkAssets"), "getAssetPath");
|
||||
getApkAssetsMtd = ReflectionUtils.findMethod(AssetManager.class, "getApkAssets");
|
||||
addAssetPathMtd = AssetManager.class.getMethod("addAssetPath", String.class);
|
||||
addAssetPathAsSharedLibraryMtd = AssetManager.class.getMethod("addAssetPathAsSharedLibrary", String.class);
|
||||
addOverlayPathMtd = AssetManager.class.getMethod("addOverlayPath", String.class);
|
||||
} catch (ClassNotFoundException | NoSuchMethodException e) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
public static String getApkPath(Object assetManager, String packagename)
|
||||
throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
Object[] apkAssetsArr = (Object[]) getAssetPathMtd.invoke(assetManager);
|
||||
public static String getApkPath(Object assetManager, String packagename) throws InvocationTargetException, IllegalAccessException, ClassNotFoundException, NoSuchMethodException {
|
||||
Object[] apkAssetsArr = (Object[]) ReflectionUtils.findMethod(Class.forName("android.content.res.ApkAssets"), "getAssetPath").invoke(assetManager);
|
||||
// By default, base apk is the last element of array if is not, for execution speed, we should iterate array from the end to the beginning
|
||||
for (int i = apkAssetsArr.length - 1; i >= 0; i--) {
|
||||
final String pathname = (String) getApkAssetsMtd.invoke(apkAssetsArr[i]);
|
||||
if (pathname.contains(packagename)) return pathname;
|
||||
String pathname = (String) ReflectionUtils.findMethod(AssetManager.class, "getApkAssets").invoke(apkAssetsArr[i]);
|
||||
if (pathname != null && pathname.contains(packagename)) return pathname;
|
||||
}
|
||||
|
||||
throw new NullPointerException();
|
||||
}
|
||||
|
||||
//region Uses for adding own assets or getting existing cookies
|
||||
public static int addAssetPath(AssetManager manager, String path)
|
||||
throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
return (int) addAssetPathMtd.invoke(manager, path);
|
||||
public static void addAssetPath(AssetManager manager, String path) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
manager.getClass().getMethod("addAssetPath", String.class).invoke(manager, path);
|
||||
}
|
||||
|
||||
public static int addAssetPathAsSharedLibrary(AssetManager manager, String path)
|
||||
throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
return (int) addAssetPathAsSharedLibraryMtd.invoke(manager, path);
|
||||
public static int addAssetPathAsSharedLibrary(AssetManager manager, String path) {
|
||||
try {
|
||||
return (int) AssetManager.class.getMethod("addAssetPathAsSharedLibrary", String.class).invoke(manager, path);
|
||||
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static int addOverlayPath(AssetManager manager, String path)
|
||||
throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
return (int) addOverlayPathMtd.invoke(manager, path);
|
||||
public static int addOverlayPath(AssetManager manager, String path) {
|
||||
try {
|
||||
return (int) AssetManager.class.getMethod("addOverlayPath", String.class).invoke(manager, path);
|
||||
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
//endregion
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package ru.vtosters.lite.themes.loaders;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.res.AssetManager;
|
||||
@@ -13,17 +14,19 @@ import ru.vtosters.lite.utils.ReflectionUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@SuppressLint("PrivateApi")
|
||||
public class ResourcesLoader {
|
||||
|
||||
private static Collection<WeakReference<Resources>> sResourceReferences = null;
|
||||
private static Object sCurrentActivityThread = null;
|
||||
private static AssetManager sAssetManager = null;
|
||||
|
||||
// methods
|
||||
private static Method ensureStringBlocksMtd = null;
|
||||
@@ -42,7 +45,7 @@ public class ResourcesLoader {
|
||||
static {
|
||||
try {
|
||||
// API version 8 has PackageInfo, 10 has LoadedApk. Needs to check 9
|
||||
final Class<?> activityThreadClz = Class.forName("android.app.ActivityThread");
|
||||
Class<?> activityThreadClz = Class.forName("android.app.ActivityThread");
|
||||
Class<?> loadedApkClz;
|
||||
try {
|
||||
loadedApkClz = Class.forName("android.app.LoadedApk");
|
||||
@@ -87,43 +90,40 @@ public class ResourcesLoader {
|
||||
}
|
||||
}
|
||||
|
||||
public static void init(Context context)
|
||||
throws Exception {
|
||||
public static void init(Context context) throws Exception {
|
||||
sCurrentActivityThread = ReflectionUtils.getActivityThread(context, null);
|
||||
|
||||
//pre-N
|
||||
// Find the singleton instance of ResourcesManager
|
||||
final Class<?> resourcesManagerClass = Class.forName("android.app.ResourcesManager");
|
||||
final Method mGetInstance = ReflectionUtils.findMethod(resourcesManagerClass, "getInstance");
|
||||
final Object resourcesManager = mGetInstance.invoke(null);
|
||||
Class<?> resourcesManagerClass = Class.forName("android.app.ResourcesManager");
|
||||
Method mGetInstance = ReflectionUtils.findMethod(resourcesManagerClass, "getInstance");
|
||||
Object resourcesManager = mGetInstance.invoke(null);
|
||||
try {
|
||||
Field fMActiveResources = ReflectionUtils.findField(resourcesManagerClass, "mActiveResources");
|
||||
final ArrayMap<?, WeakReference<Resources>> activeResources19 =
|
||||
ArrayMap<?, WeakReference<Resources>> activeResources19 =
|
||||
(ArrayMap<?, WeakReference<Resources>>) fMActiveResources.get(resourcesManager);
|
||||
sResourceReferences = activeResources19.values();
|
||||
} catch (NoSuchFieldException ignore) {
|
||||
// N moved the resources to mResourceReferences
|
||||
final Field mResourceReferences = ReflectionUtils.findField(resourcesManagerClass, "mResourceReferences");
|
||||
Field mResourceReferences = ReflectionUtils.findField(resourcesManagerClass, "mResourceReferences");
|
||||
sResourceReferences = (Collection<WeakReference<Resources>>) mResourceReferences.get(resourcesManager);
|
||||
}
|
||||
}
|
||||
|
||||
public static void load(Context context, String resPath, boolean reinject)
|
||||
throws Exception {
|
||||
final ApplicationInfo appInfo = context.getApplicationInfo();
|
||||
public static void load(Context context, String resPath, boolean reinject) throws IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchFieldException, NoSuchMethodException {
|
||||
ApplicationInfo appInfo = context.getApplicationInfo();
|
||||
|
||||
final Field[] packagesFlds = Build.VERSION.SDK_INT < Build.VERSION_CODES.O_MR1
|
||||
Field[] packagesFlds = Build.VERSION.SDK_INT < Build.VERSION_CODES.O_MR1
|
||||
? new Field[]{packagesFld, resourcePackagesFld}
|
||||
: new Field[]{packagesFld};
|
||||
for (var fld : packagesFlds) {
|
||||
final var value = fld.get(sCurrentActivityThread);
|
||||
var value = fld.get(sCurrentActivityThread);
|
||||
|
||||
for (var entry
|
||||
: ((Map<String, WeakReference<?>>) value).entrySet()) {
|
||||
final var loadedApk = entry.getValue().get();
|
||||
for (var entry : ((Map<String, WeakReference<?>>) value).entrySet()) {
|
||||
var loadedApk = entry.getValue().get();
|
||||
if (loadedApk == null) continue;
|
||||
|
||||
final var resDirPath = (String) resDirFld.get(loadedApk);
|
||||
var resDirPath = (String) resDirFld.get(loadedApk);
|
||||
if (appInfo.sourceDir.equals(resDirPath))
|
||||
resDirFld.set(loadedApk, resPath);
|
||||
}
|
||||
@@ -134,10 +134,10 @@ public class ResourcesLoader {
|
||||
return;
|
||||
}
|
||||
|
||||
sAssetManager = AssetManager.class.newInstance();
|
||||
// Create a new AssetManager instance and point it to the resources installed under
|
||||
if (AssetManagerHelper.addAssetPath(sAssetManager, resPath) == 0)
|
||||
throw new IllegalStateException("Could not create new AssetManager");
|
||||
AssetManager sAssetManager = AssetManager.class.newInstance();
|
||||
|
||||
AssetManagerHelper.addAssetPath(sAssetManager, resPath);
|
||||
|
||||
// Add SharedLibraries to AssetManager for resolve system resources not found issue
|
||||
// This influence SharedLibrary Package ID
|
||||
@@ -157,7 +157,7 @@ public class ResourcesLoader {
|
||||
ensureStringBlocksMtd.invoke(sAssetManager);
|
||||
}
|
||||
for (var wr : sResourceReferences) {
|
||||
final var resources = wr.get();
|
||||
var resources = wr.get();
|
||||
if (resources == null) continue;
|
||||
|
||||
// Set the AssetManager of the Resources instance to our brand new one
|
||||
@@ -166,9 +166,9 @@ public class ResourcesLoader {
|
||||
assetsFld.set(resources, sAssetManager);
|
||||
} catch (Throwable ignore) {
|
||||
// N
|
||||
final var resourceImpl = resourcesImplFld.get(resources);
|
||||
var resourceImpl = resourcesImplFld.get(resources);
|
||||
// for Huawei HwResourcesImpl
|
||||
final Field implAssets = ReflectionUtils.findField(resourceImpl, "mAssets");
|
||||
Field implAssets = ReflectionUtils.findField(resourceImpl, "mAssets");
|
||||
implAssets.set(resourceImpl, sAssetManager);
|
||||
}
|
||||
|
||||
@@ -196,13 +196,13 @@ public class ResourcesLoader {
|
||||
|
||||
private static void installResourceInsuranceHacks(Context context, String patchedResApkPath) {
|
||||
try {
|
||||
final Object activityThread = ReflectionUtils.getActivityThread(context, null);
|
||||
final Field mHField = ReflectionUtils.findField(activityThread, "mH");
|
||||
final Handler mH = (Handler) mHField.get(activityThread);
|
||||
final Field mCallbackField = ReflectionUtils.findField(Handler.class, "mCallback");
|
||||
final Handler.Callback originCallback = (Handler.Callback) mCallbackField.get(mH);
|
||||
Object activityThread = ReflectionUtils.getActivityThread(context, null);
|
||||
Field mHField = ReflectionUtils.findField(activityThread, "mH");
|
||||
Handler mH = (Handler) mHField.get(activityThread);
|
||||
Field mCallbackField = ReflectionUtils.findField(Handler.class, "mCallback");
|
||||
Handler.Callback originCallback = (Handler.Callback) mCallbackField.get(mH);
|
||||
if (!(originCallback instanceof ResourceInsuranceHandlerCallback)) {
|
||||
final ResourceInsuranceHandlerCallback hackCallback = new ResourceInsuranceHandlerCallback(
|
||||
ResourceInsuranceHandlerCallback hackCallback = new ResourceInsuranceHandlerCallback(
|
||||
context, patchedResApkPath, originCallback, mH.getClass());
|
||||
mCallbackField.set(mH, hackCallback);
|
||||
} else {
|
||||
@@ -250,9 +250,9 @@ public class ResourcesLoader {
|
||||
Log.w("ResourcesLoader", "try to clear typedArray cache!");
|
||||
// Clear typedArray cache.
|
||||
try {
|
||||
final Field typedArrayPoolField = ReflectionUtils.findField(Resources.class, "mTypedArrayPool");
|
||||
final Object origTypedArrayPool = typedArrayPoolField.get(resources);
|
||||
final Method acquireMethod = ReflectionUtils.findMethod(origTypedArrayPool, "acquire");
|
||||
Field typedArrayPoolField = ReflectionUtils.findField(Resources.class, "mTypedArrayPool");
|
||||
Object origTypedArrayPool = typedArrayPoolField.get(resources);
|
||||
Method acquireMethod = ReflectionUtils.findMethod(origTypedArrayPool, "acquire");
|
||||
while (true) {
|
||||
if (acquireMethod.invoke(origTypedArrayPool) == null) {
|
||||
break;
|
||||
@@ -268,9 +268,8 @@ public class ResourcesLoader {
|
||||
applicationInfo.sharedLibraryFiles != null;
|
||||
}
|
||||
|
||||
private static final class ResourceInsuranceHandlerCallback implements Handler.Callback {
|
||||
private static class ResourceInsuranceHandlerCallback implements Handler.Callback {
|
||||
private static final String LAUNCH_ACTIVITY_LIFECYCLE_ITEM_CLASSNAME = "android.app.servertransaction.LaunchActivityItem";
|
||||
|
||||
private final Context mContext;
|
||||
private final String mPatchResApkPath;
|
||||
private final Handler.Callback mOriginalCallback;
|
||||
@@ -319,48 +318,56 @@ public class ResourcesLoader {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private boolean hackMessage(Message msg) {
|
||||
boolean shouldReInjectPatchedResources = false;
|
||||
// A method to check if the message requires re-injecting patched resources
|
||||
private boolean shouldReInjectPatchedResources(Message msg) {
|
||||
if (!isPatchedResModifiedAfterLastLoad(mPatchResApkPath)) {
|
||||
shouldReInjectPatchedResources = false;
|
||||
} else {
|
||||
if (msg.what == LAUNCH_ACTIVITY || msg.what == RELAUNCH_ACTIVITY) {
|
||||
shouldReInjectPatchedResources = true;
|
||||
} else if (msg.what == EXECUTE_TRANSACTION) {
|
||||
do {
|
||||
if (mSkipInterceptExecuteTransaction) {
|
||||
break;
|
||||
}
|
||||
final Object transaction = msg.obj;
|
||||
if (transaction == null) {
|
||||
Log.w("ResourcesLoader", "transaction is null, skip rest insurance logic.");
|
||||
break;
|
||||
}
|
||||
if (mGetCallbacksMethod == null) {
|
||||
try {
|
||||
mGetCallbacksMethod = ReflectionUtils.findMethod(transaction, "getCallbacks");
|
||||
} catch (Throwable ignored) {
|
||||
// Ignored.
|
||||
}
|
||||
}
|
||||
if (mGetCallbacksMethod == null) {
|
||||
Log.e("ResourcesLoader", "fail to find getLifecycleStateRequest method, skip rest insurance logic.");
|
||||
mSkipInterceptExecuteTransaction = true;
|
||||
break;
|
||||
}
|
||||
try {
|
||||
final List<Object> req = (List<Object>) mGetCallbacksMethod.invoke(transaction);
|
||||
if (req != null && req.size() > 0) {
|
||||
final Object cb = req.get(0);
|
||||
shouldReInjectPatchedResources = cb != null && cb.getClass().getName().equals(LAUNCH_ACTIVITY_LIFECYCLE_ITEM_CLASSNAME);
|
||||
}
|
||||
} catch (Throwable ignored) {
|
||||
Log.e("ResourcesLoader", "fail to call getLifecycleStateRequest method, skip rest insurance logic.");
|
||||
}
|
||||
} while (false);
|
||||
return false;
|
||||
}
|
||||
if (msg.what == LAUNCH_ACTIVITY || msg.what == RELAUNCH_ACTIVITY) {
|
||||
return true;
|
||||
}
|
||||
// If the message is executing a transaction, check the callbacks
|
||||
if (msg.what == EXECUTE_TRANSACTION) {
|
||||
// Skip the logic if the method is not found or an error occurs
|
||||
if (mSkipInterceptExecuteTransaction) {
|
||||
return false;
|
||||
}
|
||||
// Get the transaction object from the message
|
||||
Object transaction = msg.obj;
|
||||
if (transaction == null) {
|
||||
Log.w("ResourcesLoader", "transaction is null, skip rest insurance logic.");
|
||||
return false;
|
||||
}
|
||||
// Get the getCallbacks method from the transaction class
|
||||
if (mGetCallbacksMethod == null) {
|
||||
try {
|
||||
mGetCallbacksMethod = ReflectionUtils.findMethod(transaction, "getCallbacks");
|
||||
} catch (Throwable ignored) {
|
||||
// Ignored.
|
||||
}
|
||||
}
|
||||
if (mGetCallbacksMethod == null) {
|
||||
Log.e("ResourcesLoader", "fail to find getCallbacks method, skip rest insurance logic.");
|
||||
mSkipInterceptExecuteTransaction = true;
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
List<Object> callbacks = (List<Object>) mGetCallbacksMethod.invoke(transaction);
|
||||
if (callbacks != null && !callbacks.isEmpty()) {
|
||||
Object cb = callbacks.get(0);
|
||||
return cb != null && cb.getClass().getName().equals(LAUNCH_ACTIVITY_LIFECYCLE_ITEM_CLASSNAME);
|
||||
}
|
||||
} catch (Throwable ignored) {
|
||||
Log.e("ResourcesLoader", "fail to call getCallbacks method, skip rest insurance logic.");
|
||||
}
|
||||
}
|
||||
if (shouldReInjectPatchedResources) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// A method to hack the message and re-inject patched resources if needed
|
||||
@SuppressWarnings("unchecked")
|
||||
private boolean hackMessage(Message msg) {
|
||||
if (shouldReInjectPatchedResources(msg)) {
|
||||
try {
|
||||
load(mContext, mPatchResApkPath, true);
|
||||
} catch (Throwable thr) {
|
||||
|
||||
@@ -23,8 +23,8 @@ import static ru.vtosters.lite.utils.AndroidUtils.getGlobalContext;
|
||||
|
||||
public class PalettesManager {
|
||||
|
||||
private final static File PALETTES_DIR = new File(Environment.getExternalStorageDirectory(), "VTL/palettes");
|
||||
private final static String ASSETS_DIR = "vtl_themes/palettes/";
|
||||
private static final File PALETTES_DIR = new File(Environment.getExternalStorageDirectory(), "VTL/palettes");
|
||||
private static final String ASSETS_DIR = "vtl_themes/palettes/";
|
||||
|
||||
private static PalettesManager sInstance;
|
||||
|
||||
@@ -51,17 +51,17 @@ public class PalettesManager {
|
||||
return;
|
||||
}
|
||||
|
||||
final var palettes = PALETTES_DIR.listFiles((dir, name) -> name.endsWith(".json"));
|
||||
var palettes = PALETTES_DIR.listFiles((dir, name) -> name.endsWith(".json"));
|
||||
if (palettes == null || palettes.length == 0)
|
||||
copyPaletteFromAssets();
|
||||
parsePalettes();
|
||||
}
|
||||
|
||||
private boolean copyPaletteFromAssets() {
|
||||
private void copyPaletteFromAssets() {
|
||||
try {
|
||||
final var manager = AndroidUtils.getGlobalContext().getAssets();
|
||||
final var assets = manager.list(ASSETS_DIR);
|
||||
if (assets == null || assets.length == 0) return false;
|
||||
var manager = AndroidUtils.getGlobalContext().getAssets();
|
||||
var assets = manager.list(ASSETS_DIR);
|
||||
if (assets == null || assets.length == 0) return;
|
||||
for (var asset : assets) {
|
||||
try {
|
||||
IOUtils.copy(manager.open(ASSETS_DIR + asset), new File(PALETTES_DIR, asset));
|
||||
@@ -69,11 +69,9 @@ public class PalettesManager {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void parsePalettes() {
|
||||
|
||||
@@ -8,15 +8,15 @@ public class ArscEditor {
|
||||
|
||||
public static void changeColors(BinaryResourceFile arsc, int[] colorIds, int color) {
|
||||
for (var chunk : arsc.getChunks()) {
|
||||
final var resTableChunk = (ResourceTableChunk) chunk;
|
||||
var resTableChunk = (ResourceTableChunk) chunk;
|
||||
for (var packageChunk : resTableChunk.getPackages()) {
|
||||
for (var typeChunk : packageChunk.getTypeChunks("color")) {
|
||||
for (var entry : typeChunk.getEntries().entrySet()) {
|
||||
final var value = entry.getValue();
|
||||
var value = entry.getValue();
|
||||
if (value.isComplex()) continue;
|
||||
final var binValue = value.value();
|
||||
var binValue = value.value();
|
||||
if (!isColorResourceValue(binValue)) continue;
|
||||
final var resId = packageChunk.getId() << 24 | typeChunk.getId() << 16 | entry.getKey();
|
||||
var resId = packageChunk.getId() << 24 | typeChunk.getId() << 16 | entry.getKey();
|
||||
for (var colodId : colorIds) {
|
||||
if (resId == colodId) {
|
||||
binValue.data = color;
|
||||
|
||||
@@ -301,7 +301,7 @@ public class PreferenceFragmentUtils {
|
||||
return addPreference(screen, key, title, null, null, preference -> {
|
||||
LinearLayout linearLayout = new LinearLayout(screen.getContext());
|
||||
|
||||
final EditText editText = new EditText(screen.getContext());
|
||||
EditText editText = new EditText(screen.getContext());
|
||||
editText.setText(PreferenceManager.getDefaultSharedPreferences(screen.getContext()).getString(key, ""));
|
||||
editText.setHint(title);
|
||||
editText.setTextColor(ThemesUtils.getTextAttr());
|
||||
|
||||
@@ -36,7 +36,7 @@ public class CategorizedAdapter<T> extends RecyclerView.Adapter<CategorizedAdapt
|
||||
private final List<T> mSelectedItems;
|
||||
private final List<T> mDisabledItems;
|
||||
private final List<T> mUnmovedItems;
|
||||
final private List<T> mExceptions = new ArrayList<>();
|
||||
private final List<T> mExceptions = new ArrayList<>();
|
||||
private final IViewHolderBinder mBinder;
|
||||
|
||||
// Min and max counts
|
||||
@@ -50,7 +50,8 @@ public class CategorizedAdapter<T> extends RecyclerView.Adapter<CategorizedAdapt
|
||||
this.mBinder = binder;
|
||||
}
|
||||
|
||||
public void setExceptions(T... exceptions) {
|
||||
@SafeVarargs
|
||||
public final void setExceptions(T... exceptions) {
|
||||
mExceptions.clear();
|
||||
mExceptions.addAll(Arrays.asList(exceptions));
|
||||
}
|
||||
@@ -186,11 +187,16 @@ public class CategorizedAdapter<T> extends RecyclerView.Adapter<CategorizedAdapt
|
||||
&& mSelectedItems.size() == mMinSelectedItems))
|
||||
return false;
|
||||
if (getItemViewType(toPosition) == CATEGORY_TITLE) {
|
||||
if (mExceptions.size() > 0) {
|
||||
if (!mExceptions.isEmpty()) {
|
||||
boolean except = false;
|
||||
int q = 0;
|
||||
for (var ex : mExceptions)
|
||||
if (mSelectedItems.contains(ex)) ++q;
|
||||
if (q == 1) return false;
|
||||
for (var ex : mExceptions) {
|
||||
var i = mSelectedItems.indexOf(ex);
|
||||
if (i == -1) continue;
|
||||
if (i + 1 == fromPosition) except = true;
|
||||
++q;
|
||||
}
|
||||
if (except && q == 1) return false;
|
||||
}
|
||||
mDisabledItems.add(0, mSelectedItems.remove(fromPosition - 1));
|
||||
} else Collections.swap(mSelectedItems, fromPosition - 1, toPosition - 1);
|
||||
@@ -218,7 +224,6 @@ public class CategorizedAdapter<T> extends RecyclerView.Adapter<CategorizedAdapt
|
||||
}
|
||||
|
||||
public static class CategorizedViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
private final LinearLayout mMovingItemContainer;
|
||||
private final ImageView mIcon;
|
||||
private final TextView mName;
|
||||
|
||||
@@ -18,7 +18,6 @@ import ru.vtosters.hooks.other.ThemesUtils;
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
|
||||
public class MessagesPreviewAdapter extends RecyclerView.Adapter<MessagesPreviewAdapter.MessagePreviewViewHolder> {
|
||||
|
||||
private final String[] mMessages;
|
||||
|
||||
public MessagesPreviewAdapter(String... messages) {
|
||||
@@ -35,9 +34,9 @@ public class MessagesPreviewAdapter extends RecyclerView.Adapter<MessagesPreview
|
||||
public MessagePreviewViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int viewType) {
|
||||
Context context = viewGroup.getContext();
|
||||
|
||||
final var inflater = LayoutInflater.from(context);
|
||||
var inflater = LayoutInflater.from(context);
|
||||
|
||||
final var bubbleFluidLayout = (BubbleFluidLayout) inflater.inflate(R.layout.vkim_msg_list_item_msg_from_user, viewGroup, false);
|
||||
var bubbleFluidLayout = (BubbleFluidLayout) inflater.inflate(R.layout.vkim_msg_list_item_msg_from_user, viewGroup, false);
|
||||
bubbleFluidLayout.setPadding(0, AndroidUtils.dp2px(4), 0, 0);
|
||||
bubbleFluidLayout.setGravity(viewType);
|
||||
|
||||
@@ -47,7 +46,7 @@ public class MessagesPreviewAdapter extends RecyclerView.Adapter<MessagesPreview
|
||||
bubbleFluidLayout.findViewById(R.id.status).setVisibility(View.GONE);
|
||||
bubbleFluidLayout.findViewById(R.id.status_space).setVisibility(View.GONE);
|
||||
|
||||
final var messageBubbleView = (MsgBubbleView) bubbleFluidLayout.findViewById(R.id.bubble);
|
||||
var messageBubbleView = (MsgBubbleView) bubbleFluidLayout.findViewById(R.id.bubble);
|
||||
messageBubbleView.setContentPaddingLeft(AndroidUtils.dp2px(10));
|
||||
messageBubbleView.setContentPaddingTop(AndroidUtils.dp2px(4));
|
||||
messageBubbleView.setContentPaddingRight(AndroidUtils.dp2px(4));
|
||||
@@ -59,10 +58,10 @@ public class MessagesPreviewAdapter extends RecyclerView.Adapter<MessagesPreview
|
||||
}
|
||||
bubbleFluidLayout.setLayoutDirection(viewType);
|
||||
|
||||
final var msgTextView = inflater.inflate(R.layout.vkim_msg_part_text, viewGroup, false);
|
||||
var msgTextView = inflater.inflate(R.layout.vkim_msg_part_text, viewGroup, false);
|
||||
msgTextView.setId(R.id.msg_text);
|
||||
|
||||
final var msgBubbleView = (MsgBubbleView) bubbleFluidLayout.findViewById(R.id.bubble);
|
||||
var msgBubbleView = (MsgBubbleView) bubbleFluidLayout.findViewById(R.id.bubble);
|
||||
msgBubbleView.setContentView(msgTextView);
|
||||
|
||||
return new MessagePreviewViewHolder(bubbleFluidLayout);
|
||||
|
||||
@@ -17,8 +17,8 @@ public class DisableBattery {
|
||||
public static void alert(Activity activity) {
|
||||
if (!(OEMDetector.isOEM() && Build.VERSION.SDK_INT >= 23 && Preferences.getBoolValue("showDoze", true)))
|
||||
return;
|
||||
final String packName = activity.getPackageName();
|
||||
final PowerManager manager = (PowerManager) activity.getSystemService(Context.POWER_SERVICE);
|
||||
String packName = activity.getPackageName();
|
||||
PowerManager manager = (PowerManager) activity.getSystemService(Context.POWER_SERVICE);
|
||||
if (manager.isIgnoringBatteryOptimizations(packName)) return;
|
||||
new VkAlertDialog.Builder(activity)
|
||||
.setTitle(R.string.batteryissuetitle)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package ru.vtosters.lite.ui.dialogs;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
@@ -41,6 +42,7 @@ public class OTADialog extends ModalBottomSheetWrapper<OTADialog> implements OTA
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
private View makeUpdateInfoView(String changelog) {
|
||||
LinearLayout container = new LinearLayout(mActivity);
|
||||
TextView changelogView = new TextView(mActivity);
|
||||
|
||||
@@ -37,16 +37,16 @@ public class AlphaView extends SliderViewBase implements ColorObserver {
|
||||
|
||||
@Override
|
||||
protected Bitmap makeBitmap(int w, int h) {
|
||||
final boolean isWide = w > h;
|
||||
final int n = Math.max(w, h);
|
||||
boolean isWide = w > h;
|
||||
int n = Math.max(w, h);
|
||||
int color = observableColor.getColor();
|
||||
int[] colors = new int[n];
|
||||
for (int i = 0; i < n; ++i) {
|
||||
float alpha = isWide ? (float) i / n : 1 - (float) i / n;
|
||||
colors[i] = color & 0xffffff | (int) (alpha * 0xff) << 24;
|
||||
}
|
||||
final int bmpWidth = isWide ? w : 1;
|
||||
final int bmpHeight = isWide ? 1 : h;
|
||||
int bmpWidth = isWide ? w : 1;
|
||||
int bmpHeight = isWide ? 1 : h;
|
||||
return Bitmap.createBitmap(colors, bmpWidth, bmpHeight, Bitmap.Config.ARGB_8888);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,13 +18,12 @@ class HexEdit {
|
||||
new HexFilter()
|
||||
};
|
||||
|
||||
public static void setUpListeners(final EditText hexEdit, final ObservableColor observableColor) {
|
||||
|
||||
public static void setUpListeners(EditText hexEdit, ObservableColor observableColor) {
|
||||
class MultiObserver implements ColorObserver, TextWatcher {
|
||||
|
||||
@Override
|
||||
public void updateColor(ObservableColor observableColor) {
|
||||
final String colorString = formatColor(observableColor.getColor());
|
||||
String colorString = formatColor(observableColor.getColor());
|
||||
// Prevent onTextChanged getting called when we update text programmatically
|
||||
hexEdit.removeTextChangedListener(this);
|
||||
hexEdit.setText(colorString);
|
||||
@@ -57,7 +56,7 @@ class HexEdit {
|
||||
}
|
||||
}
|
||||
|
||||
final MultiObserver multiObserver = new MultiObserver();
|
||||
MultiObserver multiObserver = new MultiObserver();
|
||||
hexEdit.addTextChangedListener(multiObserver);
|
||||
observableColor.addObserver(multiObserver);
|
||||
setShowAlphaDigits(hexEdit, true);
|
||||
@@ -71,14 +70,13 @@ class HexEdit {
|
||||
}
|
||||
}
|
||||
|
||||
public static void setShowAlphaDigits(final EditText hexEdit, boolean showAlphaDigits) {
|
||||
public static void setShowAlphaDigits(EditText hexEdit, boolean showAlphaDigits) {
|
||||
hexEdit.setFilters(showAlphaDigits ? withAlphaDigits : withoutAlphaDigits);
|
||||
hexEdit.setText(hexEdit.getText()); // trigger a reformat of text
|
||||
}
|
||||
|
||||
|
||||
private static class ColorPasteLengthFilter implements InputFilter {
|
||||
|
||||
private static final int MAX_LENGTH = 6;
|
||||
private static final int PASTED_LEN = 8;
|
||||
private final InputFilter sixDigitFilter = new InputFilter.LengthFilter(MAX_LENGTH);
|
||||
@@ -86,8 +84,8 @@ class HexEdit {
|
||||
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
|
||||
// If 8 digits have been pasted, replacing all source, trim alpha digits.
|
||||
// Otherwise standard LengthFilter behavior.
|
||||
final int srcLength = end - start;
|
||||
final int dstSelLength = dend - dstart;
|
||||
int srcLength = end - start;
|
||||
int dstSelLength = dend - dstart;
|
||||
if (srcLength == PASTED_LEN && dstSelLength == dest.length()) {
|
||||
// Discard alpha digits:
|
||||
return source.subSequence(PASTED_LEN - MAX_LENGTH, PASTED_LEN);
|
||||
@@ -98,7 +96,6 @@ class HexEdit {
|
||||
}
|
||||
|
||||
private static class HexFilter implements InputFilter {
|
||||
|
||||
@Override
|
||||
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
|
||||
boolean state = true;
|
||||
|
||||
@@ -57,9 +57,9 @@ public class HueSatView extends SquareView implements ColorObserver {
|
||||
float[] hsv = new float[]{0f, 0f, 1f};
|
||||
for (int y = 0; y < radiusPx; ++y) {
|
||||
for (int x = 0; x < radiusPx; ++x) {
|
||||
final int i = x + y * radiusPx;
|
||||
final float sat = satForPos(x, y, radiusPx);
|
||||
final float arcBleed = 2f / radiusPx; // extend curved edge pixels just outside clip area.
|
||||
int i = x + y * radiusPx;
|
||||
float sat = satForPos(x, y, radiusPx);
|
||||
float arcBleed = 2f / radiusPx; // extend curved edge pixels just outside clip area.
|
||||
if (sat <= 1 + arcBleed) {
|
||||
hsv[0] = hueForPos(x, y, radiusPx);
|
||||
hsv[1] = sat;
|
||||
@@ -71,35 +71,35 @@ public class HueSatView extends SquareView implements ColorObserver {
|
||||
}
|
||||
|
||||
private static float hueForPos(float x, float y, float radiusPx) {
|
||||
final double r = radiusPx - 1; // gives values 0...1 inclusive
|
||||
final double dx = (r - x) / r;
|
||||
final double dy = (r - y) / r;
|
||||
final double angle = Math.atan2(dy, dx);
|
||||
final double hue = 360 * angle / (Math.PI / 2);
|
||||
double r = radiusPx - 1; // gives values 0...1 inclusive
|
||||
double dx = (r - x) / r;
|
||||
double dy = (r - y) / r;
|
||||
double angle = Math.atan2(dy, dx);
|
||||
double hue = 360 * angle / (Math.PI / 2);
|
||||
return (float) hue;
|
||||
}
|
||||
|
||||
private static float satForPos(float x, float y, float radiusPx) {
|
||||
final double r = radiusPx - 1; // gives values 0...1 inclusive
|
||||
final double dx = (r - x) / r;
|
||||
final double dy = (r - y) / r;
|
||||
final double sat = dx * dx + dy * dy; // leave it squared -- exaggerates pale colors
|
||||
double r = radiusPx - 1; // gives values 0...1 inclusive
|
||||
double dx = (r - x) / r;
|
||||
double dy = (r - y) / r;
|
||||
double sat = dx * dx + dy * dy; // leave it squared -- exaggerates pale colors
|
||||
return (float) sat;
|
||||
}
|
||||
|
||||
private static void setPointer(PointF pointer, float hue, float sat, float radiusPx) {
|
||||
final float r = radiusPx - 1; // for values 0...1 inclusive
|
||||
final double distance = r * Math.sqrt(sat);
|
||||
final double angle = hue / 360 * Math.PI / 2;
|
||||
final double dx = distance * Math.cos(angle);
|
||||
final double dy = distance * Math.sin(angle);
|
||||
float r = radiusPx - 1; // for values 0...1 inclusive
|
||||
double distance = r * Math.sqrt(sat);
|
||||
double angle = hue / 360 * Math.PI / 2;
|
||||
double dx = distance * Math.cos(angle);
|
||||
double dy = distance * Math.sin(angle);
|
||||
pointer.set(r - (float) dx, r - (float) dy);
|
||||
}
|
||||
|
||||
private int optimalBitmapSize() {
|
||||
final int scale = 2;
|
||||
final int maxBitmapSize = 128;
|
||||
final DisplayMetrics dm = getResources().getDisplayMetrics();
|
||||
int scale = 2;
|
||||
int maxBitmapSize = 128;
|
||||
DisplayMetrics dm = getResources().getDisplayMetrics();
|
||||
return Math.min(maxBitmapSize, Math.min(dm.widthPixels, dm.heightPixels) / scale);
|
||||
}
|
||||
|
||||
@@ -159,9 +159,9 @@ public class HueSatView extends SquareView implements ColorObserver {
|
||||
private boolean clamp(PointF pointer, float x, float y, boolean rejectOutside) {
|
||||
x = Math.min(x, w);
|
||||
y = Math.min(y, h);
|
||||
final float dx = w - x;
|
||||
final float dy = h - y;
|
||||
final float r = (float) Math.sqrt(dx * dx + dy * dy);
|
||||
float dx = w - x;
|
||||
float dy = h - y;
|
||||
float r = (float) Math.sqrt(dx * dx + dy * dy);
|
||||
boolean outside = r > w;
|
||||
if (!outside || !rejectOutside) {
|
||||
if (outside) {
|
||||
|
||||
@@ -6,7 +6,6 @@ import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
public abstract class SliderViewBase extends View {
|
||||
|
||||
private final Paint borderPaint;
|
||||
private final Paint checkerPaint;
|
||||
private final Rect viewRect = new Rect();
|
||||
|
||||
@@ -18,10 +18,10 @@ class SquareView extends View {
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
// Constrain to square
|
||||
final int w = MeasureSpec.getSize(widthMeasureSpec);
|
||||
final int h = MeasureSpec.getSize(heightMeasureSpec);
|
||||
final int modeW = MeasureSpec.getMode(widthMeasureSpec);
|
||||
final int modeH = MeasureSpec.getMode(heightMeasureSpec);
|
||||
int w = MeasureSpec.getSize(widthMeasureSpec);
|
||||
int h = MeasureSpec.getSize(heightMeasureSpec);
|
||||
int modeW = MeasureSpec.getMode(widthMeasureSpec);
|
||||
int modeH = MeasureSpec.getMode(heightMeasureSpec);
|
||||
int size = minSizePx;
|
||||
if (modeW == MeasureSpec.UNSPECIFIED) {
|
||||
size = h;
|
||||
|
||||
@@ -8,7 +8,6 @@ import android.graphics.RectF;
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
|
||||
public class SwatchView extends SquareView implements ColorObserver {
|
||||
|
||||
private final Paint borderPaint;
|
||||
private final Path borderPath;
|
||||
private final Paint checkerPaint;
|
||||
@@ -46,14 +45,14 @@ public class SwatchView extends SquareView implements ColorObserver {
|
||||
}
|
||||
|
||||
private static void cornerArc(Path path, float cx, float cy, float r, float startAngle, float sweepAngle) {
|
||||
final RectF ovalRect = new RectF(-r, -r, r, r);
|
||||
RectF ovalRect = new RectF(-r, -r, r, r);
|
||||
ovalRect.offset(cx, cy);
|
||||
path.arcTo(ovalRect, startAngle, sweepAngle);
|
||||
}
|
||||
|
||||
private static void mainArc(Path path, float viewSize, float margin, float startAngle, float sweepAngle) {
|
||||
float r = viewSize + margin;
|
||||
final RectF ovalRect = new RectF(-r, -r, r, r);
|
||||
RectF ovalRect = new RectF(-r, -r, r, r);
|
||||
ovalRect.offset(viewSize, viewSize);
|
||||
path.arcTo(ovalRect, startAngle, sweepAngle);
|
||||
}
|
||||
@@ -77,23 +76,23 @@ public class SwatchView extends SquareView implements ColorObserver {
|
||||
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||
// We expect to be stacked behind a square HueSatView and fill the top left corner.
|
||||
|
||||
final float inset = borderPaint.getStrokeWidth() / 2;
|
||||
final float r = Math.min(w, h);
|
||||
float inset = borderPaint.getStrokeWidth() / 2;
|
||||
float r = Math.min(w, h);
|
||||
|
||||
// Trim the corners of the swatch is where it is radialMarginPx thick.
|
||||
// find how long the outeredges are:
|
||||
final float margin = radialMarginPx;
|
||||
final float diagonal = r + 2 * margin;
|
||||
final float opp = (float) Math.sqrt(diagonal * diagonal - r * r);
|
||||
final float edgeLen = r - opp;
|
||||
float margin = radialMarginPx;
|
||||
float diagonal = r + 2 * margin;
|
||||
float opp = (float) Math.sqrt(diagonal * diagonal - r * r);
|
||||
float edgeLen = r - opp;
|
||||
|
||||
// Arc angles for drawing CCW
|
||||
final float outerAngle = (float) Math.toDegrees(Math.atan2(opp, r));
|
||||
final float startAngle = 270 - outerAngle;
|
||||
float outerAngle = (float) Math.toDegrees(Math.atan2(opp, r));
|
||||
float startAngle = 270 - outerAngle;
|
||||
// Sweep angle for each half of the swatch:
|
||||
final float sweepAngle = outerAngle - 45;
|
||||
float sweepAngle = outerAngle - 45;
|
||||
// Sweep angle for the smooth corners:
|
||||
final float cornerSweepAngle = 90 - outerAngle;
|
||||
float cornerSweepAngle = 90 - outerAngle;
|
||||
|
||||
// Outer border:
|
||||
beginBorder(borderPath, inset, edgeLen, margin, cornerSweepAngle);
|
||||
|
||||
@@ -5,7 +5,6 @@ import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
|
||||
public class ValueView extends SliderViewBase implements ColorObserver {
|
||||
|
||||
private ObservableColor observableColor = new ObservableColor(0);
|
||||
|
||||
public ValueView(Context context) {
|
||||
@@ -37,8 +36,8 @@ public class ValueView extends SliderViewBase implements ColorObserver {
|
||||
}
|
||||
|
||||
protected Bitmap makeBitmap(int w, int h) {
|
||||
final boolean isWide = w > h;
|
||||
final int n = Math.max(w, h);
|
||||
boolean isWide = w > h;
|
||||
int n = Math.max(w, h);
|
||||
int[] colors = new int[n];
|
||||
|
||||
float[] hsv = new float[]{0, 0, 0};
|
||||
@@ -48,8 +47,8 @@ public class ValueView extends SliderViewBase implements ColorObserver {
|
||||
hsv[2] = isWide ? (float) i / n : 1 - (float) i / n;
|
||||
colors[i] = Color.HSVToColor(hsv);
|
||||
}
|
||||
final int bmpWidth = isWide ? w : 1;
|
||||
final int bmpHeight = isWide ? 1 : h;
|
||||
int bmpWidth = isWide ? w : 1;
|
||||
int bmpHeight = isWide ? 1 : h;
|
||||
return Bitmap.createBitmap(colors, bmpWidth, bmpHeight, Bitmap.Config.ARGB_8888);
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ public class AndroidUtils {
|
||||
@NonNull
|
||||
public static Context getGlobalContext() {
|
||||
try {
|
||||
final Method getInitialApplicationMtd = ReflectionUtils.findMethod(Class.forName("android.app.AppGlobals"), "getInitialApplication");
|
||||
Method getInitialApplicationMtd = ReflectionUtils.findMethod(Class.forName("android.app.AppGlobals"), "getInitialApplication");
|
||||
return (Context) getInitialApplicationMtd.invoke(null);
|
||||
} catch (Exception e) {
|
||||
Log.d("GlobalContext", "Error while fetching context via refl");
|
||||
@@ -186,8 +186,8 @@ public class AndroidUtils {
|
||||
}
|
||||
|
||||
private static String getRandomString(int sizeOfRandomString) {
|
||||
final Random random = new Random();
|
||||
final StringBuilder sb = new StringBuilder(sizeOfRandomString);
|
||||
Random random = new Random();
|
||||
StringBuilder sb = new StringBuilder(sizeOfRandomString);
|
||||
for (int i = 0; i < sizeOfRandomString; ++i)
|
||||
sb.append(ALLOWED_CHARACTERS.charAt(random.nextInt(ALLOWED_CHARACTERS.length())));
|
||||
return sb.toString();
|
||||
@@ -221,7 +221,6 @@ public class AndroidUtils {
|
||||
long lastUpdateTime = context.getPackageManager().getPackageInfo(context.getPackageName(), 0).lastUpdateTime;
|
||||
return firstInstallTime == lastUpdateTime;
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ public class CrashReporter {
|
||||
}
|
||||
|
||||
public static void init(Activity activity) {
|
||||
final var defaultUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
|
||||
var defaultUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
|
||||
Thread.setDefaultUncaughtExceptionHandler((thread, th) -> CrashReporter.start(defaultUncaughtExceptionHandler, thread, th, activity));
|
||||
}
|
||||
|
||||
@@ -76,9 +76,8 @@ public class CrashReporter {
|
||||
}
|
||||
|
||||
private static boolean isFoxbinInstalled() {
|
||||
var pm = getGlobalContext().getPackageManager();
|
||||
try {
|
||||
pm.getPackageInfo("com.f0x1d.dogbin", 0);
|
||||
getGlobalContext().getPackageManager().getPackageInfo("com.f0x1d.dogbin", 0);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
|
||||
@@ -16,7 +16,7 @@ import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class IOUtils {
|
||||
public static final int BUFFER_SIZE = 8192;
|
||||
public static int BUFFER_SIZE = 8192;
|
||||
|
||||
public static byte[] decodeStream(InputStream encIs, byte[] aesKey)
|
||||
throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, IOException {
|
||||
@@ -51,11 +51,11 @@ public class IOUtils {
|
||||
return new String(readFully(is), StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
public static byte[] readFully(final InputStream is)
|
||||
public static byte[] readFully(InputStream is)
|
||||
throws IOException {
|
||||
final var bos = new ByteArrayOutputStream();
|
||||
var bos = new ByteArrayOutputStream();
|
||||
try (bos) {
|
||||
final byte[] buff = new byte[BUFFER_SIZE];
|
||||
byte[] buff = new byte[BUFFER_SIZE];
|
||||
int len;
|
||||
while ((len = is.read(buff)) > 0)
|
||||
bos.write(buff, 0, len);
|
||||
@@ -63,7 +63,7 @@ public class IOUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] readFully(final File in)
|
||||
public static byte[] readFully(File in)
|
||||
throws IOException {
|
||||
return readFully(new FileInputStream(in));
|
||||
}
|
||||
@@ -80,25 +80,25 @@ public class IOUtils {
|
||||
fos.close();
|
||||
}
|
||||
|
||||
public static void copy(final InputStream is, final OutputStream os)
|
||||
public static void copy(InputStream is, OutputStream os)
|
||||
throws IOException {
|
||||
final byte[] buff = new byte[BUFFER_SIZE];
|
||||
byte[] buff = new byte[BUFFER_SIZE];
|
||||
int len;
|
||||
while ((len = is.read(buff)) > 0)
|
||||
os.write(buff, 0, len);
|
||||
}
|
||||
|
||||
public static void copy(final byte[] buffer, final File out)
|
||||
public static void copy(byte[] buffer, File out)
|
||||
throws IOException {
|
||||
copy(new ByteArrayInputStream(buffer), new FileOutputStream(out));
|
||||
}
|
||||
|
||||
public static void copy(final InputStream is, final File out)
|
||||
public static void copy(InputStream is, File out)
|
||||
throws IOException {
|
||||
copy(is, new FileOutputStream(out));
|
||||
}
|
||||
|
||||
public static void copy(final File in, final File out)
|
||||
public static void copy(File in, File out)
|
||||
throws IOException {
|
||||
copy(new FileInputStream(in), new FileOutputStream(out));
|
||||
}
|
||||
@@ -119,7 +119,7 @@ public class IOUtils {
|
||||
}
|
||||
|
||||
public static long getDirSize(File dir) {
|
||||
final var arr = dir.listFiles();
|
||||
var arr = dir.listFiles();
|
||||
if (arr == null || arr.length == 0)
|
||||
return 0;
|
||||
|
||||
|
||||
@@ -242,13 +242,13 @@ public class NewsFeedFiltersUtils {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (checkCopyright(post) && !isWhitelistedFilters(post)) {
|
||||
if (checkCopyright(post)) {
|
||||
if (dev())
|
||||
Log.d("NewsfeedAdBlockV2", "Removed post " + post.optInt("post_id") + " from discover, Reason: copyright filters");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (checkCaption(post) && !isWhitelistedFilters(post)) {
|
||||
if (checkCaption(post)) {
|
||||
if (dev())
|
||||
Log.d("NewsfeedAdBlockV2", "Removed post " + post.optInt("post_id") + " from discover, Reason: caption filters");
|
||||
return false;
|
||||
@@ -448,13 +448,13 @@ public class NewsFeedFiltersUtils {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (checkCopyright(list) && !isWhitelistedFilters(list)) {
|
||||
if (checkCopyright(list)) {
|
||||
if (dev())
|
||||
Log.d("NewsfeedAdBlockV2", "Removed post " + list.optInt("post_id") + " from feed, Reason: copyright filters");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (checkCaption(list) && !isWhitelistedFilters(list)) {
|
||||
if (checkCaption(list)) {
|
||||
if (dev())
|
||||
Log.d("NewsfeedAdBlockV2", "Removed post " + list.optInt("post_id") + " from feed, Reason: caption filters");
|
||||
continue;
|
||||
|
||||
@@ -17,6 +17,9 @@ import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
import static android.os.Environment.DIRECTORY_DOWNLOADS;
|
||||
import static android.os.Environment.getExternalStoragePublicDirectory;
|
||||
|
||||
public class VKAccountDB {
|
||||
private static SQLiteOpenHelper getDatabase() {
|
||||
return new UsersDbHelper(AndroidUtils.getGlobalContext());
|
||||
@@ -70,11 +73,11 @@ public class VKAccountDB {
|
||||
|
||||
var db = getDatabase().getReadableDatabase();
|
||||
var dateFormat = new SimpleDateFormat("yyyy-MM-dd.HH-mm-ss", Locale.getDefault());
|
||||
var file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + "/VTLBackups/saved_accounts_" + dateFormat.format(new Date()) + ".vtl");
|
||||
var dir = new File(getExternalStoragePublicDirectory(DIRECTORY_DOWNLOADS), "/VTLBackups/");
|
||||
var file = new File(dir, "saved_accounts_" + dateFormat.format(new Date()) + ".vtl");
|
||||
|
||||
InputStream inputStream;
|
||||
try {
|
||||
inputStream = new FileInputStream(db.getPath());
|
||||
try (InputStream inputStream = new FileInputStream(db.getPath())){
|
||||
dir.mkdirs();
|
||||
|
||||
FileOutputStream outputStream = new FileOutputStream(file);
|
||||
byte[] buff = new byte[1024];
|
||||
@@ -86,7 +89,6 @@ public class VKAccountDB {
|
||||
|
||||
AndroidUtils.sendToast(AndroidUtils.getString("accounts_saved_by_path") + " " + file.getPath());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
AndroidUtils.sendToast(AndroidUtils.getString("error_saving_file"));
|
||||
}
|
||||
}
|
||||
|
||||
+20
-20
@@ -222,41 +222,41 @@
|
||||
|
||||
.line 14
|
||||
:cond_0
|
||||
new-instance v0, Lcom/vk/api/audio/AudioSendStartEvent;
|
||||
# new-instance v0, Lcom/vk/api/audio/AudioSendStartEvent;
|
||||
|
||||
invoke-virtual {p1}, Lcom/vk/dto/music/MusicTrack;->y1()Ljava/lang/String;
|
||||
# invoke-virtual {p1}, Lcom/vk/dto/music/MusicTrack;->y1()Ljava/lang/String;
|
||||
|
||||
move-result-object p1
|
||||
# move-result-object p1
|
||||
|
||||
sget-object v2, Lcom/vk/api/base/ApiConfig;->d:Lcom/vk/api/base/ApiConfig$a;
|
||||
# sget-object v2, Lcom/vk/api/base/ApiConfig;->d:Lcom/vk/api/base/ApiConfig$a;
|
||||
|
||||
invoke-interface {v2}, Lcom/vk/api/base/ApiConfig$a;->O1()Ljava/lang/String;
|
||||
# invoke-interface {v2}, Lcom/vk/api/base/ApiConfig$a;->O1()Ljava/lang/String;
|
||||
|
||||
move-result-object v2
|
||||
# move-result-object v2
|
||||
|
||||
invoke-direct {v0, p1, v2}, Lcom/vk/api/audio/AudioSendStartEvent;-><init>(Ljava/lang/String;Ljava/lang/String;)V
|
||||
# invoke-direct {v0, p1, v2}, Lcom/vk/api/audio/AudioSendStartEvent;-><init>(Ljava/lang/String;Ljava/lang/String;)V
|
||||
|
||||
const/4 p1, 0x0
|
||||
# const/4 p1, 0x0
|
||||
|
||||
.line 15
|
||||
invoke-static {v0, p1, v1, p1}, Lcom/vk/api/base/ApiRequest;->d(Lcom/vk/api/base/ApiRequest;Lcom/vk/api/base/ApiThreadHolder;ILjava/lang/Object;)Lio/reactivex/Observable;
|
||||
# .line 15
|
||||
# invoke-static {v0, p1, v1, p1}, Lcom/vk/api/base/ApiRequest;->d(Lcom/vk/api/base/ApiRequest;Lcom/vk/api/base/ApiThreadHolder;ILjava/lang/Object;)Lio/reactivex/Observable;
|
||||
|
||||
move-result-object p1
|
||||
# move-result-object p1
|
||||
|
||||
.line 16
|
||||
new-instance v0, Lcom/vk/music/notifications/restriction/MusicRestrictionModelImpl$d;
|
||||
# .line 16
|
||||
# new-instance v0, Lcom/vk/music/notifications/restriction/MusicRestrictionModelImpl$d;
|
||||
|
||||
invoke-direct {v0, p0}, Lcom/vk/music/notifications/restriction/MusicRestrictionModelImpl$d;-><init>(Lcom/vk/music/notifications/restriction/MusicRestrictionModelImpl;)V
|
||||
# invoke-direct {v0, p0}, Lcom/vk/music/notifications/restriction/MusicRestrictionModelImpl$d;-><init>(Lcom/vk/music/notifications/restriction/MusicRestrictionModelImpl;)V
|
||||
|
||||
.line 17
|
||||
sget-object v1, Lcom/vk/music/notifications/restriction/MusicRestrictionModelImpl$e;->a:Lcom/vk/music/notifications/restriction/MusicRestrictionModelImpl$e;
|
||||
# .line 17
|
||||
# sget-object v1, Lcom/vk/music/notifications/restriction/MusicRestrictionModelImpl$e;->a:Lcom/vk/music/notifications/restriction/MusicRestrictionModelImpl$e;
|
||||
|
||||
.line 18
|
||||
invoke-virtual {p1, v0, v1}, Lio/reactivex/Observable;->a(Lio/reactivex/functions/Consumer;Lio/reactivex/functions/Consumer;)Lio/reactivex/disposables/Disposable;
|
||||
# .line 18
|
||||
# invoke-virtual {p1, v0, v1}, Lio/reactivex/Observable;->a(Lio/reactivex/functions/Consumer;Lio/reactivex/functions/Consumer;)Lio/reactivex/disposables/Disposable;
|
||||
|
||||
move-result-object p1
|
||||
# move-result-object p1
|
||||
|
||||
iput-object p1, p0, Lcom/vk/music/notifications/restriction/MusicRestrictionModelImpl;->a:Lio/reactivex/disposables/Disposable;
|
||||
# iput-object p1, p0, Lcom/vk/music/notifications/restriction/MusicRestrictionModelImpl;->a:Lio/reactivex/disposables/Disposable;
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
Reference in New Issue
Block a user