Compare commits

..
Author SHA1 Message Date
gdlbo 6eb88e678f ok 2024-03-01 23:46:19 +03:00
gdlbo 4aee200214 init 2024-02-23 02:41:00 +03:00
351 changed files with 13276 additions and 24842 deletions
+2 -11
View File
@@ -21,21 +21,14 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up NDK
uses: nttld/setup-ndk@v1
with:
ndk-version: r25c
add-to-path: true
- name: Set up JDK - name: Set up JDK
uses: actions/setup-java@v3 uses: actions/setup-java@v3
with: with:
java-version: '17' java-version: '17'
distribution: 'adopt' distribution: 'adopt'
- uses: actions/checkout@v3
- name: Download tools - name: Download tools
run: | run: |
sudo apt update sudo apt update
@@ -54,8 +47,6 @@ jobs:
java -jar scripts/apktool.jar b smali -o smali/VTLite.apk --use-aapt2 -p scripts/framework/ java -jar scripts/apktool.jar b smali -o smali/VTLite.apk --use-aapt2 -p scripts/framework/
zipalign -p -f 4 smali/VTLite.apk smali/VTLite.apk_aligned zipalign -p -f 4 smali/VTLite.apk smali/VTLite.apk_aligned
mv smali/VTLite.apk_aligned smali/VTLite.apk mv smali/VTLite.apk_aligned smali/VTLite.apk
env:
ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
- name: Sign and verify the apk - name: Sign and verify the apk
run: | run: |
+3 -5
View File
@@ -4,7 +4,7 @@ import java.util.zip.ZipFile
plugins { plugins {
id 'com.android.application' id 'com.android.application'
id 'net.nemerosa.versioning' version '3.1.0' id 'net.nemerosa.versioning' version '2.15.1'
id 'org.mozilla.rust-android-gradle.rust-android' id 'org.mozilla.rust-android-gradle.rust-android'
} }
@@ -78,7 +78,7 @@ android {
targetCompatibility JavaVersion.VERSION_14 targetCompatibility JavaVersion.VERSION_14
} }
namespace 'ru.vtosters.lite' namespace 'ru.vtosters.lite'
ndkVersion '25.2.9519653' ndkVersion '26.1.10909125'
buildFeatures { buildFeatures {
aidl true aidl true
} }
@@ -95,9 +95,7 @@ dependencies {
implementation 'com.microsoft.appcenter:appcenter-crashes:5.0.4' implementation 'com.microsoft.appcenter:appcenter-crashes:5.0.4'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4' coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4'
implementation 'net.sourceforge.streamsupport:streamsupport-minifuture:1.7.4' implementation 'net.sourceforge.streamsupport:streamsupport-minifuture:1.7.4'
implementation 'com.google.guava:guava:33.2.1-android' implementation 'com.google.guava:guava:32.0.0-android'
implementation 'com.mpatric:mp3agic:0.9.1'
implementation fileTree(dir: 'libs', include: ['*.aar']) implementation fileTree(dir: 'libs', include: ['*.aar'])
compileOnly fileTree(dir: 'libs', include: ['*.jar']) compileOnly fileTree(dir: 'libs', include: ['*.jar'])
Binary file not shown.
+1 -1
View File
@@ -35,7 +35,7 @@
-optimizationpasses 5 -optimizationpasses 5
-keepattributes SourceFile, LineNumberTable -keepattributes SourceFile, LineNumberTable
-keep,allowoptimization class ru.vtosters.hooks.**, ru.vtosters.sponsorpost.internal.Native, ru.vtosters.lite.utils.AndroidUtils, ru.vtosters.lite.utils.NetworkUtils, ru.vtosters.lite.utils.CrashReporter, ru.vtosters.lite.ui.activities.**, ru.vtosters.lite.services.**, ru.vtosters.lite.ui.fragments.**, ru.vtosters.lite.themes.**, ru.vtosters.lite.music.hook.**, ru.vtosters.lite.music.cache.injectors.**, ru.vtosters.lite.ui.wallpapers.NativeEffects, com.aefyr.tsg.g2.TelegramStickersService { -keep,allowoptimization class ru.vtosters.hooks.**, ru.vtosters.lite.utils.AndroidUtils, ru.vtosters.lite.utils.NetworkUtils, ru.vtosters.lite.utils.CrashReporter, ru.vtosters.lite.ui.activities.**, ru.vtosters.lite.services.**, ru.vtosters.lite.ui.fragments.**, ru.vtosters.lite.themes.**, ru.vtosters.lite.music.hook.**, ru.vtosters.lite.music.cache.injectors.**, ru.vtosters.lite.ui.wallpapers.NativeEffects, com.aefyr.tsg.g2.TelegramStickersService {
public <fields>; public <fields>;
public <methods>; public <methods>;
public <init>(...); public <init>(...);
+124
View File
@@ -0,0 +1,124 @@
package com.arthenica.ffmpegkit;
import android.os.Build;
import android.util.Log;
import com.arthenica.smartexception.java.Exceptions;
import java.util.Collections;
import java.util.List;
public class NativeLoader {
static final String[] FFMPEG_LIBRARIES = {"avutil", "swresample", "avcodec", "avformat", "avfilter", "avdevice"};
private static void loadLibrary(String libraryName) {
System.loadLibrary(libraryName);
}
private static List<String> loadExternalLibraries() {
//return Packages.getExternalLibraries();
return Collections.emptyList();
}
private static String loadNativeAbi() {
return AbiDetect.getNativeAbi();
}
static String loadAbi() {
return AbiDetect.getAbi();
}
static String loadPackageName() {
return Packages.getPackageName();
}
static String loadVersion() {
return FFmpegKitConfig.getVersion();
}
static boolean loadIsLTSBuild() {
return AbiDetect.isNativeLTSBuild();
}
static int loadLogLevel() {
return FFmpegKitConfig.getNativeLogLevel();
}
static String loadBuildDate() {
return FFmpegKitConfig.getBuildDate();
}
static void enableRedirection() {
FFmpegKitConfig.enableRedirection();
}
static void loadFFmpegKitAbiDetect() {
loadLibrary("ffmpegkit_abidetect");
}
static boolean loadFFmpeg() {
boolean nativeFFmpegLoaded = false;
boolean nativeFFmpegTriedAndFailed = false;
if (AbiDetect.ARM_V7A.equals(loadNativeAbi())) {
try {
for (String ffmpegLibrary : FFMPEG_LIBRARIES) {
loadLibrary(ffmpegLibrary + "_neon");
}
nativeFFmpegLoaded = true;
} catch (final Error e) {
android.util.Log.i(FFmpegKitConfig.TAG, String.format("NEON supported armeabi-v7a ffmpeg library not found. Loading default armeabi-v7a library.%s", Exceptions.getStackTraceString(e)));
nativeFFmpegTriedAndFailed = true;
}
}
if (!nativeFFmpegLoaded) {
for (String ffmpegLibrary : FFMPEG_LIBRARIES) {
loadLibrary(ffmpegLibrary);
}
}
return nativeFFmpegTriedAndFailed;
}
static void loadFFmpegKit(boolean nativeFFmpegTriedAndFailed) {
boolean nativeFFmpegKitLoaded = false;
if (!nativeFFmpegTriedAndFailed && "arm-v7a".equals(loadNativeAbi())) {
try {
loadLibrary("ffmpegkit_armv7a_neon");
nativeFFmpegKitLoaded = true;
AbiDetect.setArmV7aNeonLoaded();
} catch (Error e) {
Log.i("ffmpeg-kit", String.format("NEON supported armeabi-v7a ffmpegkit library not found. Loading default armeabi-v7a library.%s", Exceptions.getStackTraceString(e)));
}
}
if (!nativeFFmpegKitLoaded) {
loadLibrary("ffmpegkit");
}
}
static String getDeviceDebugInformation() {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("brand: ");
stringBuilder.append(Build.BRAND);
stringBuilder.append(", model: ");
stringBuilder.append(Build.MODEL);
stringBuilder.append(", device: ");
stringBuilder.append(Build.DEVICE);
stringBuilder.append(", api level: ");
stringBuilder.append(Build.VERSION.SDK_INT);
if (Build.VERSION.SDK_INT >= 21) {
stringBuilder.append(", abis: ");
stringBuilder.append(FFmpegKitConfig.argumentsToString(Build.SUPPORTED_ABIS));
stringBuilder.append(", 32bit abis: ");
stringBuilder.append(FFmpegKitConfig.argumentsToString(Build.SUPPORTED_32_BIT_ABIS));
stringBuilder.append(", 64bit abis: ");
stringBuilder.append(FFmpegKitConfig.argumentsToString(Build.SUPPORTED_64_BIT_ABIS));
} else {
stringBuilder.append(", cpu abis: ");
stringBuilder.append(Build.CPU_ABI);
stringBuilder.append(", cpu abi2s: ");
stringBuilder.append(Build.CPU_ABI2);
}
return stringBuilder.toString();
}
}
@@ -0,0 +1,732 @@
/*
* BSD 3-Clause License
*
* Copyright (c) 2020, Taner Sener
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.arthenica.smartexception;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
/**
* <p>Abstract class that includes common variables and methods for different <code>Exceptions</code> implementations.
*
* @author Taner Sener
* @since 0.1.0
*/
public abstract class AbstractExceptions {
/**
* <p>Default max depth used in methods which do not have a maxDepth argument.
*/
public static final int DEFAULT_MAX_DEPTH = 10;
/**
* <p>Default value for ignoring all causes when stack trace elements are printed or converted to string.
*/
public static final boolean DEFAULT_IGNORE_ALL_CAUSES = false;
/**
* <p>Stores global root package names.
*/
public static final Set<String> rootPackageSet = Collections.synchronizedSet(new HashSet<String>());
/**
* <p>Stores global group package names.
*/
public static final Set<String> groupPackageSet = Collections.synchronizedSet(new HashSet<String>());
/**
* <p>Stores global ignore package names.
*/
public static final Set<String> ignorePackageSet = Collections.synchronizedSet(new HashSet<String>());
/**
* <p>Stores global ignore cause package names.
*/
public static final Set<String> ignoreCausePackageSet = Collections.synchronizedSet(new HashSet<String>());
/**
* <p>Stores the value of global ignore all causes option.
*/
public static boolean ignoreAllCauses = DEFAULT_IGNORE_ALL_CAUSES;
/**
* <p>Stores the global stack trace serializer implementation.
*/
public static StackTraceElementSerializer stackTraceElementSerializer;
/**
* <p>Registers a new root package.
*
* @param packageString root package name to register
*/
public static void registerRootPackage(final String packageString) {
rootPackageSet.add(packageString);
}
/**
* <p>Clears previously registered root packages.
*/
public static void clearRootPackages() {
rootPackageSet.clear();
}
/**
* <p>Registers a new group package.
*
* @param packageString group package name to register
*/
public static void registerGroupPackage(final String packageString) {
groupPackageSet.add(packageString);
}
/**
* <p>Clears previously registered group packages.
*/
public static void clearGroupPackages() {
groupPackageSet.clear();
}
/**
* <p>Returns the global stack trace serializer implementation which is used to serialize {@link StackTraceElement}
* objects in <code>getStackTraceString</code> methods.
*
* @return current stack trace serializer implementation
*/
public static StackTraceElementSerializer getStackTraceElementSerializer() {
return stackTraceElementSerializer;
}
/**
* <p>Sets the global stack trace serializer implementation which is used to serialize {@link StackTraceElement}
* objects in <code>getStackTraceString</code> methods.
*
* @param stackTraceElementSerializer new stack trace serializer implementation
*/
public static void setStackTraceElementSerializer(final StackTraceElementSerializer stackTraceElementSerializer) {
AbstractExceptions.stackTraceElementSerializer = stackTraceElementSerializer;
}
/**
* <p>Registers a new ignore package.
*
* @param packageString ignore package name to register
* @param ignoreCauseClasses ignore cause classes from this package too
*/
public static void registerIgnorePackage(final String packageString, final boolean ignoreCauseClasses) {
ignorePackageSet.add(packageString);
if (ignoreCauseClasses) {
ignoreCausePackageSet.add(packageString);
}
}
/**
* <p>Clears previously registered ignore packages.
*/
public static void clearIgnorePackages() {
ignorePackageSet.clear();
ignoreCausePackageSet.clear();
}
/**
* <p>Returns the value of ignore all causes option.
*
* @return the value of global ignore all causes option. If value is true then stack trace elements printed or
* converted to string will not include causes. If value is false causes will be appended to the stack trace of
* the main throwable.
*/
public static boolean getIgnoreAllCauses() {
return ignoreAllCauses;
}
/**
* <p>Sets the value of ignore all causes option.
*
* @param ignoreAllCauses new global ignore all causes option. If value is true then stack trace elements printed
* or converted to string will not include causes. If value is false causes will be appended to the stack trace of
* the main throwable.
*/
public static void setIgnoreAllCauses(final boolean ignoreAllCauses) {
AbstractExceptions.ignoreAllCauses = ignoreAllCauses;
}
/**
* <p>Returns the smart stack trace for the given <code>throwable</code>.
*
* <p>This method uses root packages registered by {@link #registerRootPackage(String)}, group packages registered
* by {@link #registerGroupPackage(String)} and ignore packages registered by
* {@link #registerIgnorePackage(String, boolean)} to build the smart stack trace.
*
* @param throwable parent throwable
* @return a string containing the smart stack trace for the given <code>throwable</code>
*/
public static String getStackTraceString(final Throwable throwable) {
return getStackTraceString(throwable, false, rootPackageSet, groupPackageSet, ignorePackageSet, 0, ignoreAllCauses);
}
/**
* <p>Returns the smart stack trace for the given <code>throwable</code>.
*
* <p>This method uses root packages registered by {@link #registerRootPackage(String)}, group packages registered
* by {@link #registerGroupPackage(String)} and ignore packages registered by
* {@link #registerIgnorePackage(String, boolean)} to build the smart stack trace.
*
* @param throwable parent throwable
* @param ignoreAllCauses ignore all causes in the exception chain
* @return a string containing the smart stack trace for the given <code>throwable</code>
*/
public static String getStackTraceString(final Throwable throwable, final boolean ignoreAllCauses) {
return getStackTraceString(throwable, false, rootPackageSet, groupPackageSet, ignorePackageSet, 0, ignoreAllCauses);
}
/**
* <p>Returns the smart stack trace for the given <code>throwable</code> using packages provided.
*
* @param throwable parent throwable
* @param rootPackageSet root packages to use for building the stack trace
* @param groupPackageSet group packages to use for building the stack trace
* @param ignorePackageSet ignore packages to use for building the stack trace
* @return a string containing the smart stack trace for the given <code>throwable</code>
*/
public static String getStackTraceString(final Throwable throwable, final Set<String> rootPackageSet, final Set<String> groupPackageSet, final Set<String> ignorePackageSet) {
return getStackTraceString(throwable, false, rootPackageSet, groupPackageSet, ignorePackageSet, 0, ignoreAllCauses);
}
/**
* <p>Returns the smart stack trace for the given <code>throwable</code> using packages provided.
*
* @param throwable parent throwable
* @param rootPackageSet root packages to use for building the stack trace
* @param groupPackageSet group packages to use for building the stack trace
* @param ignorePackageSet ignore packages to use for building the stack trace
* @param ignoreAllCauses ignore all causes in the exception chain
* @return a string containing the smart stack trace for the given <code>throwable</code>
*/
public static String getStackTraceString(final Throwable throwable, final Set<String> rootPackageSet, final Set<String> groupPackageSet, final Set<String> ignorePackageSet, final boolean ignoreAllCauses) {
return getStackTraceString(throwable, false, rootPackageSet, groupPackageSet, ignorePackageSet, 0, ignoreAllCauses);
}
/**
* <p>Returns the smart stack trace for the given <code>throwable</code> using root package provided.
*
* @param throwable parent throwable
* @param rootPackage root package to use for building the stack trace
* @return a string containing the smart stack trace for the given <code>throwable</code>
*/
public static String getStackTraceString(final Throwable throwable, final String rootPackage) {
return getStackTraceString(throwable, false, Collections.singleton(rootPackage), new HashSet<String>(), new HashSet<String>(), 0, ignoreAllCauses);
}
/**
* <p>Returns the smart stack trace for the given <code>throwable</code> using root package provided.
*
* @param throwable parent throwable
* @param rootPackage root package to use for building the stack trace
* @param groupPackage group package to use for building the stack trace
* @return a string containing the smart stack trace for the given <code>throwable</code>
*/
public static String getStackTraceString(final Throwable throwable, final String rootPackage, final String groupPackage) {
return getStackTraceString(throwable, false, Collections.singleton(rootPackage), Collections.singleton(groupPackage), new HashSet<String>(), 0, ignoreAllCauses);
}
/**
* <p>Returns the smart stack trace for the given <code>throwable</code> using elements found until the maxDepth.
*
* @param throwable parent throwable
* @param maxDepth max depth in exception chain that will be used
* @return a string containing the smart stack trace for the given <code>throwable</code>
*/
public static String getStackTraceString(final Throwable throwable, final int maxDepth) {
return getStackTraceString(throwable, false, new HashSet<String>(), new HashSet<String>(), new HashSet<String>(), maxDepth, ignoreAllCauses);
}
/**
* <p>Returns the smart stack trace for the given <code>throwable</code> using elements found until the maxDepth.
*
* @param throwable parent throwable
* @param maxDepth max depth in exception chain that will be used
* @param ignoreAllCauses ignore all causes in the exception chain
* @return a string containing the smart stack trace for the given <code>throwable</code>
*/
public static String getStackTraceString(final Throwable throwable, final int maxDepth, final boolean ignoreAllCauses) {
return getStackTraceString(throwable, false, new HashSet<String>(), new HashSet<String>(), new HashSet<String>(), maxDepth, ignoreAllCauses);
}
/**
* <p>Returns the smart stack trace for the given <code>throwable</code> using parameters provided.
*
* @param throwable parent throwable
* @param isCause throwable is a cause or not
* @param rootPackageSet root packages to use for building the stack trace
* @param groupPackageSet group packages to use for building the stack trace
* @param ignorePackageSet ignore packages to use for building the stack trace
* @param maxDepth max depth in exception chain that will be used
* @param ignoreAllCauses ignore all causes in the exception chain
* @return a string containing the smart stack trace for the given <code>throwable</code>
*/
public static String getStackTraceString(final Throwable throwable, final boolean isCause, final Set<String> rootPackageSet, final Set<String> groupPackageSet, final Set<String> ignorePackageSet, final int maxDepth, final boolean ignoreAllCauses) {
final StringBuilder builder = new StringBuilder();
if (throwable == null) {
return "";
}
final String className = throwable.getClass().getName();
final StackTraceElement[] stackTraceElements;
if (maxDepth > 0) {
stackTraceElements = getStackTrace(throwable, maxDepth);
} else {
stackTraceElements = getStackTrace(throwable, rootPackageSet, ignorePackageSet);
}
String message = throwable.getLocalizedMessage();
if (isEmpty(message)) {
message = throwable.getMessage();
}
/* PRINT MESSAGE */
if (isCause) {
builder.append(System.lineSeparator());
builder.append("Caused by: ");
builder.append(className);
if (!isEmpty(message)) {
builder.append(": ");
builder.append(message);
}
} else {
builder.append(System.lineSeparator());
builder.append(className);
if (!isEmpty(message)) {
builder.append(": ");
builder.append(message);
}
}
/* PRINT SHORT STACK TRACE */
String currentGroupPackage = null;
StackTraceElement firstStackTraceElementInTheGroup = null;
int currentGroupCount = 0;
for (StackTraceElement traceElement : stackTraceElements) {
String traceElementClassName = traceElement.getClassName();
String groupPackageMatch = getContainingPackage(traceElementClassName, groupPackageSet);
if (groupPackageMatch != null) {
if (!groupPackageMatch.equals(currentGroupPackage)) {
appendStackTraceGroupElement(builder, currentGroupPackage, currentGroupCount, firstStackTraceElementInTheGroup);
builder.append(System.lineSeparator());
builder.append("\tat ");
currentGroupPackage = groupPackageMatch;
firstStackTraceElementInTheGroup = traceElement;
currentGroupCount = 1;
} else {
currentGroupCount++;
}
} else {
currentGroupCount = appendStackTraceGroupElement(builder, currentGroupPackage, currentGroupCount, firstStackTraceElementInTheGroup);
builder.append(System.lineSeparator());
builder.append("\tat ");
if (stackTraceElementSerializer == null) {
throw new IllegalArgumentException("Stack trace element serializer not initialized.");
} else {
builder.append(stackTraceElementSerializer.toString(traceElement));
}
currentGroupPackage = null;
}
}
appendStackTraceGroupElement(builder, currentGroupPackage, currentGroupCount, firstStackTraceElementInTheGroup);
final Throwable cause = throwable.getCause();
if (cause != null && !containsPackage(className, ignoreCausePackageSet) && !ignoreAllCauses) {
builder.append(getStackTraceString(cause, true, rootPackageSet, groupPackageSet, ignorePackageSet, maxDepth, ignoreAllCauses));
}
return builder.toString();
}
/**
* <p>Appends stack trace group information for the given parameters.
*
* @param stringBuilder string builder that group stack trace information will be appended
* @param currentGroupPackage package name of the current group
* @param numberOfElementsInTheCurrentGroup number of elements in the current group
* @param firstStackTraceElementInTheGroup first stack trace element of this group
* @return new value for the group element count
*/
public static int appendStackTraceGroupElement(final StringBuilder stringBuilder, final String currentGroupPackage, final int numberOfElementsInTheCurrentGroup, final StackTraceElement firstStackTraceElementInTheGroup) {
if (numberOfElementsInTheCurrentGroup > 0) {
if (stackTraceElementSerializer == null) {
throw new IllegalArgumentException("Stack trace element serializer not initialized.");
} else {
stringBuilder.append((numberOfElementsInTheCurrentGroup == 1) ? stackTraceElementSerializer.toString(firstStackTraceElementInTheGroup) : String.format("%s%s ... %d more", stackTraceElementSerializer.getModuleName(firstStackTraceElementInTheGroup), currentGroupPackage, (numberOfElementsInTheCurrentGroup - 1)));
}
}
return 0;
}
/**
* <p>Returns true if package of the given class name matches one of package names provided in
* <code>packageSet</code>.
*
* @param fullClassName fully qualified class name
* @param packageSet set containing package names
* @return true if package of the given class name matches one of package names provided in <code>packageSet</code>,
* false otherwise
*/
public static boolean containsPackage(final String fullClassName, final Set<String> packageSet) {
return getContainingPackage(fullClassName, packageSet) != null;
}
/**
* <p>Checks if one of the package names in the <code>packageSet</code> matches the package of the given class name
* and returns the matching package name.
*
* @param fullClassName fully qualified class name
* @param packageSet set containing package names
* @return one of the package names in the <code>packageSet</code> that matches the package of the given class name
* or null if no matching package name is found in the <code>packageSet</code>
*/
public static String getContainingPackage(final String fullClassName, final Set<String> packageSet) {
for (final String parentExceptionPackage : packageSet) {
if (fullClassName.startsWith(parentExceptionPackage)) {
return parentExceptionPackage;
}
}
return null;
}
/**
* <p>Returns all messages found in the exception chain of the <code>throwable</code> as a single string.
*
* @param throwable parent throwable
* @return a string containing all messages found in the exception chain
*/
public static String getAllMessages(final Throwable throwable) {
final StringBuilder messageBuilder = new StringBuilder();
getAllMessages(throwable, messageBuilder);
return messageBuilder.toString();
}
/**
* <p>Adds all messages found in the exception chain of the <code>throwable</code> into the given message builder.
*
* @param throwable parent throwable
* @param messageBuilder message builder
*/
public static void getAllMessages(final Throwable throwable, final StringBuilder messageBuilder) {
if (throwable != null) {
final String message = throwable.getMessage();
if (!isEmpty(message)) {
if (messageBuilder.length() != 0) {
messageBuilder.append(System.lineSeparator());
messageBuilder.append(" - Caused by: ");
}
messageBuilder.append(message);
}
getAllMessages(throwable.getCause(), messageBuilder);
}
}
/**
* <p>Builds a smart stack trace for the given <code>throwable</code> using elements found until the maxDepth and
* elements of it.
*
* @param throwable parent throwable
* @param maxDepth max depth in exception chain that will be returned
* @return an array containing stack trace elements
*/
public static StackTraceElement[] getStackTrace(final Throwable throwable, final int maxDepth) {
final ArrayList<StackTraceElement> list = new ArrayList<StackTraceElement>();
if (throwable != null) {
StackTraceElement[] stackTrace = throwable.getStackTrace();
for (int i = 0; (i < stackTrace.length) && (i < maxDepth); i++) {
list.add(stackTrace[i]);
}
}
return list.toArray(new StackTraceElement[0]);
}
/**
* <p>Builds a smart stack trace for the given <code>throwable</code> using packages provided and returns elements
* of it.
*
* @param throwable parent throwable
* @param rootPackageSet root packages to use for building the stack trace
* @param ignorePackageSet ignore packages to use for building the stack trace
* @return an array containing stack trace elements
*/
public static StackTraceElement[] getStackTrace(final Throwable throwable, final Set<String> rootPackageSet, final Set<String> ignorePackageSet) {
final ArrayList<StackTraceElement> list = new ArrayList<StackTraceElement>();
final ArrayList<StackTraceElement> partialList = new ArrayList<StackTraceElement>();
if (throwable != null) {
for (StackTraceElement stackTraceElement : throwable.getStackTrace()) {
final String className = stackTraceElement.getClassName();
if (!isEmpty(className)) {
if (containsPackage(className, rootPackageSet)) {
list.addAll(partialList);
list.add(stackTraceElement);
} else if (!containsPackage(className, ignorePackageSet)) {
partialList.add(stackTraceElement);
}
}
}
}
if (list.isEmpty()) {
list.addAll(partialList);
}
return list.toArray(new StackTraceElement[0]);
}
/**
* <p>Returns true if the given cause class is found in the exception chain of the <code>throwable</code>.
* <br>
* <br>
* <p>Search starts from the <code>throwable</code> itself and goes through the exception chain up until
* {@link #DEFAULT_MAX_DEPTH}.
*
* @param throwable parent throwable
* @param causeClass class to search for
* @return true if the given cause class is found in the exception chain of the <code>throwable</code>, false
* otherwise
*/
public static boolean containsCause(final Throwable throwable, final Class<?> causeClass) {
return containsCause(throwable, causeClass, null);
}
/**
* <p>Returns true if the given cause class and cause message is found in the exception chain of the
* <code>throwable</code>.
* <br>
* <br>
* <p>Search starts from the <code>throwable</code> itself and goes through the exception chain up until
* {@link #DEFAULT_MAX_DEPTH}.
*
* @param throwable parent throwable
* @param causeClass class to search for
* @param causeMessage message to search for
* @return true if the given cause class and cause message is found in the exception chain of the
* <code>throwable</code>, false otherwise
*/
public static boolean containsCause(final Throwable throwable, final Class<?> causeClass, final String causeMessage) {
return (searchCause(throwable, causeClass, causeMessage, DEFAULT_MAX_DEPTH) != null);
}
/**
* <p>Returns the cause of the <code>throwable</code> by walking through the exception chain.
*
* @param throwable parent throwable
* @return the cause of the <code>throwable</code> found or null if <code>throwable</code> does not have a cause
*/
public static Throwable getCause(final Throwable throwable) {
return getCause(throwable, DEFAULT_MAX_DEPTH);
}
/**
* <p>Returns the cause of the <code>throwable</code> by walking through the exception chain.
* <br>
* <br>
* <p>Note that this method walks through the exception chain up to given <code>maxDepth</code>. If exception chain
* includes more items than <code>maxDepth</code>, cause found at <code>maxDepth</code> level is returned.
*
* @param throwable parent throwable
* @param maxDepth max depth in exception chain that will be searched
* @return the cause of the <code>throwable</code> found until <code>maxDepth</code> is reached in the exception
* chain or null if <code>throwable</code> does not have a cause
*/
public static Throwable getCause(final Throwable throwable, final int maxDepth) {
if (throwable == null) {
return null;
}
if (maxDepth <= 0) {
return throwable;
}
final Throwable cause = throwable.getCause();
if (cause == null) {
return throwable;
} else {
return getCause(cause, maxDepth - 1);
}
}
/**
* <p>Searches for the given cause class in the exception chain of the <code>throwable</code>.
* <br>
* <br>
* <p>Search starts from the <code>throwable</code> itself and goes through the exception chain up until
* {@link #DEFAULT_MAX_DEPTH}.
*
* @param throwable parent throwable
* @param causeClass class to search for
* @return the <code>throwable</code> found or null if no class in exception chain matches the given cause class
*/
public static Throwable searchCause(final Throwable throwable, final Class<?> causeClass) {
return searchCause(throwable, causeClass, null, DEFAULT_MAX_DEPTH);
}
/**
* <p>Searches for the given cause class and cause message in the exception chain of the <code>throwable</code>.
* <br>
* <br>
* <p>Search starts from the <code>throwable</code> itself and goes through the exception chain up until
* {@link #DEFAULT_MAX_DEPTH}.
*
* @param throwable parent throwable
* @param causeClass class to search for
* @param causeMessage message to search for
* @return the <code>throwable</code> found or null if no class in exception chain matches the given cause class
* and cause message
*/
public static Throwable searchCause(final Throwable throwable, final Class<?> causeClass, final String causeMessage) {
return searchCause(throwable, causeClass, causeMessage, DEFAULT_MAX_DEPTH);
}
/**
* <p>Searches for the given cause class and cause message in the exception chain of the <code>throwable</code>.
* <br>
* <br>
* <p>Search starts from the <code>throwable</code> itself and goes through the exception chain up until the given
* <code>maxDepth</code>.
*
* @param throwable parent throwable
* @param causeClass class to search for
* @param causeMessage message to search for
* @param maxDepth max depth in exception chain that will be searched
* @return the <code>throwable</code> found or null if no class in exception chain matches the given cause class
* and cause message
*/
public static Throwable searchCause(final Throwable throwable, final Class<?> causeClass, final String causeMessage, final int maxDepth) {
if (throwable == null) {
return null;
}
if (isEmpty(causeMessage)) {
if (throwable.getClass().equals(causeClass)) {
return throwable;
}
} else {
if (throwable.getClass().equals(causeClass) && getAllMessages(throwable).toLowerCase().contains(causeMessage.toLowerCase())) {
return throwable;
}
}
if (maxDepth <= 0) {
return null;
}
final Throwable cause = throwable.getCause();
if (cause == null) {
return null;
} else {
return searchCause(cause, causeClass, causeMessage, maxDepth - 1);
}
}
/**
* <p>Searches for the given cause class in the exception chain of the <code>throwable</code>.
* <br>
* <br>
* <p>Search starts from the <code>throwable</code> itself and goes through the exception chain up until the given
* <code>maxDepth</code>.
*
* @param throwable parent throwable
* @param causeClass class to search for
* @param maxDepth max depth in exception chain that will be searched
* @return the <code>throwable</code> found or null if no class in exception chain matches the given cause class
*/
public static Throwable searchCause(final Throwable throwable, final Class<?> causeClass, final int maxDepth) {
if (throwable == null) {
return null;
}
if (throwable.getClass().equals(causeClass)) {
return throwable;
}
if (maxDepth <= 0) {
return null;
}
final Throwable cause = throwable.getCause();
if (cause == null) {
return null;
} else {
return searchCause(cause, causeClass, maxDepth - 1);
}
}
/**
* <p>Checks if given string is empty or not.
*
* @param value string to check
* @return true if given string is null or has zero length after trim(), false otherwise
*/
public static boolean isEmpty(final String value) {
if (value == null) {
return true;
}
return value.trim().length() == 0;
}
/**
* <p>Returns the package name of this class.
*
* @param className fully qualified class name
* @return name of the package
*/
public static String packageName(final String className) {
if (className == null) {
return "";
}
int index = className.lastIndexOf(".");
if (index >= 0) {
return className.substring(0, index);
} else {
return "";
}
}
}
@@ -0,0 +1,74 @@
/*
* BSD 3-Clause License
*
* Copyright (c) 2020, Taner Sener
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.arthenica.smartexception;
/**
* <p>Base interface for classes which can serialize {@link StackTraceElement} objects.
*
* @author Taner Sener
* @since 0.1.0
*/
public interface StackTraceElementSerializer {
/**
* <p>Converts the given <code>stackTraceElement</code> into a string.
*
* @param stackTraceElement stack trace element to convert
* @return string representing the given <code>stackTraceElement</code>
*/
String toString(final StackTraceElement stackTraceElement);
/**
* <p>Returns module name for the given <code>stackTraceElement</code>.
*
* @param stackTraceElement stack trace element to use
* @return module name of the given <code>stackTraceElement</code> or an empty string if implementation does not
* support printing module names
*/
String getModuleName(final StackTraceElement stackTraceElement);
/**
* <p>Returns string definition used to print native methods.
*
* @return string definition used to print native methods
*/
String getNativeMethodDefinition();
/**
* <p>Returns string definition used to print methods with unknown source.
*
* @return string definition used to print methods with unknown source
*/
String getUnknownSourceDefinition();
}
@@ -0,0 +1,423 @@
/*
* BSD 3-Clause License
*
* Copyright (c) 2020, Taner Sener
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.arthenica.smartexception.java;
import com.arthenica.smartexception.AbstractExceptions;
import com.arthenica.smartexception.StackTraceElementSerializer;
import java.util.Set;
/**
* <p>Utility class to handle Throwable objects and stack traces. This implementation is compatible with all Java and
* Android versions.
*
* @author Taner Sener
* @since 0.1.0
*/
public class Exceptions {
static {
AbstractExceptions.setStackTraceElementSerializer(new StackTraceElementSerializer() {
@Override
public String toString(StackTraceElement stackTraceElement) {
final StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(stackTraceElement.getClassName());
stringBuilder.append(".");
stringBuilder.append(stackTraceElement.getMethodName());
if (stackTraceElement.isNativeMethod()) {
stringBuilder.append("(Native Method)");
} else if ((stackTraceElement.getFileName() != null) && (stackTraceElement.getFileName().length() > 0)) {
stringBuilder.append("(");
stringBuilder.append(stackTraceElement.getFileName());
if (stackTraceElement.getLineNumber() >= 0) {
stringBuilder.append(":");
stringBuilder.append(stackTraceElement.getLineNumber());
}
stringBuilder.append(")");
} else {
stringBuilder.append("(Unknown Source)");
}
return stringBuilder.toString();
}
@Override
public String getModuleName(StackTraceElement stackTraceElement) {
return "";
}
@Override
public String getNativeMethodDefinition() {
return "(Native Method)";
}
@Override
public String getUnknownSourceDefinition() {
return "(Unknown Source)";
}
});
}
/**
* <p>Registers a new root package.
*
* @param packageString root package name to register
*/
public static void registerRootPackage(final String packageString) {
AbstractExceptions.registerRootPackage(packageString);
}
/**
* <p>Clears previously registered root packages.
*/
public static void clearRootPackages() {
AbstractExceptions.clearRootPackages();
}
/**
* <p>Registers a new group package.
*
* @param packageString group package name to register
*/
public static void registerGroupPackage(final String packageString) {
AbstractExceptions.registerGroupPackage(packageString);
}
/**
* <p>Clears previously registered group packages.
*/
public static void clearGroupPackages() {
AbstractExceptions.clearGroupPackages();
}
/**
* <p>Returns the global stack trace serializer implementation which is used to serialize {@link StackTraceElement}
* objects in <code>getStackTraceString</code> methods.
*
* @return current stack trace serializer implementation
*/
public static StackTraceElementSerializer getStackTraceElementSerializer() {
return AbstractExceptions.getStackTraceElementSerializer();
}
/**
* <p>Sets the global stack trace serializer implementation which is used to serialize {@link StackTraceElement}
* objects in <code>getStackTraceString</code> methods.
*
* @param stackTraceElementSerializer new stack trace serializer implementation
*/
public static void setStackTraceElementSerializer(final StackTraceElementSerializer stackTraceElementSerializer) {
AbstractExceptions.setStackTraceElementSerializer(stackTraceElementSerializer);
}
/**
* <p>Registers a new ignore package.
*
* @param packageString ignore package name to register
* @param ignoreCauseClasses ignore cause classes from this package too
*/
public static void registerIgnorePackage(final String packageString, final boolean ignoreCauseClasses) {
AbstractExceptions.registerIgnorePackage(packageString, ignoreCauseClasses);
}
/**
* <p>Clears previously registered ignore packages.
*/
public static void clearIgnorePackages() {
AbstractExceptions.clearIgnorePackages();
}
/**
* <p>Returns the value of ignore all causes option.
*
* @return the value of global ignore all causes option. If value is true then stack trace elements printed or
* converted to string will not include causes. If value is false causes will be appended to the stack trace of
* the main throwable.
*/
public static boolean getIgnoreAllCauses() {
return AbstractExceptions.getIgnoreAllCauses();
}
/**
* <p>Sets the value of ignore all causes option.
*
* @param ignoreAllCauses new global ignore all causes option. If value is true then stack trace elements printed
* or converted to string will not include causes. If value is false causes will be appended to the stack trace of
* the main throwable.
*/
public static void setIgnoreAllCauses(final boolean ignoreAllCauses) {
AbstractExceptions.setIgnoreAllCauses(ignoreAllCauses);
}
/**
* <p>Returns the smart stack trace for the given <code>throwable</code>.
*
* <p>This method uses root packages registered by {@link #registerRootPackage(String)}, group packages registered
* by {@link #registerGroupPackage(String)} and ignore packages registered by
* {@link #registerIgnorePackage(String, boolean)} to build the smart stack trace.
*
* @param throwable parent throwable
* @return a string containing the smart stack trace for the given <code>throwable</code>
*/
public static String getStackTraceString(final Throwable throwable) {
return AbstractExceptions.getStackTraceString(throwable);
}
/**
* <p>Returns the smart stack trace for the given <code>throwable</code>.
*
* <p>This method uses root packages registered by {@link #registerRootPackage(String)}, group packages registered
* by {@link #registerGroupPackage(String)} and ignore packages registered by
* {@link #registerIgnorePackage(String, boolean)} to build the smart stack trace.
*
* @param throwable parent throwable
* @param ignoreAllCauses ignore all causes in the exception chain
* @return a string containing the smart stack trace for the given <code>throwable</code>
*/
public static String getStackTraceString(final Throwable throwable, final boolean ignoreAllCauses) {
return AbstractExceptions.getStackTraceString(throwable, ignoreAllCauses);
}
/**
* <p>Returns the smart stack trace for the given <code>throwable</code> using packages provided.
*
* @param throwable parent throwable
* @param rootPackageSet root packages to use for building the stack trace
* @param groupPackageSet group packages to use for building the stack trace
* @param ignorePackageSet ignore packages to use for building the stack trace
* @return a string containing the smart stack trace for the given <code>throwable</code>
*/
public static String getStackTraceString(final Throwable throwable, final Set<String> rootPackageSet, final Set<String> groupPackageSet, final Set<String> ignorePackageSet) {
return AbstractExceptions.getStackTraceString(throwable, rootPackageSet, groupPackageSet, ignorePackageSet);
}
/**
* <p>Returns the smart stack trace for the given <code>throwable</code> using packages provided.
*
* @param throwable parent throwable
* @param rootPackageSet root packages to use for building the stack trace
* @param groupPackageSet group packages to use for building the stack trace
* @param ignorePackageSet ignore packages to use for building the stack trace
* @param ignoreAllCauses ignore all causes in the exception chain
* @return a string containing the smart stack trace for the given <code>throwable</code>
*/
public static String getStackTraceString(final Throwable throwable, final Set<String> rootPackageSet, final Set<String> groupPackageSet, final Set<String> ignorePackageSet, final boolean ignoreAllCauses) {
return AbstractExceptions.getStackTraceString(throwable, rootPackageSet, groupPackageSet, ignorePackageSet, ignoreAllCauses);
}
/**
* <p>Returns the smart stack trace for the given <code>throwable</code> using root package provided.
*
* @param throwable parent throwable
* @param rootPackage root package to use for building the stack trace
* @return a string containing the smart stack trace for the given <code>throwable</code>
*/
public static String getStackTraceString(final Throwable throwable, final String rootPackage) {
return AbstractExceptions.getStackTraceString(throwable, rootPackage);
}
/**
* <p>Returns the smart stack trace for the given <code>throwable</code> using root package provided.
*
* @param throwable parent throwable
* @param rootPackage root package to use for building the stack trace
* @param groupPackage group package to use for building the stack trace
* @return a string containing the smart stack trace for the given <code>throwable</code>
*/
public static String getStackTraceString(final Throwable throwable, final String rootPackage, final String groupPackage) {
return AbstractExceptions.getStackTraceString(throwable, rootPackage, groupPackage);
}
/**
* <p>Returns the smart stack trace for the given <code>throwable</code> using elements found until the maxDepth.
*
* @param throwable parent throwable
* @param maxDepth max depth in exception chain that will be used
* @return a string containing the smart stack trace for the given <code>throwable</code>
*/
public static String getStackTraceString(final Throwable throwable, final int maxDepth) {
return AbstractExceptions.getStackTraceString(throwable, maxDepth);
}
/**
* <p>Returns the smart stack trace for the given <code>throwable</code> using elements found until the maxDepth.
*
* @param throwable parent throwable
* @param maxDepth max depth in exception chain that will be used
* @param ignoreAllCauses ignore all causes in the exception chain
* @return a string containing the smart stack trace for the given <code>throwable</code>
*/
public static String getStackTraceString(final Throwable throwable, final int maxDepth, final boolean ignoreAllCauses) {
return AbstractExceptions.getStackTraceString(throwable, maxDepth, ignoreAllCauses);
}
/**
* <p>Returns all messages found in the exception chain of the <code>throwable</code> as a single string.
*
* @param throwable parent throwable
* @return a string containing all messages found in the exception chain
*/
public static String getAllMessages(final Throwable throwable) {
return AbstractExceptions.getAllMessages(throwable);
}
/**
* <p>Returns true if the given cause class is found in the exception chain of the <code>throwable</code>.
* <br>
* <br>
* <p>Search starts from the <code>throwable</code> itself and goes through the exception chain up until
* {@link AbstractExceptions#DEFAULT_MAX_DEPTH}.
*
* @param throwable parent throwable
* @param causeClass class to search for
* @return true if the given cause class is found in the exception chain of the <code>throwable</code>, false
* otherwise
*/
public static boolean containsCause(final Throwable throwable, final Class<?> causeClass) {
return AbstractExceptions.containsCause(throwable, causeClass);
}
/**
* <p>Returns true if the given cause class and cause message is found in the exception chain of the
* <code>throwable</code>.
* <br>
* <br>
* <p>Search starts from the <code>throwable</code> itself and goes through the exception chain up until
* {@link AbstractExceptions#DEFAULT_MAX_DEPTH}.
*
* @param throwable parent throwable
* @param causeClass class to search for
* @param causeMessage message to search for
* @return true if the given cause class and cause message is found in the exception chain of the
* <code>throwable</code>, false otherwise
*/
public static boolean containsCause(final Throwable throwable, final Class<?> causeClass, final String causeMessage) {
return AbstractExceptions.containsCause(throwable, causeClass, causeMessage);
}
/**
* <p>Returns the cause of the <code>throwable</code> by walking through the exception chain.
*
* @param throwable parent throwable
* @return the cause of the <code>throwable</code> found or null if <code>throwable</code> does not have a cause
*/
public static Throwable getCause(final Throwable throwable) {
return AbstractExceptions.getCause(throwable);
}
/**
* <p>Returns the cause of the <code>throwable</code> by walking through the exception chain.
* <br>
* <br>
* <p>Note that this method walks through the exception chain up to given <code>maxDepth</code>. If exception chain
* includes more items than <code>maxDepth</code>, cause found at <code>maxDepth</code> level is returned.
*
* @param throwable parent throwable
* @param maxDepth max depth in exception chain that will be searched
* @return the cause of the <code>throwable</code> found until <code>maxDepth</code> is reached in the exception
* chain or null if <code>throwable</code> does not have a cause
*/
public static Throwable getCause(final Throwable throwable, final int maxDepth) {
return AbstractExceptions.getCause(throwable, maxDepth);
}
/**
* <p>Searches for the given cause class in the exception chain of the <code>throwable</code>.
* <br>
* <br>
* <p>Search starts from the <code>throwable</code> itself and goes through the exception chain up until
* {@link AbstractExceptions#DEFAULT_MAX_DEPTH}.
*
* @param throwable parent throwable
* @param causeClass class to search for
* @return the <code>throwable</code> found or null if no class in exception chain matches the given cause class
*/
public static Throwable searchCause(final Throwable throwable, final Class<?> causeClass) {
return AbstractExceptions.searchCause(throwable, causeClass);
}
/**
* <p>Searches for the given cause class and cause message in the exception chain of the <code>throwable</code>.
* <br>
* <br>
* <p>Search starts from the <code>throwable</code> itself and goes through the exception chain up until
* {@link AbstractExceptions#DEFAULT_MAX_DEPTH}.
*
* @param throwable parent throwable
* @param causeClass class to search for
* @param causeMessage message to search for
* @return the <code>throwable</code> found or null if no class in exception chain matches the given cause class
* and cause message
*/
public static Throwable searchCause(final Throwable throwable, final Class<?> causeClass, final String causeMessage) {
return AbstractExceptions.searchCause(throwable, causeClass, causeMessage);
}
/**
* <p>Searches for the given cause class and cause message in the exception chain of the <code>throwable</code>.
* <br>
* <br>
* <p>Search starts from the <code>throwable</code> itself and goes through the exception chain up until the given
* <code>maxDepth</code>.
*
* @param throwable parent throwable
* @param causeClass class to search for
* @param causeMessage message to search for
* @param maxDepth max depth in exception chain that will be searched
* @return the <code>throwable</code> found or null if no class in exception chain matches the given cause class
* and cause message
*/
public static Throwable searchCause(final Throwable throwable, final Class<?> causeClass, final String causeMessage, final int maxDepth) {
return AbstractExceptions.searchCause(throwable, causeClass, causeMessage, maxDepth);
}
/**
* <p>Searches for the given cause class in the exception chain of the <code>throwable</code>.
* <br>
* <br>
* <p>Search starts from the <code>throwable</code> itself and goes through the exception chain up until the given
* <code>maxDepth</code>.
*
* @param throwable parent throwable
* @param causeClass class to search for
* @param maxDepth max depth in exception chain that will be searched
* @return the <code>throwable</code> found or null if no class in exception chain matches the given cause class
*/
public static Throwable searchCause(final Throwable throwable, final Class<?> causeClass, final int maxDepth) {
return AbstractExceptions.searchCause(throwable, causeClass, maxDepth);
}
}
@@ -1,336 +0,0 @@
package com.vk.libvideo;
import android.os.Bundle;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
import com.vk.bridges.StatisticsBridge1;
import com.vk.dto.common.VideoFile;
import com.vk.media.player.PlayerTypes;
import com.vk.navigation.NavigatorKeys;
import com.vk.statistic.Statistic;
import org.json.JSONException;
import org.json.JSONObject;
import ru.vtosters.hooks.other.Preferences;
import ru.vtosters.lite.utils.Metrics;
import java.util.Locale;
import java.util.Objects;
/* loaded from: classes3.dex */
public class VideoTracker {
private final VideoFile a;
/* renamed from: b reason: collision with root package name */
private Statistic f15638b;
/* renamed from: c reason: collision with root package name */
private String f15639c;
/* renamed from: d reason: collision with root package name */
private String f15640d;
/* renamed from: e reason: collision with root package name */
private final boolean f15641e;
/* renamed from: f reason: collision with root package name */
private int f15642f = -1;
private final int g;
private final int h;
private final int i;
private final int j;
private final int k;
private int l;
private PlayerType m;
private Screen n;
/* loaded from: classes3.dex */
public enum FullscreenTransition {
TAP,
SCREEN_ROTATION
}
/* loaded from: classes3.dex */
public enum PlayerType {
INLINE,
CAROUSEL,
FULLSCREEN
}
/* loaded from: classes3.dex */
public enum ResizeAction {
BUTTON,
GESTURE
}
/* loaded from: classes3.dex */
public enum RewindType {
SLIDER,
DOUBLE_TAP
}
/* loaded from: classes3.dex */
public enum Screen {
INLINE,
CAROUSEL,
PORTRAIT,
LANDSCAPE
}
public VideoTracker(VideoFile videoFile, @Nullable Statistic statistic, String str, boolean z, @Nullable String str2) {
this.a = videoFile;
this.f15638b = statistic;
int i = videoFile.d;
this.g = (int) (i * 0.25f);
this.h = (int) (i * 0.5f);
this.i = (int) (i * 0.75f);
this.j = (int) (i * 0.95f);
this.k = i - 1;
this.f15639c = str;
this.f15641e = z;
this.f15640d = str2;
}
private static boolean a(int i, int i2, int i3) {
return i3 > i && i3 <= i2;
}
public void a(String str) {
this.f15640d = str;
}
public boolean b() {
return this.f15638b != null;
}
public void c() {
this.f15642f = -1;
}
public void d() {
c("video_fullscreen_off");
}
public void e() {
c("video_fullscreen_on");
}
public void f() {
c("video_pause");
c("pause", "pause");
}
public void g() {
c("video_resume");
c("pause", "resume");
}
public void h() {
c("video_volume_off");
c("volume", "off");
}
public void i() {
c("video_volume_on");
c("volume", "on");
}
private void c(String str, String str2) {
a(b(str, str2));
}
public String a() {
return this.f15640d;
}
public void b(String str) {
if (Objects.equals(this.f15639c, str)) {
return;
}
this.f15639c = str;
}
private void c(String str) {
if (this.f15638b != null) {
StatisticsBridge1.a().a(this.f15638b, str);
}
}
public void a(@Nullable Statistic statistic) {
this.f15638b = statistic;
}
private Bundle b(String str, String str2) {
Bundle bundle = new Bundle();
bundle.putString(NotificationCompat.CATEGORY_EVENT, str);
bundle.putString("value", str2);
bundle.putInt("position", this.f15642f);
return bundle;
}
public void a(PlayerType playerType) {
if (this.m != playerType) {
this.m = playerType;
}
}
public void a(int i, int i2) {
int i3 = this.f15642f;
if (i == i3) {
return;
}
if (a(i3, i, 0)) {
a("start", i2);
c("video_start");
}
// if (this.a.d > 0) {
// if (a(this.f15642f, i, 3)) {
// a("3s", i2);
// c("video_play_3s");
// }
// if (a(this.f15642f, i, 10)) {
// a("10s", i2);
// c("video_play_10s");
// }
// if (a(this.f15642f, i, this.g)) {
// a("25", i2);
// c("video_play_25");
// }
// if (a(this.f15642f, i, this.h)) {
// a("50", i2);
// c("video_play_50");
// }
// if (a(this.f15642f, i, this.i)) {
// a("75", i2);
// c("video_play_75");
// }
// if (a(this.f15642f, i, this.j)) {
// a("95", i2);
// c("video_play_95");
// }
// if (a(this.f15642f, i, this.k)) {
// a("100", i2);
// c("video_play_100");
// }
// }
this.f15642f = i;
}
public void a(int i) {
a("continue", i);
c("video_start");
}
public void a(@NonNull Screen screen) {
if (this.n != screen) {
this.n = screen;
c("screen", screen.toString().toLowerCase());
}
}
public void a(@NonNull FullscreenTransition fullscreenTransition) {
c("full_screen_transition", fullscreenTransition.toString().toLowerCase());
}
public void a(int i, boolean z) {
if (this.l != i) {
this.l = i;
c(z ? "auto_quality" : "quality", PlayerTypes.c(i));
}
}
public void a(@Nullable String str, boolean z) {
String str2 = z ? "auto_subtitles" : "subtitles";
if (str == null) {
str = "off";
}
c(str2, str);
}
public void a(int i, int i2, @NonNull RewindType rewindType) {
Bundle b2 = b("rewind", i <= i2 ? "forward" : "backward");
b2.putString("rewind_type", rewindType.toString().toLowerCase());
b2.putInt("rewind_start", i);
b2.putInt("rewind_end", i2);
a(b2);
}
public void a(float f2) {
String format;
if (f2 == 1.0f) {
format = "normal";
} else {
int i = (int) f2;
format = f2 == ((float) i) ? String.format(Locale.US, "%s", i) : String.format(Locale.US, "%.2f", f2);
}
c("speed", format);
}
public void a(boolean z, @NonNull ResizeAction resizeAction) {
Bundle b2 = b("resize", z ? "widen" : "narrow");
b2.putString("resize_action", resizeAction.toString().toLowerCase());
a(b2);
}
public void a(String str, String str2) {
Bundle bundle = new Bundle();
bundle.putString("link_type", str);
bundle.putString("value", str2);
bundle.putString(NotificationCompat.CATEGORY_EVENT, "show_link");
a(bundle);
}
private void a(String str, int i) {
Bundle bundle = new Bundle();
bundle.putString("autoplay", this.f15641e ? "1" : "0");
bundle.putString("position", str);
if (i >= 0) {
bundle.putInt("quality", i - 2);
}
PlayerType playerType = this.m;
if (playerType != null) {
bundle.putString("player_type", playerType.toString().toLowerCase());
}
a("video_play", bundle);
}
private void a(Bundle bundle) {
a("video_event", bundle);
}
private void a(String str, Bundle bundle) {
bundle.putString("video_id", this.a.a + "_" + this.a.b);
bundle.putString(NavigatorKeys.V, this.f15639c);
bundle.putString(NavigatorKeys.Z, this.f15639c);
if (!TextUtils.isEmpty(this.f15640d)) {
bundle.putString("context", this.f15640d);
}
if (!TextUtils.isEmpty(this.a.l0)) {
bundle.putString(NavigatorKeys.l0, this.a.l0);
}
bundle.putLong("ts", System.currentTimeMillis());
StatisticsBridge1.a().a(str, bundle);
if (Preferences.getBoolValue("sendVideoMetrics", true) && str.equals("video_play")) {
try {
sendMetrics(str);
} catch (JSONException e) {
throw new RuntimeException(e);
}
}
}
public void sendMetrics(String event) throws JSONException {
JSONObject jsonObject = new JSONObject();
jsonObject.put("e", event);
jsonObject.put("source", this.f15639c);
jsonObject.put("ref", this.f15639c);
jsonObject.put("video_id", this.a.a + "_" + this.a.b);
jsonObject.put("position", "start");
jsonObject.put("position_sec", 0);
jsonObject.put("cur_quality", "auto");
Metrics.trackEvents(jsonObject, false);
}
}
@@ -24,8 +24,8 @@ public class MediaImageEncoder {
return compressBitmap(bitmap, file, CompressFormat.JPEG, quality); return compressBitmap(bitmap, file, CompressFormat.JPEG, quality);
} }
public static boolean needToCompress() { private static boolean compressBitmapLossless(Bitmap bitmap, File file) {
return Preferences.getPreferences().getBoolean("compressPhotos", true); return compressBitmap(bitmap, file, CompressFormat.PNG, 100);
} }
private static boolean compressBitmapNative(Bitmap bitmap, File file, int quality) { private static boolean compressBitmapNative(Bitmap bitmap, File file, int quality) {
@@ -37,7 +37,7 @@ public class MediaImageEncoder {
byte[] jpegData = MediaNative.compressBitmapJpeg(bitmap, quality); byte[] jpegData = MediaNative.compressBitmapJpeg(bitmap, quality);
if (jpegData == null) { if (jpegData == null) {
return compressBitmapJpeg(bitmap, file, quality); return false;
} }
try (FileOutputStream fileOutputStream = new FileOutputStream(file)) { try (FileOutputStream fileOutputStream = new FileOutputStream(file)) {
@@ -51,21 +51,43 @@ public class MediaImageEncoder {
public static boolean encodePicture(Bitmap bitmap, File file, int quality) { public static boolean encodePicture(Bitmap bitmap, File file, int quality) {
if (bitmap != null && !bitmap.isRecycled() && bitmap.getWidth() * bitmap.getHeight() != 0) { if (bitmap != null && !bitmap.isRecycled() && bitmap.getWidth() * bitmap.getHeight() != 0) {
return compressBitmapNative(bitmap, file, quality); switch (Preferences.getPreferences().getString("compression", "jpeg")) {
case "skip" -> {
return compressBitmapLossless(bitmap, file);
}
case "jpeg" -> {
return compressBitmapJpeg(bitmap, file, quality);
}
case "native" -> {
return compressBitmapNative(bitmap, file, quality);
}
}
}
return false;
}
public static boolean needToSkipCompression() {
return Preferences.getPreferences().getString("compression", "jpeg").equals("skip");
}
public static boolean needToCompress() {
return !needToSkipCompression();
}
public static boolean encodeJpeg(Bitmap bitmap, File file) {
return encodePicture(bitmap, file, 90);
}
public static boolean encodeJpegWithoutCompression(Bitmap bitmap, File file) {
return encodePicture(bitmap, file, 95);
}
public static boolean encodeJpeg(Bitmap bitmap, File file, int quality) {
if (bitmap != null && !bitmap.isRecycled() && bitmap.getWidth() * bitmap.getHeight() != 0) {
return needToCompress() ? compressBitmapNative(bitmap, file, quality) : compressBitmapJpeg(bitmap, file, quality);
} else { } else {
return false; return false;
} }
} }
public static boolean encodeJpeg(Bitmap bitmap, File file) {
return encodePicture(bitmap, file, Preferences.compress(85));
}
public static boolean encodeJpegWithoutCompression(Bitmap bitmap, File file) {
return encodePicture(bitmap, file, 100);
}
public static boolean encodeJpeg(Bitmap bitmap, File file, int quality) {
return encodePicture(bitmap, file, Preferences.compress(85));
}
} }
@@ -248,6 +248,7 @@ public final class MediaNative {
if (isMediaLoaded) { if (isMediaLoaded) {
return; return;
} }
NativeLibLoader.loadLibrary("ffmpeg");
isMediaLoaded = NativeLibLoader.loadLibrary("vkmedia"); isMediaLoaded = NativeLibLoader.loadLibrary("vkmedia");
isMediaLoaded = NativeLibLoader.loadLibrary("vkmediaencoder"); isMediaLoaded = NativeLibLoader.loadLibrary("vkmediaencoder");
} }
@@ -1,256 +0,0 @@
package com.vk.music.stats;
import android.content.Intent;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.core.app.NotificationCompat;
import com.vk.core.extensions.StringExt;
import com.vk.music.common.MusicPlaybackLaunchContext;
import com.vk.music.logger.MusicLogger;
import com.vk.music.stats.i.MusicPlaybackParams;
import com.vk.navigation.NavigatorKeys;
import com.vtosters.lite.data.Analytics;
import kotlin.jvm.b.Functions2;
import org.json.JSONException;
import org.json.JSONObject;
import ru.vtosters.hooks.other.Preferences;
import ru.vtosters.lite.utils.Metrics;
import java.util.Objects;
import java.util.UUID;
public final class VKMusicStatsTracker implements MusicStatsTracker {
private String a;
private final Functions2<String, Analytics.l> b;
private static final a c = new a();
public VKMusicStatsTracker(Functions2<String, Analytics.l> functions2) {
this.b = functions2;
}
private String e(MusicPlaybackParams musicPlaybackParams) {
String g = musicPlaybackParams.g();
if (g == null) {
g = this.a;
}
if (g == null) {
g = "auto";
}
this.a = g;
return g;
}
@Override
public void a(boolean z) {
MusicStatsTracker.a.a(this, z);
}
@Override
public void b(MusicPlaybackParams musicPlaybackParams) {
c.a(a(musicPlaybackParams, "music_start_playback"));
if (Preferences.sendMusicMetrics()) {
try {
makeMetricsRequest(musicPlaybackParams, "music_start_playback");
} catch (JSONException e) {
throw new RuntimeException(e);
}
}
}
@Override
public void b(boolean z) {
}
@Override
public void c(MusicPlaybackParams musicPlaybackParams) {
MusicStatsTracker.a.b(this, musicPlaybackParams);
}
@Override
public void d() {
MusicStatsTracker.a.a(this);
}
@Override
public void a(Intent intent, String str) {
MusicStatsTracker.a.a(this, intent, str);
}
@Override
public void b(String str, @NonNull String str2) {
Analytics.l invoke = this.b.invoke("music_subscription_action");
invoke.a("popup", str);
invoke.a("action", str2);
c.a(invoke);
}
@Override
public void c(boolean z) {
MusicStatsTracker.a.b(this, z);
}
@Override
public void d(MusicPlaybackParams musicPlaybackParams) {
if (Objects.equals("pause", this.a)) {
a(musicPlaybackParams);
}
}
@Override
public void a(long j) {
MusicStatsTracker.a.a(this, j);
}
@Override
public void c() {
Analytics.l invoke = this.b.invoke("music_subscription_push");
invoke.a("action", "open");
c.a(invoke);
}
@Override
public void a(String str, MusicStatsRefer musicStatsRefer) {
Analytics.l invoke = this.b.invoke("music_subscription_show");
invoke.a("popup", str);
c.a(invoke);
}
@Override
public void b() {
Analytics.l invoke = this.b.invoke("music_subscription_push");
invoke.a("action", "send");
c.a(invoke);
}
@Override
public void a(MusicPlaybackParams musicPlaybackParams) {
c.a(a(musicPlaybackParams, "music_stop_playback"));
if (Preferences.sendMusicMetrics()) {
try {
makeMetricsRequest(musicPlaybackParams, "music_stop_playback");
} catch (JSONException e) {
throw new RuntimeException(e);
}
}
}
@Override
public void a(PlayerStateChangedParams playerStateChangedParams) {
Analytics.l invoke = this.b.invoke("audio_player");
invoke.a(NavigatorKeys.t0, playerStateChangedParams.a());
invoke.a("prev_state", playerStateChangedParams.c());
invoke.a("duration", playerStateChangedParams.b());
c.a(invoke);
}
@Override
public void b(String str) {
Analytics.l invoke = this.b.invoke("music_subscription_free_displayed");
invoke.a("popup", str);
c.a(invoke);
}
@Override
public void a(String str) {
Analytics.l invoke = this.b.invoke("playlist_start");
invoke.a(NavigatorKeys.e, str);
c.a(invoke);
}
private void makeMetricsRequest(MusicPlaybackParams musicPlaybackParams, String str) throws JSONException {
JSONObject event = new JSONObject();
if (str.equals("music_stop_playback")) {
event.put("e", str);
event.put("audio_id", musicPlaybackParams.c());
event.put("duration", musicPlaybackParams.a());
event.put("start_time", musicPlaybackParams.i());
event.put("shuffle", "false");
event.put("reason", "new");
event.put("state", "app");
event.put("duration", musicPlaybackParams.a());
} else {
event.put("e", str);
event.put("audio_id", musicPlaybackParams.c());
event.put("start_time", musicPlaybackParams.i());
event.put("shuffle", "false");
event.put("reason", "auto");
event.put("state", "app");
if (StringExt.a((CharSequence) musicPlaybackParams.e())) {
event.put("prev_audio_id", musicPlaybackParams.e());
}
}
Metrics.trackEvents(event, true);
}
private Analytics.l a(MusicPlaybackParams musicPlaybackParams, String str) {
Analytics.l invoke = this.b.invoke(str);
invoke.a("audio_id", musicPlaybackParams.c());
invoke.a("uuid", UUID.randomUUID().hashCode());
invoke.a("shuffle", musicPlaybackParams.l());
invoke.a("reason", e(musicPlaybackParams));
invoke.a("start_time", musicPlaybackParams.i());
invoke.a("playback_started_at", musicPlaybackParams.d());
invoke.a(NavigatorKeys.l0, musicPlaybackParams.j());
if (!Objects.equals("music_start_playback", str)) {
invoke.a("duration", musicPlaybackParams.a());
}
int i = h.$EnumSwitchMapping$0[musicPlaybackParams.b().ordinal()];
if (i == 1) {
invoke.a("repeat", "one");
} else if (i == 2) {
invoke.a("repeat", "all");
}
invoke.a(NavigatorKeys.t0, musicPlaybackParams.m());
MusicPlaybackLaunchContext h = musicPlaybackParams.h();
invoke.a(NavigatorKeys.V, h.v0());
if (h.v1()) {
invoke.a("playlist_id", h.u1());
}
if (h.i(4) || h.i(8)) {
invoke.a("expanded", h.i(4));
}
if (StringExt.a((CharSequence) musicPlaybackParams.e())) {
invoke.a("prev_audio_id", musicPlaybackParams.e());
}
if (StringExt.a((CharSequence) musicPlaybackParams.f())) {
invoke.a("prev_playlist_id", musicPlaybackParams.f());
}
return invoke;
}
@Override
public void a(String str, String str2) {
boolean a2 = Objects.equals("success", str2);
Analytics.l builder = this.b.invoke("music_subscription_purchase_result");
builder.a("popup", str);
builder.a(NotificationCompat.CATEGORY_STATUS, a2 ? "success" : "fail");
if (!a2) {
builder.a("reason", str2);
}
c.a(builder);
}
@Override
public void a() {
Analytics.l invoke = this.b.invoke("music_subscription");
invoke.a("popup", "background_v2");
invoke.a("action", "link");
c.a(invoke);
}
public static final class a {
private a() {
}
@VisibleForTesting
public void a(Analytics.l lVar) {
MusicLogger.a("MusicStats", "[VK_TRACKER]", lVar);
lVar.b();
}
}
}
@@ -1,106 +0,0 @@
package com.vk.newsfeed.holders;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import com.vk.core.dialogs.alert.VkAlertDialog;
import com.vk.core.ui.themes.VKThemeHelper;
import com.vk.dto.newsfeed.entries.NewsEntry;
import com.vk.dto.newsfeed.entries.Post;
import com.vk.dto.newsfeed.entries.PromoPost;
import com.vk.extensions.ViewExtKt;
import com.vtosters.lite.R;
import ru.vtosters.hooks.other.Preferences;
import ru.vtosters.lite.utils.AndroidUtils;
import ru.vtosters.lite.utils.newsfeed.NewsFeedFiltersUtils;
import ru.vtosters.sponsorpost.internal.Native;
import ru.vtosters.sponsorpost.internal.VotesPreferences;
import ru.vtosters.sponsorpost.internal.VotesService;
import ru.vtosters.sponsorpost.utils.PostsPreferences;
public final class AdMarkHolder extends BaseNewsEntryHolder<NewsEntry> {
private final TextView F;
@SuppressLint("ResourceType")
public AdMarkHolder(ViewGroup viewGroup) {
super(R.layout.marked_as_ads, viewGroup);
View view = ((RecyclerView.ViewHolder) this).itemView;
this.F = ViewExtKt.a(view, R.id.text, (View.OnClickListener) null);
}
@SuppressLint("ResourceType")
public void b(NewsEntry newsEntry) { // onBind
String disclamer = null;
Drawable drawable = null;
int height = 0;
if (newsEntry instanceof Post) {
Post post = ((Post) newsEntry);
if (post.L1()) {
drawable = VKThemeHelper.a(R.drawable.marked_as_ads, R.attr.icon_tertiary);
int ownerId = post.b();
int postId = post.P1();
int date = post.K();
String text = post.getText();
if (PostsPreferences.isPostAd(ownerId, postId)) {
disclamer = "SponsorPost: Реклама";
if (Preferences.isValidSignature() && Native.canVote()) {
this.F.setOnClickListener(listener -> voteDialog(ownerId, postId, date, this.F.getContext()));
}
} else if (VotesPreferences.isPostAd(ownerId, postId)) {
disclamer = "SponsorPost: Возможно реклама";
if (Preferences.isValidSignature() && Native.canVote()) {
this.F.setOnClickListener(listener -> voteDialog(ownerId, postId, date, this.F.getContext()));
}
} else if (NewsFeedFiltersUtils.sponsorFilters(text)) {
disclamer = "SponsorPost: Заблокировано фильтрами";
this.F.setOnClickListener(listener -> AndroidUtils.sendToast("Заблокировано по выражению: " + NewsFeedFiltersUtils.sponsorFiltersBanWord(text)));
} else {
disclamer = m(R.string.sponsored_post_in_group);
}
height = e0().getDimensionPixelSize(R.dimen.newsfeed_ad_mark_post_height);
}
} else if (newsEntry instanceof PromoPost) {
PromoPost promoPost = (PromoPost) newsEntry;
if (!promoPost.E1().isEmpty()) {
height = e0().getDimensionPixelSize(R.dimen.newsfeed_ad_mark_promo_post_height);
disclamer = promoPost.E1();
}
}
this.F.setCompoundDrawablesRelativeWithIntrinsicBounds(drawable, null, null, null);
this.F.setMinHeight(height);
this.F.setText(disclamer);
}
public static void voteDialog(int ownerId, int postId, int date, Context ctx) {
new VkAlertDialog.Builder(ctx)
.setTitle("SponsorPost")
.setMessage("Этот пост является рекламным?")
.setPositiveButton("Да", ((dialog, which) -> {
int resp = VotesService.ratePost(ownerId, postId, date, true).optInt("code");
if (resp == 201 || resp == 100) {
AndroidUtils.sendToast("Спасибо за голос!");
} else {
AndroidUtils.sendToast("Ваш голос уже учтен");
}
}))
.setNeutralButton("Нет", ((dialog, which) -> {
int resp = VotesService.ratePost(ownerId, postId, date, false).optInt("code");
if (resp == 201 || resp == 100) {
AndroidUtils.sendToast("Спасибо за голос!");
} else {
AndroidUtils.sendToast("Ваш голос уже учтен");
}
}))
.show();
}
}
+32 -33
View File
@@ -19661,39 +19661,38 @@ public final class R {
public static final int cache_size = 0x7f12188d; // 2131892365 public static final int cache_size = 0x7f12188d; // 2131892365
public static final int cache_size_summ = 0x7f12188e; // 2131892366 public static final int cache_size_summ = 0x7f12188e; // 2131892366
public static final int clear_all_cache = 0x7f12188f; // 2131892367 public static final int clear_all_cache = 0x7f12188f; // 2131892367
public static final int disable_roaming_state = 0x7f121890; // 2131892368 public static final int invalidate_theme_cache_each_update_title = 0x7f121890; // 2131892368
public static final int disable_roaming_state_submit = 0x7f121891; // 2131892369 public static final int invertFilters = 0x7f121891; // 2131892369
public static final int invalidate_theme_cache_each_update_title = 0x7f121892; // 2131892370 public static final int invertFilters_summ = 0x7f121892; // 2131892370
public static final int invertFilters = 0x7f121893; // 2131892371 public static final int invertcached_summ = 0x7f121893; // 2131892371
public static final int invertFilters_summ = 0x7f121894; // 2131892372 public static final int invertcached_title = 0x7f121894; // 2131892372
public static final int invertcached_summ = 0x7f121895; // 2131892373 public static final int microg_dialog1 = 0x7f121895; // 2131892373
public static final int invertcached_title = 0x7f121896; // 2131892374 public static final int microg_dialog2 = 0x7f121896; // 2131892374
public static final int microg_dialog1 = 0x7f121897; // 2131892375 public static final int microg_dialog3 = 0x7f121897; // 2131892375
public static final int microg_dialog2 = 0x7f121898; // 2131892376 public static final int serverFeaturesDisable = 0x7f121898; // 2131892376
public static final int microg_dialog3 = 0x7f121899; // 2131892377 public static final int serverFeaturesDisable_summ = 0x7f121899; // 2131892377
public static final int serverFeaturesDisable = 0x7f12189a; // 2131892378 public static final int sett_autoupdates = 0x7f12189a; // 2131892378
public static final int serverFeaturesDisable_summ = 0x7f12189b; // 2131892379 public static final int sett_autoupdates_summ = 0x7f12189b; // 2131892379
public static final int sett_autoupdates = 0x7f12189c; // 2131892380 public static final int sett_battery = 0x7f12189c; // 2131892380
public static final int sett_autoupdates_summ = 0x7f12189d; // 2131892381 public static final int sett_battery_summ1 = 0x7f12189d; // 2131892381
public static final int sett_battery = 0x7f12189e; // 2131892382 public static final int sett_battery_summ2 = 0x7f12189e; // 2131892382
public static final int sett_battery_summ1 = 0x7f12189f; // 2131892383 public static final int sett_chats = 0x7f12189f; // 2131892383
public static final int sett_battery_summ2 = 0x7f1218a0; // 2131892384 public static final int sett_chats_summ = 0x7f1218a0; // 2131892384
public static final int sett_chats = 0x7f1218a1; // 2131892385 public static final int sett_community = 0x7f1218a1; // 2131892385
public static final int sett_chats_summ = 0x7f1218a2; // 2131892386 public static final int sett_community_summ = 0x7f1218a2; // 2131892386
public static final int sett_community = 0x7f1218a3; // 2131892387 public static final int sett_missing_links = 0x7f1218a3; // 2131892387
public static final int sett_community_summ = 0x7f1218a4; // 2131892388 public static final int sett_missing_links_summ = 0x7f1218a4; // 2131892388
public static final int sett_missing_links = 0x7f1218a5; // 2131892389 public static final int sett_nogms_summ = 0x7f1218a5; // 2131892389
public static final int sett_missing_links_summ = 0x7f1218a6; // 2131892390 public static final int sett_notifs = 0x7f1218a6; // 2131892390
public static final int sett_nogms_summ = 0x7f1218a7; // 2131892391 public static final int sett_notifs_summ = 0x7f1218a7; // 2131892391
public static final int sett_notifs = 0x7f1218a8; // 2131892392 public static final int sett_recommendations = 0x7f1218a8; // 2131892392
public static final int sett_notifs_summ = 0x7f1218a9; // 2131892393 public static final int tgs_token_missing = 0x7f1218aa; // 2131892394
public static final int sett_recommendations = 0x7f1218aa; // 2131892394 public static final int tgs_token_reset = 0x7f1218ab; // 2131892395
public static final int tgs_token_missing = 0x7f1218ab; // 2131892395 public static final int tgs_token_reset_dialog = 0x7f1218ac; // 2131892396
public static final int tgs_token_reset = 0x7f1218ac; // 2131892396 public static final int tgs_token_reset_menu = 0x7f1218ad; // 2131892397
public static final int tgs_token_reset_dialog = 0x7f1218ad; // 2131892397 public static final int update_err = 0x7f1218ae; // 2131892398
public static final int tgs_token_reset_menu = 0x7f1218ae; // 2131892398 public static final int update_latest = 0x7f1218af; // 2131892399
public static final int update_err = 0x7f1218af; // 2131892399 public static final int APKTOOL_DUMMY_18a9 = 0x7f1218a9; // 2131892393
public static final int update_latest = 0x7f1218b0; // 2131892400
} }
public static final class style { public static final class style {
@@ -12,12 +12,12 @@ import com.vk.im.engine.models.messages.NestedMsg;
import com.vk.libsqliteext.CustomSqliteExtensionsKt; import com.vk.libsqliteext.CustomSqliteExtensionsKt;
import io.requery.android.database.sqlite.SQLiteDatabase; import io.requery.android.database.sqlite.SQLiteDatabase;
import io.requery.android.database.sqlite.SQLiteOpenHelper; import io.requery.android.database.sqlite.SQLiteOpenHelper;
import ru.vtosters.hooks.other.Preferences;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import static ru.vtosters.hooks.other.Preferences.getBoolValue; import static ru.vtosters.hooks.other.Preferences.getBoolValue;
import static ru.vtosters.hooks.other.Preferences.getString;
import static ru.vtosters.lite.utils.AndroidUtils.getGlobalContext; import static ru.vtosters.lite.utils.AndroidUtils.getGlobalContext;
public class DeletedMessagesHook { public class DeletedMessagesHook {
@@ -69,7 +69,7 @@ public class DeletedMessagesHook {
} }
private static String getPrefixUndelete() { private static String getPrefixUndelete() {
var prefs = Preferences.getPreferences().getString("undeletemsg_prefix_value", "🗑"); var prefs = getString("undeletemsg_prefix_value");
if (prefs.isEmpty()) { if (prefs.isEmpty()) {
return "\uD83D\uDDD1 "; return "\uD83D\uDDD1 ";
@@ -78,7 +78,7 @@ public class DeletedMessagesHook {
return prefs + " "; return prefs + " ";
} }
public static void updateDialog(MsgDeleteLpTask msgDeleteLpTask) { public static void updateDialog(MsgDeleteLpTask msgDeleteLpTask) throws NoSuchFieldException, IllegalAccessException {
var cursor = getMessageFromDatabaseById(msgDeleteLpTask.d); var cursor = getMessageFromDatabaseById(msgDeleteLpTask.d);
if (cursor == null) return; if (cursor == null) return;
@@ -72,9 +72,8 @@ public class EncryptionMessagesHook {
public static String decryptMessage(String msgBody, int peer) { public static String decryptMessage(String msgBody, int peer) {
try { try {
for (IMProcessor processor : EncryptProvider.processors) { for (IMProcessor processor : EncryptProvider.processors) {
if ((processor.isUsed() || !processor.isPublic()) && processor.isEncrypted(msgBody) && (processor.isPublic() || EncryptProvider.getKeyForProcessor(processor, peer) != null)) { if ((processor.isUsed() || !processor.isPublic()) && processor.isEncrypted(msgBody) && (processor.isPublic() || EncryptProvider.getKeyForProcessor(processor, peer) != null))
return "\uD83D\uDD12 " + processor.getUIName() + ": " + injectOwnTextAll(processor.decode(msgBody, EncryptProvider.getKeyForProcessor(processor, peer))); return "\uD83D\uDD12 " + injectOwnTextAll(processor.decode(msgBody, EncryptProvider.getKeyForProcessor(processor, peer)));
}
} }
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
@@ -1,38 +0,0 @@
package ru.vtosters.hooks;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
import java.util.zip.GZIPInputStream;
public class FirebaseGzipSupport {
public static JSONObject getJsonObjectFromConnection(URLConnection connection) throws IOException, JSONException {
InputStream inputStream = getDecompressedInputStream(connection);
String jsonString = readInputStream(inputStream);
return new JSONObject(jsonString);
}
private static InputStream getDecompressedInputStream(URLConnection connection) throws IOException {
String contentEncoding = connection.getHeaderField("Content-Encoding");
if (contentEncoding != null && contentEncoding.equalsIgnoreCase("gzip")) {
return new GZIPInputStream(connection.getInputStream());
}
return connection.getInputStream();
}
private static String readInputStream(InputStream inputStream) throws IOException {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
StringBuilder sb = new StringBuilder();
int read;
while ((read = bufferedReader.read()) != -1) {
sb.append((char) read);
}
return sb.toString();
}
}
@@ -1,32 +0,0 @@
package ru.vtosters.hooks;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import com.vk.dto.gift.GiftCategory;
import com.vtosters.lite.TabletDialogActivity;
import com.vtosters.lite.fragments.gifts.GiftCategoryFragment;
import ru.vtosters.lite.ui.fragments.SuperAppEditorFragment;
import ru.vtosters.lite.utils.NavigatorUtils;
import java.util.ArrayList;
public class GiftsFix {
public static Bundle staticBundle;
public static void openGiftCategoryFragment(Activity activity, ArrayList<Integer> userIds, GiftCategory giftCategory, int balance, String reference) {
Bundle bundle = new Bundle();
bundle.putIntegerArrayList("uids", userIds);
bundle.putParcelable("data", giftCategory);
bundle.putInt("balance", balance);
bundle.putString("ref", reference);
staticBundle = bundle;
NavigatorUtils.switchFragment(activity, GiftCategoryFragment.class);
}
public static Bundle getBundle() {
return staticBundle;
}
}
@@ -11,37 +11,37 @@ import com.vtosters.lite.R;
import ru.vtosters.lite.utils.AndroidUtils; import ru.vtosters.lite.utils.AndroidUtils;
public class GmsHook { public class GmsHook {
private static final String[] spoofPackages = {"com.mgoogle", "app.revanced"}; private static final boolean needToSpoof = !isGmsInstalled() && (isFakeGmsInstalled() || isFakeGms2Installed());
private static final String currentGms;
static { public static boolean isGmsInstalled() {
currentGms = getSpoofPackagePrefix();
}
private static boolean isPackageInstalled(String packageName) {
try { try {
AndroidUtils.getGlobalContext().getPackageManager().getPackageInfo(packageName, 0); AndroidUtils.getGlobalContext().getPackageManager().getPackageInfo("com.google.android.gms", 0);
return true; return true;
} catch (Exception unused) { } catch (Exception unused) {
return false; return false;
} }
} } // Google Market Services check
public static boolean isGmsInstalled() { public static boolean isFakeGmsInstalled() {
return isPackageInstalled("com.google.android.gms"); try {
} AndroidUtils.getGlobalContext().getPackageManager().getPackageInfo("com.mgoogle.android.gms", 0);
return true;
} catch (Exception unused) {
return false;
}
} // Microg Google Market Services check
public static boolean isSpoofPackageInstalled(String packageName) { public static boolean isFakeGms2Installed() {
return isPackageInstalled(packageName + ".android.gms"); try {
} AndroidUtils.getGlobalContext().getPackageManager().getPackageInfo("app.revanced.android.gms", 0);
return true;
} catch (Exception unused) {
return false;
}
} // Microg Google Market Services check
public static boolean isAnyServicesInstalled() { public static boolean isAnyServicesInstalled() {
for (String packageName : spoofPackages) { return isGmsInstalled() || isFakeGmsInstalled() || isFakeGms2Installed();
if (isSpoofPackageInstalled(packageName)) {
return true;
}
}
return false;
} }
public static void fixGapps() { public static void fixGapps() {
@@ -52,10 +52,10 @@ public class GmsHook {
notificationManager.createNotificationChannel(new NotificationChannel("audio_playback_channel", AndroidUtils.getResources().getString(R.string.audio_message_play_error), NotificationManager.IMPORTANCE_LOW)); notificationManager.createNotificationChannel(new NotificationChannel("audio_playback_channel", AndroidUtils.getResources().getString(R.string.audio_message_play_error), NotificationManager.IMPORTANCE_LOW));
} }
} }
} } // Music channels fix
public static String replaceGMSPackage(String str) { public static String replaceGMSPackage(String str) {
return shouldSpoof() ? str.replaceAll("com.google", currentGms) : str; return needToSpoof ? str.replaceAll("com.google", (isFakeGms2Installed() ? "app.revanced" : "com.mgoogle")) : str;
} }
public static String getFirebaseInstanceIdReceiver() { public static String getFirebaseInstanceIdReceiver() {
@@ -63,37 +63,6 @@ public class GmsHook {
} }
public static Class getFirebaseInstanceIdReceiverClass() { public static Class getFirebaseInstanceIdReceiverClass() {
return getSpoofFirebaseInstanceIdReceiverClass(); return needToSpoof ? (isFakeGms2Installed() ? FirebaseInstanceIdReceiver3.class : FirebaseInstanceIdReceiver2.class) : FirebaseInstanceIdReceiver.class;
}
public static String getCurrentGms() {
return currentGms;
}
private static boolean shouldSpoof() {
return !isGmsInstalled() && (isSpoofPackageInstalled("com.mgoogle") || isSpoofPackageInstalled("app.revanced"));
}
private static String getSpoofPackagePrefix() {
for (String packageName : spoofPackages) {
if (isSpoofPackageInstalled(packageName)) {
return packageName;
}
}
return "com.google";
}
private static Class getSpoofFirebaseInstanceIdReceiverClass() {
switch (currentGms) {
case "com.mgoogle" -> {
return FirebaseInstanceIdReceiver2.class;
}
case "app.revanced" -> {
return FirebaseInstanceIdReceiver3.class;
}
default -> {
return FirebaseInstanceIdReceiver.class;
}
}
} }
} }
@@ -6,7 +6,6 @@ import android.os.Build;
import b.h.g.k.VKProgressDialog; import b.h.g.k.VKProgressDialog;
import com.aefyr.tsg.g2.TelegramStickersService; import com.aefyr.tsg.g2.TelegramStickersService;
import com.vk.core.dialogs.alert.VkAlertDialog; import com.vk.core.dialogs.alert.VkAlertDialog;
import com.vk.medianative.NativeLibLoader;
import com.vtosters.lite.R; import com.vtosters.lite.R;
import ru.vtosters.hooks.other.Preferences; import ru.vtosters.hooks.other.Preferences;
import ru.vtosters.hooks.other.ThemesUtils; import ru.vtosters.hooks.other.ThemesUtils;
@@ -18,8 +17,10 @@ import ru.vtosters.lite.themes.ThemesManager;
import ru.vtosters.lite.ui.dialogs.InstallGMS; import ru.vtosters.lite.ui.dialogs.InstallGMS;
import ru.vtosters.lite.ui.dialogs.OTADialog; import ru.vtosters.lite.ui.dialogs.OTADialog;
import ru.vtosters.lite.ui.dialogs.Start; import ru.vtosters.lite.ui.dialogs.Start;
import ru.vtosters.lite.utils.*; import ru.vtosters.lite.utils.AndroidUtils;
import ru.vtosters.sponsorpost.internal.Native; import ru.vtosters.lite.utils.LifecycleUtils;
import ru.vtosters.lite.utils.NavigatorUtils;
import ru.vtosters.lite.utils.VTVerifications;
import ru.vtosters.sponsorpost.utils.Updates; import ru.vtosters.sponsorpost.utils.Updates;
import static ru.vtosters.hooks.other.Preferences.checkupdates; import static ru.vtosters.hooks.other.Preferences.checkupdates;
@@ -66,10 +67,6 @@ public class MainActivityInjector {
// VKIDProtection.alert(activity); // VKIDProtection.alert(activity);
//needs to show selected tgs pack count in settings after cold launch //needs to show selected tgs pack count in settings after cold launch
TelegramStickersService.getInstance(activity); TelegramStickersService.getInstance(activity);
if (AccountManagerUtils.isLogin() && !Preferences.serverFeaturesDisable() && Preferences.isValidSignature()) {
Native.canVote = NativeLibLoader.loadLibrary("sponsorpost");
}
} }
private static void updateBinsAndTmpArchive(Activity activity) { private static void updateBinsAndTmpArchive(Activity activity) {
@@ -83,7 +80,7 @@ public class MainActivityInjector {
ThemesManager.generateModApk(ThemesUtils.getReservedAccent()); ThemesManager.generateModApk(ThemesUtils.getReservedAccent());
activity.runOnUiThread(LifecycleUtils::restartApplication); activity.runOnUiThread(LifecycleUtils::restartApplication);
} catch (Throwable e) { } catch (Throwable e) {
e.fillInStackTrace(); e.printStackTrace();
activity.runOnUiThread(() -> { activity.runOnUiThread(() -> {
dialog.cancel(); dialog.cancel();
new VkAlertDialog.Builder(activity) new VkAlertDialog.Builder(activity)
@@ -11,12 +11,13 @@ import com.vk.discover.DiscoverItemDecorator;
import ru.vtosters.hooks.other.Preferences; import ru.vtosters.hooks.other.Preferences;
import ru.vtosters.lite.concurrent.VTExecutors; import ru.vtosters.lite.concurrent.VTExecutors;
import ru.vtosters.lite.utils.NetworkUtils; import ru.vtosters.lite.utils.NetworkUtils;
import ru.vtosters.sponsorpost.internal.VotesPreferences;
import ru.vtosters.sponsorpost.internal.VotesService;
import ru.vtosters.sponsorpost.services.PostService; import ru.vtosters.sponsorpost.services.PostService;
import ru.vtosters.sponsorpost.utils.PostsPreferences; import ru.vtosters.sponsorpost.utils.PostsPreferences;
import java.util.*; import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import static java.lang.Long.MAX_VALUE; import static java.lang.Long.MAX_VALUE;
import static ru.vtosters.hooks.other.Preferences.*; import static ru.vtosters.hooks.other.Preferences.*;
@@ -101,16 +102,8 @@ public class NewsfeedHook {
public static void takeOwnerIdSponsorPost(int ownerid) { public static void takeOwnerIdSponsorPost(int ownerid) {
if (NetworkUtils.isNetworkConnected() && PostsPreferences.isEnabled() && !Preferences.serverFeaturesDisable()) { if (NetworkUtils.isNetworkConnected() && PostsPreferences.isEnabled() && !Preferences.serverFeaturesDisable()) {
VTExecutors.getIoScheduler().a(() -> { VTExecutors.getIoScheduler().a(() -> {
if (PostsPreferences.isGroupAd(ownerid) || VotesPreferences.isGroupAd(ownerid)) { List<Long> postIds = PostService.getPostIdsByOwnerId((long) ownerid, 0L);
if (VotesPreferences.isEnabled()) { PostsPreferences.saveGroupSpecifiedPosts(postIds, (long) ownerid);
Map<String, List<String>> postIds = VotesService.getPostIdsByOwnerId((long) ownerid, 0L);
VotesPreferences.saveGroupSpecifiedPosts(postIds.get("vote"), (long) ownerid);
PostsPreferences.saveGroupSpecifiedPosts(postIds.get("prod"), (long) ownerid);
} else {
List<Long> postIds = PostService.getPostIdsByOwnerId((long) ownerid, 0L);
PostsPreferences.saveGroupSpecifiedPosts(postIds, (long) ownerid);
}
}
}); });
} }
} }
@@ -119,28 +112,17 @@ public class NewsfeedHook {
long ownerid = Long.parseLong(strArr[0].split("_")[0]); long ownerid = Long.parseLong(strArr[0].split("_")[0]);
long postId = Long.parseLong(strArr[0].split("_")[1]); long postId = Long.parseLong(strArr[0].split("_")[1]);
if (PostsPreferences.isPostAd(ownerid, postId) || VotesPreferences.isPostAd(ownerid, postId)) { if (PostsPreferences.isPostAd(ownerid, postId)) {
return; return;
} }
if (NetworkUtils.isNetworkConnected() && PostsPreferences.isEnabled() && !Preferences.serverFeaturesDisable()) { if (NetworkUtils.isNetworkConnected() && PostsPreferences.isEnabled() && !Preferences.serverFeaturesDisable() && PostsPreferences.isGroupAd(ownerid)) {
VTExecutors.getIoScheduler().a(() -> { VTExecutors.getIoScheduler().a(() -> {
if (VotesPreferences.isEnabled()) { boolean postIds = PostService.isPostAd(ownerid, postId);
boolean postIds = VotesService.isPostAd(ownerid, postId);
if (postIds) { if (postIds) {
VotesPreferences.saveAdPostInfo(ownerid, postId); PostsPreferences.saveAdPostInfo(ownerid, postId);
} sendToast("Этот пост помечен как рекламный");
} else {
boolean postIds = PostService.isPostAd(ownerid, postId);
if (postIds) {
PostsPreferences.saveAdPostInfo(ownerid, postId);
if (!PostsPreferences.isEnabledMarking()) {
sendToast("Этот пост помечен как рекламный");
}
}
} }
}); });
} }
@@ -1,23 +1,13 @@
package ru.vtosters.hooks; package ru.vtosters.hooks;
import android.util.Log; import android.util.Log;
import android.util.SparseArray;
import com.vk.core.network.Network;
import com.vk.im.engine.internal.api_parsers.UserApiParser;
import com.vk.im.engine.models.users.User;
import com.vtosters.lite.R; import com.vtosters.lite.R;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Response;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import ru.vtosters.hooks.other.Preferences; import ru.vtosters.hooks.other.Preferences;
import ru.vtosters.lite.utils.AndroidUtils; import ru.vtosters.lite.utils.AndroidUtils;
import java.io.IOException;
import static com.vk.core.network.Network.ClientType.CLIENT_API;
import static ru.vtosters.hooks.other.Preferences.*; import static ru.vtosters.hooks.other.Preferences.*;
import static ru.vtosters.lite.net.Request.makeRequest; import static ru.vtosters.lite.net.Request.makeRequest;
import static ru.vtosters.lite.proxy.ProxyUtils.getApi; import static ru.vtosters.lite.proxy.ProxyUtils.getApi;
@@ -95,34 +85,22 @@ public class OnlineFormatterHook {
if (appname != null) return appname; if (appname != null) return appname;
okhttp3.Request req = new okhttp3.Request.a() makeRequest("https://" + getApi() + "/method/apps.get?app_id=" + appid + "&v=5.99&access_token=" + getUserToken(),
.b("https://" + getApi() + "/method/apps.get?app_id=" + appid + "&v=5.99&access_token=" + getUserToken()) response -> {
.a(); try {
JSONObject mainJson = new JSONObject(response);
JSONObject responseJson = mainJson.getJSONObject("response");
JSONArray itemsJson = responseJson.getJSONArray("items");
Network.b(CLIENT_API).a(req).a(new Callback() { AppName = itemsJson.getJSONObject(0).optString("title", "");
@Override
public void a(Call call, IOException e) {
e.fillInStackTrace();
}
@Override prefs.edit().putString(String.valueOf(appid), AppName).apply();
public void a(Call call, Response response) throws IOException {
try {
JSONObject mainJson = new JSONObject(response.a().g());
JSONObject responseJson = mainJson.getJSONObject("response");
JSONArray itemsJson = responseJson.getJSONArray("items");
AppName = itemsJson.getJSONObject(0).optString("title", "");
prefs.edit().putString(String.valueOf(appid), AppName).apply();
if (dev()) sendToast("AppName: " + AppName + " for appid: " + appid);
} catch (JSONException e) {
e.fillInStackTrace();
}
}
});
if (dev()) sendToast("AppName: " + AppName + " for appid: " + appid);
} catch (JSONException e) {
e.printStackTrace();
}
});
return AppName; return AppName;
} }
@@ -147,36 +125,16 @@ public class OnlineFormatterHook {
return json; return json;
} }
public static SparseArray<User> onlineHookList(String str) { public static JSONArray onlineHookList(JSONArray jsonArr) {
JSONArray jSONArray; if (!getBoolValue("onlinefix", false) || Preferences.serverFeaturesDisable()) return jsonArr;
try { try {
jSONArray = new JSONObject(str).getJSONArray("response"); JsonInjectors.setOnlineInfoUsers(jsonArr);
} catch (JSONException e) { } catch (Exception e) {
throw new RuntimeException(e); Log.e("onlineHookProfiles", e.getMessage());
}
SparseArray<User> sparseArray = new SparseArray<>(jSONArray.length());
int length = jSONArray.length();
if (getBoolValue("onlinefix", false) || !Preferences.serverFeaturesDisable()) {
try {
jSONArray = JsonInjectors.setOnlineInfoUsers(jSONArray);
} catch (JSONException e) {
throw new RuntimeException(e);
}
} }
for (int i = 0; i < length; i++) { return jsonArr;
JSONObject jSONObject;
try {
jSONObject = jSONArray.getJSONObject(i);
} catch (JSONException e) {
throw new RuntimeException(e);
}
User b = UserApiParser.b(jSONObject);
sparseArray.put(b.getId(), b);
}
return sparseArray;
} }
public static JSONObject onlineHookProfiles(JSONObject json) { public static JSONObject onlineHookProfiles(JSONObject json) {
@@ -209,19 +167,19 @@ public class OnlineFormatterHook {
try { try {
JsonInjectors.setOnlineInfoUsers(json.optJSONObject("read_requests").optJSONArray("items")); JsonInjectors.setOnlineInfoUsers(json.optJSONObject("read_requests").optJSONArray("items"));
} catch (Exception e) { } catch (Exception e) {
Log.e("onlineHookItemsRec", e.getMessage()); Log.e("onlineHookItems", e.getMessage());
} }
try { try {
JsonInjectors.setOnlineInfoUsers(json.optJSONObject("recommendations").optJSONArray("items")); JsonInjectors.setOnlineInfoUsers(json.optJSONObject("recommendations").optJSONArray("items"));
} catch (Exception e) { } catch (Exception e) {
Log.e("onlineHookItemsRec", e.getMessage()); Log.e("onlineHookItems", e.getMessage());
} }
try { try {
JsonInjectors.setOnlineInfoUsers(json.optJSONArray("profiles")); JsonInjectors.setOnlineInfoUsers(json.optJSONArray("profiles"));
} catch (Exception e) { } catch (Exception e) {
Log.e("onlineHookItemsRec", e.getMessage()); Log.e("onlineHookItems", e.getMessage());
} }
return json; return json;
@@ -15,7 +15,6 @@ import android.widget.Toast;
import androidx.core.content.FileProvider; import androidx.core.content.FileProvider;
import com.vk.core.dialogs.actionspopup.ActionsPopup; import com.vk.core.dialogs.actionspopup.ActionsPopup;
import com.vk.core.dialogs.alert.VkAlertDialog; import com.vk.core.dialogs.alert.VkAlertDialog;
import com.vk.core.network.Network;
import com.vk.core.util.ToastUtils; import com.vk.core.util.ToastUtils;
import com.vk.dto.common.AttachmentWithMedia; import com.vk.dto.common.AttachmentWithMedia;
import com.vk.dto.common.ImageSize; import com.vk.dto.common.ImageSize;
@@ -91,7 +90,7 @@ public class PhotoViewer {
var req = new Request.a() var req = new Request.a()
.b(getImageUrlFromAttachment(attachment)) .b(getImageUrlFromAttachment(attachment))
.a(); .a();
Network.b(Network.ClientType.CLIENT_API).a(req).a(new Callback() { client.a(req).a(new Callback() {
@Override @Override
public void a(Call call, IOException e) { public void a(Call call, IOException e) {
e.printStackTrace(); e.printStackTrace();
@@ -101,7 +100,7 @@ public class PhotoViewer {
public void a(Call call, Response response) public void a(Call call, Response response)
throws IOException { throws IOException {
var tmpImage = new File(AndroidUtils.getGlobalContext().getExternalCacheDir(), attachment.getId() + ".jpg"); var tmpImage = new File(AndroidUtils.getGlobalContext().getExternalCacheDir(), attachment.getId() + ".jpg");
try (var resp = Network.b(Network.ClientType.CLIENT_IMAGE_LOADER).a(req).execute(); try (var resp = client.a(req).execute();
var sink = Okio.a(Okio.b(tmpImage))) { var sink = Okio.a(Okio.b(tmpImage))) {
sink.a(resp.a().f()); sink.a(resp.a().f());
var manager = (ClipboardManager) AndroidUtils.getGlobalContext().getSystemService(Context.CLIPBOARD_SERVICE); var manager = (ClipboardManager) AndroidUtils.getGlobalContext().getSystemService(Context.CLIPBOARD_SERVICE);
@@ -1,16 +0,0 @@
package ru.vtosters.hooks;
import com.vtosters.lite.api.execute.GetWallInfo;
import java.util.ArrayList;
/**
* Since June 12, 2024, there are troubles with API method "execute.getUserInfo" in the old versions of VK Client (<6.16),
* to fix that issue we need to upgrade "execute.getUserInfo" for new API versions
* and implement the new API method "newsfeed.getPostTopics" (TODO)*/
@SuppressWarnings("unused")
public class PostTopicsHook {
public static void hook(final GetWallInfo.a model) throws Exception {
model.f = new ArrayList<>();
}
}
@@ -1,43 +0,0 @@
package ru.vtosters.hooks;
import android.content.Context;
import android.util.Log;
import com.vk.dto.newsfeed.entries.NewsEntry;
import com.vk.dto.newsfeed.entries.Post;
import com.vk.newsfeed.NewsEntryActionsAdapter;
import com.vk.newsfeed.holders.AdMarkHolder;
import ru.vtosters.hooks.other.Preferences;
import ru.vtosters.lite.utils.AccountManagerUtils;
import ru.vtosters.sponsorpost.internal.Native;
import ru.vtosters.sponsorpost.utils.PostsPreferences;
public class PostsMenuHook {
private static final int id = 200;
public static void injectList(NewsEntryActionsAdapter actionsAdapter, NewsEntry newsEntry) {
if (newsEntry instanceof Post && Preferences.isValidSignature() && Native.canVote()) {
int ownerId = ((Post) newsEntry).b();
if (ownerId != AccountManagerUtils.getUserId() && PostsPreferences.isEnabled()) {
actionsAdapter.a(id, "SponsorPost");
}
}
}
public static void injectButtons(int i, NewsEntry newsEntry, Context context) {
if (newsEntry instanceof Post) {
Post post = (Post) newsEntry;
int ownerId = post.b();
int postId = post.P1();
int date = post.K();
AdMarkHolder.voteDialog(ownerId, postId, date, context);
} else {
Log.d("PostsMenuHook", "unsupported instance");
}
}
public static boolean isCustomButton(int i) {
return i == id;
}
}
@@ -26,15 +26,26 @@ import static ru.vtosters.lite.utils.LifecycleUtils.restartApplication;
public class ProxyHook { public class ProxyHook {
public static String linkReplacer(String link) { public static String linkReplacer(String link) {
String vkapi = "api.vk.com"; var vkapi = "api.vk.com";
String oauth = "oauth.vk.com"; var oauth = "oauth.vk.com";
String vkstatic = "static.vk.com"; var vkstatic = "static.vk.com";
String proxyapi = getProxyHost("proxyapi"); var proxyapi = getString("proxyapi");
String proxyoauth = getProxyHost("proxyoauth"); var proxyoauth = getString("proxyoauth");
String proxystatic = getProxyHost("proxystatic"); var proxystatic = getString("proxystatic");
if (!isAnyProxyEnabled() || link.isEmpty() || areProxyHostsEmpty(proxyapi, proxyoauth, proxystatic)) { if (isVikaProxyEnabled()) {
proxyapi = VikaMobile.getApiHost();
proxyoauth = VikaMobile.getOauthHost();
proxystatic = VikaMobile.getStaticHost();
}
if (!isAnyProxyEnabled() || link.isEmpty()) {
return link;
}
if (proxyapi.isEmpty() || proxyoauth.isEmpty() || proxystatic.isEmpty()) {
Log.d("VTLite", "Proxy is not set" + " " + proxyapi + " " + proxyoauth + " " + proxystatic);
return link; return link;
} }
@@ -53,29 +64,6 @@ public class ProxyHook {
return link; return link;
} }
private static String getProxyHost(String hostType) {
if (isVikaProxyEnabled()) {
return switch (hostType) {
case "proxyapi" -> VikaMobile.getApiHost();
case "proxyoauth" -> VikaMobile.getOauthHost();
case "proxystatic" -> VikaMobile.getStaticHost();
default -> "";
};
} else {
return "";
}
}
private static boolean areProxyHostsEmpty(String... hosts) {
for (String host : hosts) {
if (host.isEmpty()) {
Log.d("VTLite", "Proxy is not set: " + host);
return true;
}
}
return false;
}
public static String staticFix(String str) { public static String staticFix(String str) {
var string = getString("proxystatic"); var string = getString("proxystatic");
@@ -120,18 +108,22 @@ public class ProxyHook {
RadioButton rgDefault = new RadioButton(new ContextThemeWrapper(ctx, com.vtosters.lite.R.style.Widget_AppCompat_CompoundButton_RadioButton)); RadioButton rgDefault = new RadioButton(new ContextThemeWrapper(ctx, com.vtosters.lite.R.style.Widget_AppCompat_CompoundButton_RadioButton));
RadioButton rgZaborona = new RadioButton(new ContextThemeWrapper(ctx, com.vtosters.lite.R.style.Widget_AppCompat_CompoundButton_RadioButton)); RadioButton rgZaborona = new RadioButton(new ContextThemeWrapper(ctx, com.vtosters.lite.R.style.Widget_AppCompat_CompoundButton_RadioButton));
// RadioButton rgRandomProxy = new RadioButton(new ContextThemeWrapper(ctx, com.vtosters.lite.R.style.Widget_AppCompat_CompoundButton_RadioButton));
RadioButton rgVika = new RadioButton(new ContextThemeWrapper(ctx, com.vtosters.lite.R.style.Widget_AppCompat_CompoundButton_RadioButton)); RadioButton rgVika = new RadioButton(new ContextThemeWrapper(ctx, com.vtosters.lite.R.style.Widget_AppCompat_CompoundButton_RadioButton));
SwitchHook.setCompoundButton(rgDefault); SwitchHook.setCompoundButton(rgDefault);
SwitchHook.setCompoundButton(rgZaborona); SwitchHook.setCompoundButton(rgZaborona);
// SwitchHook.setCompoundButton(rgRandomProxy);
SwitchHook.setCompoundButton(rgVika); SwitchHook.setCompoundButton(rgVika);
rg.addView(rgDefault); rg.addView(rgDefault);
rg.addView(rgVika); rg.addView(rgVika);
rg.addView(rgZaborona); rg.addView(rgZaborona);
// rg.addView(rgRandomProxy);
rgDefault.setTextSize(TypedValue.COMPLEX_UNIT_PX, dp2px(14f)); rgDefault.setTextSize(TypedValue.COMPLEX_UNIT_PX, dp2px(14f));
rgZaborona.setTextSize(TypedValue.COMPLEX_UNIT_PX, dp2px(14f)); rgZaborona.setTextSize(TypedValue.COMPLEX_UNIT_PX, dp2px(14f));
// rgRandomProxy.setTextSize(TypedValue.COMPLEX_UNIT_PX, dp2px(14f));
rgVika.setTextSize(TypedValue.COMPLEX_UNIT_PX, dp2px(14f)); rgVika.setTextSize(TypedValue.COMPLEX_UNIT_PX, dp2px(14f));
rg.setPadding(dp2px(18f), dp2px(12f), dp2px(18f), 0); rg.setPadding(dp2px(18f), dp2px(12f), dp2px(18f), 0);
@@ -142,21 +134,28 @@ public class ProxyHook {
rgZaborona.setText(ctx.getString(R.string.proxy_enable) + " (Zaborona)"); rgZaborona.setText(ctx.getString(R.string.proxy_enable) + " (Zaborona)");
rgZaborona.setTextColor(getTextAttr()); rgZaborona.setTextColor(getTextAttr());
// rgRandomProxy.setText(ctx.getString(R.string.proxy_enable) + " (Random Socks Proxy)");
// rgRandomProxy.setTextColor(getTextAttr());
rgVika.setText(ctx.getString(R.string.proxy_enable) + " (Vika Mobile)"); rgVika.setText(ctx.getString(R.string.proxy_enable) + " (Vika Mobile)");
rgVika.setTextColor(getTextAttr()); rgVika.setTextColor(getTextAttr());
rgZaborona.setChecked(isZaboronaEnabled()); rgZaborona.setChecked(isZaboronaEnabled());
rgDefault.setChecked(!isZaboronaEnabled() && !isVikaProxyEnabled()); rgDefault.setChecked(!isZaboronaEnabled() && !isRandomProxyEnabled() && !isVikaProxyEnabled());
// rgRandomProxy.setChecked(isRandomProxyEnabled());
rgVika.setChecked(isVikaProxyEnabled()); rgVika.setChecked(isVikaProxyEnabled());
new VkAlertDialog.Builder(ctx) new VkAlertDialog.Builder(ctx)
.setTitle(ctx.getString(R.string.vtlproxy)) .setTitle(ctx.getString(R.string.vtlproxy))
.setMessage(ctx.getString(R.string.proxy_warning) + "\n\nТак же можно включить встроенный прокси от ВКонтакте перейдя в настройки прокси") .setMessage(ctx.getString(R.string.proxy_warning))
.setView(rg) .setView(rg)
.setPositiveButton(ctx.getString(R.string.vtl_confirm), ((dialog, which) -> { // Применить .setPositiveButton(ctx.getString(R.string.vtl_confirm), ((dialog, which) -> { // Применить
if (rgZaborona.isChecked()) { if (rgZaborona.isChecked()) {
Preferences.getPreferences().edit().putString("proxy", "zaborona").commit(); Preferences.getPreferences().edit().putString("proxy", "zaborona").commit();
restartApplication(); restartApplication();
// } else if (rgRandomProxy.isChecked()) {
// Preferences.getPreferences().edit().putString("proxy", "randomproxy").commit();
// restartApplication();
} else if (rgVika.isChecked()) { } else if (rgVika.isChecked()) {
Preferences.getPreferences().edit().putString("proxy", "vika").commit(); Preferences.getPreferences().edit().putString("proxy", "vika").commit();
restartApplication(); restartApplication();
@@ -1,40 +1,29 @@
package ru.vtosters.hooks; package ru.vtosters.hooks;
import androidx.core.util.Pair;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import ru.vtosters.hooks.other.Preferences;
import ru.vtosters.lite.utils.RenameTool;
public class RenameHook { public class RenameHook {
public static void injectIntoJson(JSONObject obj) throws JSONException { public static void injectIntoJson(JSONObject obj) throws JSONException {
if (isEnabled()) { // int i = obj.getInt("id");
int i = obj.getInt("id"); // if (RenameTool.updateRequested) {
if (RenameTool.updateRequested) { // RenameTool.reloadDB();
RenameTool.reloadDB(); // }
} //
// Pair<String, String> user = RenameTool.renamedUsers.get(i);
Pair<String, String> user = RenameTool.renamedUsers.get(i); // if (user == null) return;
if (user == null) return; // obj.put(RenameTool.COLUMN_FIRSTNAME, user.first).put(RenameTool.COLUMN_LASTNAME, user.second);
obj.put(RenameTool.COLUMN_FIRSTNAME, user.first).put(RenameTool.COLUMN_LASTNAME, user.second);
}
}
public static boolean isEnabled() {
return Preferences.getBoolValue("unstableNameChanger", true);
} }
public static void injectIntoJsonGroup(JSONObject obj) throws JSONException { public static void injectIntoJsonGroup(JSONObject obj) throws JSONException {
if (isEnabled()) { // int i = obj.getInt("id");
int i = obj.getInt("id"); //
// if (RenameTool.updateRequested) {
if (RenameTool.updateRequested) { // RenameTool.reloadDB();
RenameTool.reloadDB(); // }
} //
// String user = RenameTool.renamedGroups.get(i);
String user = RenameTool.renamedGroups.get(i); // if (user == null) return;
if (user == null) return; // obj.put("name", user);
obj.put("name", user);
}
} }
} }
@@ -1,29 +0,0 @@
package ru.vtosters.hooks;
import android.os.Build;
import me.leolin.shortcutbadger.Badger;
import me.leolin.shortcutbadger.impl.*;
import java.util.List;
public class ShortcutBadgerHook {
public static void init(List<Class<? extends Badger>> list) {
list.add(AdwHomeBadger.class);
list.add(ApexHomeBadger.class);
list.add(DefaultBadger.class);
list.add(NewHtcHomeBadger.class);
list.add(NovaHomeBadger.class);
list.add(AsusHomeBadger.class);
list.add(HuaweiHomeBadger.class);
list.add(OPPOHomeBader.class);
list.add(SamsungHomeBadger.class);
list.add(ZukHomeBadger.class);
list.add(VivoHomeBadger.class);
list.add(ZTEHomeBadger.class);
list.add(EverythingMeHomeBadger.class);
if (Build.VERSION.SDK_INT < 34) {
list.add(SonyHomeBadger.class);
}
}
}
@@ -3,17 +3,15 @@ package ru.vtosters.hooks;
import com.vk.medianative.MediaImageEncoder; import com.vk.medianative.MediaImageEncoder;
import com.vk.toggle.FeatureManager; import com.vk.toggle.FeatureManager;
import com.vk.toggle.Features; import com.vk.toggle.Features;
import ru.vtosters.hooks.other.Preferences;
import static ru.vtosters.hooks.other.Preferences.*; import static ru.vtosters.hooks.other.Preferences.*;
public class TogglesHook { public class TogglesHook {
public static boolean shouldPatch(Features.Type feature) { public static boolean shouldPatch(Features.Type feature) {
return switch (feature) { return switch (feature) {
case AB_IM_LONGPOLL_MSG_BATCHING, AB_IM_VIEW_POOL, AB_MULTI_GIFTS, AB_NEWS_VIDEO_LAYOUT_TEXT, AB_STICKERS_DISCOVER, AB_GIFTS_FROM_KEYBOARD, EXPERIMENT_NEWS_VIDEO_LAYOUT_TEXT, FEATURE_DISCOVER_CATEGORIES, FEATURE_FAVE_PERF_IMPROVE, FEATURE_GROUP_ADMIN_MESSAGES, FEATURE_GROUP_MSG_PUSH_PARAM, FEATURE_IMAGE_QUALITY_UPGRADE, FEATURE_IM_AUDIO_MSG_TRANSCRIPT, FEATURE_IM_CASPER_MSGS, FEATURE_IM_DISABLE_FORCE_OPEN_VK_ME, FEATURE_IM_GIF_AUTOPLAY, FEATURE_IM_MR_IN_VKAPP, FEATURE_MEMORIES_ENABLED, FEATURE_MENU_GAMES_CAROUSEL, FEATURE_MILKSHAKE, FEATURE_MILKSHAKE_NEWS_SCROLL_ON_BACK, FEATURE_MILKSHAKE_SWITCH_THEME_ON_TAP, FEATURE_ML_BRANDS, FEATURE_ML_FEATURES, FEATURE_ML_MODELS_LOADING, FEATURE_MONEY_TRANSFERS_VKPAY, FEATURE_MUSIC_ARTIST_CATALOG, FEATURE_MUSIC_NEW_CATALOG, FEATURE_NARRATIVE_SNIPPET_TYPE, FEATURE_NEWS_GAMES_IN_DISCOVER, FEATURE_NEWS_HEADER_SCROLL, FEATURE_NEW_FORMAT_SHOW_WPB, FEATURE_PODCASTS_PAGE, FEATURE_QR_CREATE_QR, FEATURE_QR_DYNAMIC_FRAME, FEATURE_QR_SCAN_FROM_PHOTO, FEATURE_QR_VISION_DECODER, FEATURE_QUEUE_COUNTERS, FEATURE_REEF, FEATURE_ROAMING_AUTOPLAY, FEATURE_SHARE_MSGS_ON_INVITE, FEATURE_STICKERS_BOT_LINK, FEATURE_STICKERS_NEW_CATALOG, FEATURE_STORIES_AVATAR, FEATURE_STORIES_FEED_TYPE, FEATURE_STORIES_SHOW_ALWAYS, FEATURE_STORY_ANSWER_PUBLIC, FEATURE_STORY_APP_STICKER, FEATURE_STORY_ARCHIVE, FEATURE_STORY_ARCHIVE_POST, FEATURE_STORY_BAKGROUND_ALL, FEATURE_STORY_BOX, FEATURE_STORY_CADRE, FEATURE_STORY_CAMERA_TOOLTIP, FEATURE_STORY_EDITOR_GALLERY, FEATURE_STORY_EDITOR_TYPE, FEATURE_STORY_ENDLESS_VIDEO, FEATURE_STORY_FAST_REACTIONS, FEATURE_STORY_GIF, FEATURE_STORY_GROUPED, FEATURE_STORY_HIGH_QUALITY, FEATURE_STORY_MASKS_FRONTAL, FEATURE_STORY_MENTION_ICON, FEATURE_STORY_MODERN_PUBLISH, FEATURE_STORY_MUSIC_EDITOR, FEATURE_STORY_MUSIC_REPLIES, FEATURE_STORY_MUSIC_REPLIES_2, FEATURE_STORY_NEW_FRAME, FEATURE_STORY_ONE_TIME, FEATURE_STORY_PHOTO_SHARING, FEATURE_STORY_PHOTO_STICKER, FEATURE_STORY_POLLS, FEATURE_STORY_POST_REPOST, FEATURE_STORY_PRELOADING, FEATURE_STORY_QUESTION, FEATURE_STORY_QUESTION_STYLE, FEATURE_STORY_RLOTTIE, FEATURE_STORY_RLOTTIE_CACHE, FEATURE_STORY_SAVE_DEFAULT_ON, FEATURE_STORY_SEND_DIALOG_LIST, FEATURE_STORY_STICKER_PACK, FEATURE_STORY_TIME, FEATURE_STORY_VIEWER_CAMERA, FEATURE_STORY_VOICE_ANSWER, FEATURE_STORY_WITH_CONTEXT, FEATURE_SUPERAPP_MENU, FEATURE_TRAFFIC_SAVER, FEATURE_VKPAY_WIDGET, FEATURE_VKUI_INTERNAL_TO_MINI_APPS, FEATURE_VKUI_TOKEN_CACHE, FEATURE_VK_APPS_SEARCH, FEATURE_VOTES_BALANCE, FEATURE_WEBVIEW_TOKEN_ACTIVE, FEATURE_MILKSHAKE_FORCE_DISABLED, FEATURE_MILKSHAKE_ACTIVATION_DISABLED, EXPERIMENT_NEWS_DISABLE_CACHE -> case AB_MULTI_GIFTS, AB_NEWS_VIDEO_LAYOUT_TEXT, AB_STICKERS_DISCOVER, AB_GIFTS_FROM_KEYBOARD, EXPERIMENT_NEWS_VIDEO_LAYOUT_TEXT, FEATURE_DISCOVER_CATEGORIES, FEATURE_FAVE_PERF_IMPROVE, FEATURE_GROUP_ADMIN_MESSAGES, FEATURE_GROUP_MSG_PUSH_PARAM, FEATURE_IMAGE_QUALITY_UPGRADE, FEATURE_IM_AUDIO_MSG_TRANSCRIPT, FEATURE_IM_CASPER_MSGS, FEATURE_IM_DISABLE_FORCE_OPEN_VK_ME, FEATURE_IM_GIF_AUTOPLAY, FEATURE_IM_MR_IN_VKAPP, FEATURE_MEMORIES_ENABLED, FEATURE_MENU_GAMES_CAROUSEL, FEATURE_MILKSHAKE, FEATURE_MILKSHAKE_NEWS_SCROLL_ON_BACK, FEATURE_MILKSHAKE_SWITCH_THEME_ON_TAP, FEATURE_ML_BRANDS, FEATURE_ML_FEATURES, FEATURE_ML_MODELS_LOADING, FEATURE_MONEY_TRANSFERS_VKPAY, FEATURE_MUSIC_ARTIST_CATALOG, FEATURE_MUSIC_NEW_CATALOG, FEATURE_NARRATIVE_SNIPPET_TYPE, FEATURE_NEWS_GAMES_IN_DISCOVER, FEATURE_NEWS_HEADER_SCROLL, FEATURE_NEW_FORMAT_SHOW_WPB, FEATURE_PODCASTS_PAGE, FEATURE_QR_CREATE_QR, FEATURE_QR_DYNAMIC_FRAME, FEATURE_QR_SCAN_FROM_PHOTO, FEATURE_QR_VISION_DECODER, FEATURE_QUEUE_COUNTERS, FEATURE_REEF, FEATURE_ROAMING_AUTOPLAY, FEATURE_SHARE_MSGS_ON_INVITE, FEATURE_STICKERS_BOT_LINK, FEATURE_STICKERS_NEW_CATALOG, FEATURE_STORIES_AVATAR, FEATURE_STORIES_FEED_TYPE, FEATURE_STORIES_SHOW_ALWAYS, FEATURE_STORY_ANSWER_PUBLIC, FEATURE_STORY_APP_STICKER, FEATURE_STORY_ARCHIVE, FEATURE_STORY_ARCHIVE_POST, FEATURE_STORY_BAKGROUND_ALL, FEATURE_STORY_BOX, FEATURE_STORY_CADRE, FEATURE_STORY_CAMERA_TOOLTIP, FEATURE_STORY_EDITOR_GALLERY, FEATURE_STORY_EDITOR_TYPE, FEATURE_STORY_ENDLESS_VIDEO, FEATURE_STORY_FAST_REACTIONS, FEATURE_STORY_GIF, FEATURE_STORY_GROUPED, FEATURE_STORY_HIGH_QUALITY, FEATURE_STORY_MASKS_FRONTAL, FEATURE_STORY_MENTION_ICON, FEATURE_STORY_MODERN_PUBLISH, FEATURE_STORY_MUSIC_EDITOR, FEATURE_STORY_MUSIC_REPLIES, FEATURE_STORY_MUSIC_REPLIES_2, FEATURE_STORY_NEW_FRAME, FEATURE_STORY_ONE_TIME, FEATURE_STORY_PHOTO_SHARING, FEATURE_STORY_PHOTO_STICKER, FEATURE_STORY_POLLS, FEATURE_STORY_POST_REPOST, FEATURE_STORY_PRELOADING, FEATURE_STORY_QUESTION, FEATURE_STORY_QUESTION_STYLE, FEATURE_STORY_RLOTTIE, FEATURE_STORY_RLOTTIE_CACHE, FEATURE_STORY_SAVE_DEFAULT_ON, FEATURE_STORY_SEND_DIALOG_LIST, FEATURE_STORY_STICKER_PACK, FEATURE_STORY_TIME, FEATURE_STORY_VIEWER_CAMERA, FEATURE_STORY_VOICE_ANSWER, FEATURE_STORY_WITH_CONTEXT, FEATURE_SUPERAPP_MENU, FEATURE_TRAFFIC_SAVER, FEATURE_VKPAY_WIDGET, FEATURE_VKUI_INTERNAL_TO_MINI_APPS, FEATURE_VKUI_TOKEN_CACHE, FEATURE_VK_APPS_SEARCH, FEATURE_VOTES_BALANCE, FEATURE_WEBVIEW_TOKEN_ACTIVE, FEATURE_MILKSHAKE_FORCE_DISABLED, FEATURE_MILKSHAKE_ACTIVATION_DISABLED, EXPERIMENT_NEWS_DISABLE_CACHE ->
true; true;
default -> default -> false;
false;
}; };
} }
@@ -25,10 +23,9 @@ public class TogglesHook {
case FEATURE_MILKSHAKE_FORCE_DISABLED, FEATURE_MILKSHAKE_ACTIVATION_DISABLED -> !milkshake(); case FEATURE_MILKSHAKE_FORCE_DISABLED, FEATURE_MILKSHAKE_ACTIVATION_DISABLED -> !milkshake();
case AB_NEWS_VIDEO_LAYOUT_TEXT, FEATURE_COMPACT_REPOST, EXPERIMENT_NEWS_VIDEO_LAYOUT_TEXT -> case AB_NEWS_VIDEO_LAYOUT_TEXT, FEATURE_COMPACT_REPOST, EXPERIMENT_NEWS_VIDEO_LAYOUT_TEXT ->
postsredesign(); postsredesign();
case FEATURE_IMAGE_QUALITY_UPGRADE -> !MediaImageEncoder.needToCompress(); case FEATURE_IMAGE_QUALITY_UPGRADE -> MediaImageEncoder.needToSkipCompression();
case FEATURE_MENU_GAMES_CAROUSEL -> miniapps(); case FEATURE_MENU_GAMES_CAROUSEL -> miniapps();
case FEATURE_TRAFFIC_SAVER -> !disableForceTrafficSaver(); case FEATURE_TRAFFIC_SAVER -> !disableForceTrafficSaver();
case AB_IM_LONGPOLL_MSG_BATCHING, AB_IM_VIEW_POOL -> Preferences.getBoolValue("batchmessages", false);
default -> true; default -> true;
}; };
} }
@@ -0,0 +1,93 @@
package ru.vtosters.hooks;
import android.util.Log;
import com.vk.core.network.Network;
import com.vk.core.network.proxy.NetworkProxy;
import com.vk.core.network.proxy.ProxyHost;
import com.vk.core.network.proxy.ProxySettings;
import com.vk.core.preference.Preference;
import com.vtosters.lite.NetworkProxyPreferences;
import okhttp3.*;
import org.json.JSONException;
import org.json.JSONObject;
import ru.vtosters.hooks.other.Preferences;
import ru.vtosters.lite.di.singleton.VtOkHttpClient;
import ru.vtosters.lite.utils.AndroidUtils;
import java.io.IOException;
public class VKProxy {
private static final OkHttpClient sClient = VtOkHttpClient.getInstance();
private static String nwProxy = Preferences.getPreferences().getString("configNetworkProxy", "");
private static String nwProxyCerts = Preferences.getPreferences().getString("configNetworkProxyCerts", "");
public static boolean isProxyEnabled() {
return Preferences.getBoolValue("useProxyServer", false);
}
public static void setProxyStatus(boolean status) {
Preferences.getPreferences().edit().putBoolean("useProxyServer", status).apply();
}
public static String getConfigNetworkProxy() {
if (nwProxyCerts.isEmpty()) {
load();
}
return nwProxy;
}
public static String getConfigNetworkProxyCerts() {
if (nwProxyCerts.isEmpty()) {
load();
}
return nwProxyCerts;
}
public static void load() {
Request request = new Request.a()
.b("https://firebaseremoteconfig.googleapis.com/v1/projects/api-project-841415684880/namespaces/firebase:fetch")
.a(RequestBody.a(MediaType.b("application/json; charset=UTF-8"), "{\"appId\":\"1:841415684880:android:632f429381141121\",\"appInstanceId\":\"RB855168585SG\"}\n"))
// .a("Content-Encoding", "gzip")
// .a("Accept-Encoding", "gzip")
.a("Connection", "Keep-Alive")
.a("X-Android-Package", "com.vkontakte.android")
.a("X-Android-Cert", "C8EAB7342DA62B4A229E1BFDEB27B029DD6788A1")
.a("Host", "firebaseremoteconfig.googleapis.com")
.a("x-goog-api-key", "AIzaSyCL17U2Q5i1NVwIcXgMOZMidSRFHyGYgwM")
.a("User-Agent", Network.l.c().a())
.a();
sClient.a(request).a(new Callback() {
@Override
public void a(Call call, IOException e) {
Log.d("VKProxy", e.getMessage());
}
@Override
public void a(Call call, Response response) throws IOException {
String payload = response.a().g();
try {
JSONObject jsonObject = new JSONObject(payload).getJSONObject("entries");
String configNetworkProxy = jsonObject.getString("config_network_proxy");
String configNetworkProxyCerts = jsonObject.getString("config_network_proxy_certs");
Log.d("VKProxy", configNetworkProxy);
Log.d("VKProxy", configNetworkProxyCerts);
new ProxySettings().c(configNetworkProxy);
new ProxySettings().b(configNetworkProxyCerts);
Preferences.getPreferences().edit().putString("configNetworkProxy", configNetworkProxy).putString("configNetworkProxyCerts", configNetworkProxyCerts).apply();
nwProxy = configNetworkProxy;
nwProxyCerts = configNetworkProxyCerts;
AndroidUtils.sendToast("Инициализированы прокси");
} catch (JSONException e) {
throw new RuntimeException(e);
}
}
});
}
}
@@ -24,7 +24,9 @@ public class VKUIHook {
private static void applyVKUIStyles(WebView webView) { private static void applyVKUIStyles(WebView webView) {
if (getBoolValue("VKUI_INJ", true) && webView != null && webView.getUrl() != null && !webView.getUrl().contains("static.vk.com/memories")) { if (getBoolValue("VKUI_INJ", true) && webView != null && webView.getUrl() != null && !webView.getUrl().contains("static.vk.com/memories")) {
WebViewColoringUtils.load(); if (!WebViewColoringUtils.isLoaded) {
WebViewColoringUtils.load();
}
loadAndApplyCSS(webView); loadAndApplyCSS(webView);
} }
} }
@@ -9,7 +9,6 @@ import com.vk.music.common.MusicPlaybackLaunchContext;
import com.vtosters.lite.R; import com.vtosters.lite.R;
import ru.vtosters.lite.downloaders.AudioDownloader; import ru.vtosters.lite.downloaders.AudioDownloader;
import ru.vtosters.lite.music.cache.MusicCacheImpl; import ru.vtosters.lite.music.cache.MusicCacheImpl;
import ru.vtosters.lite.music.cache.delegate.PlaylistCacheDbDelegate;
import ru.vtosters.lite.utils.AndroidUtils; import ru.vtosters.lite.utils.AndroidUtils;
import ru.vtosters.lite.utils.music.VKXUtils; import ru.vtosters.lite.utils.music.VKXUtils;
@@ -20,29 +19,18 @@ import static ru.vtosters.hooks.other.Preferences.milkshake;
import static ru.vtosters.lite.utils.NetworkUtils.isNetworkConnected; import static ru.vtosters.lite.utils.NetworkUtils.isNetworkConnected;
public class MusicBottomSheetHook { public class MusicBottomSheetHook {
public static ArrayList<MusicAction> hook(ArrayList<MusicAction> actions, MusicTrack musicTrack) { public static ArrayList<MusicAction> hook(final ArrayList<MusicAction> actions, final MusicTrack musicTrack) {
if (musicTrack.F1()) return actions; if (musicTrack.F1()) return actions;
final var trackId = asId(musicTrack);
String trackId = asId(musicTrack);
if (isVkxInstalled()) actions.add(getPlayInVKXAction()); if (isVkxInstalled()) actions.add(getPlayInVKXAction());
if (isIntegrationEnabled()) { if (isIntegrationEnabled()) {
if (VKXUtils.isVkxCached(trackId)) { if (VKXUtils.isVkxCached(trackId)) actions.add(getRemoveCacheTrackVkxAction());
actions.add(getRemoveCacheTrackVkxAction()); else if (isNetworkConnected()) actions.add(addToCacheTrackVkxAction());
} else if (isNetworkConnected()) {
actions.add(addToCacheTrackVkxAction());
}
} else { } else {
if (MusicCacheImpl.isCachedTrack(trackId)) { if (MusicCacheImpl.isCachedTrack(trackId)) actions.add(getRemoveCacheTrackAction());
actions.add(getRemoveCacheTrackAction()); else if (isNetworkConnected()) actions.add(addToCacheTrackAction());
} else if (isNetworkConnected()) {
actions.add(addToCacheTrackAction());
}
} }
if (isNetworkConnected()) actions.add(downloadAsMp3Action()); if (isNetworkConnected()) actions.add(downloadAsMp3Action());
return actions; return actions;
} }
@@ -84,12 +72,8 @@ public class MusicBottomSheetHook {
} else if (isNetworkConnected()) { } else if (isNetworkConnected()) {
actions.add(addToCacheTrackVkxAction()); actions.add(addToCacheTrackVkxAction());
} }
} else { } else if (isNetworkConnected()) {
if (PlaylistCacheDbDelegate.isCachedPlaylist(AndroidUtils.getGlobalContext(), playlist.v1())) { actions.add(addToCacheTrackAction());
actions.add(getRemoveCacheTrackAction());
} else if (isNetworkConnected()) {
actions.add(addToCacheTrackAction());
}
} }
if (isNetworkConnected()) actions.add(downloadAsMp3Action()); if (isNetworkConnected()) actions.add(downloadAsMp3Action());
@@ -150,14 +134,10 @@ public class MusicBottomSheetHook {
} }
public static boolean injectOnClick(int actionId, Playlist playlist) { // playlist inj public static boolean injectOnClick(int actionId, Playlist playlist) { // playlist inj
if (actionId == R.id.play_in_vkx) { if (actionId == R.id.play_in_vkx)
return tryPlayInVKX(null, null, playlist); return tryPlayInVKX(null, null, playlist);
}
if (actionId == R.id.remove_from_cache) { if (actionId == R.id.add_to_cache) {
PlaylistCacheDbDelegate.deletePlaylist(AndroidUtils.getGlobalContext(), playlist.v1());
return true;
} else if (actionId == R.id.add_to_cache) {
AudioDownloader.cachePlaylist(playlist); AudioDownloader.cachePlaylist(playlist);
return true; return true;
} }
@@ -167,7 +147,7 @@ public class MusicBottomSheetHook {
try { try {
service.deletePlaylistFromCache(playlist.a, playlist.b); service.deletePlaylistFromCache(playlist.a, playlist.b);
} catch (RemoteException e) { } catch (RemoteException e) {
e.fillInStackTrace(); e.printStackTrace();
} }
}); });
return true; return true;
@@ -176,7 +156,7 @@ public class MusicBottomSheetHook {
try { try {
service.addPlaylistToCache(playlist.a, playlist.b, (playlist.Q != null) ? playlist.Q : ""); service.addPlaylistToCache(playlist.a, playlist.b, (playlist.Q != null) ? playlist.Q : "");
} catch (RemoteException e) { } catch (RemoteException e) {
e.fillInStackTrace(); e.printStackTrace();
} }
}); });
return true; return true;
@@ -188,7 +168,7 @@ public class MusicBottomSheetHook {
try { try {
service.downloadPlaylist(playlist.a, playlist.b, (playlist.Q != null) ? playlist.Q : ""); service.downloadPlaylist(playlist.a, playlist.b, (playlist.Q != null) ? playlist.Q : "");
} catch (RemoteException e) { } catch (RemoteException e) {
e.fillInStackTrace(); e.printStackTrace();
} }
}); });
} else { } else {
@@ -4,22 +4,20 @@ import android.util.Log;
import bruhcollective.itaysonlab.libvkx.client.LibVKXClient; import bruhcollective.itaysonlab.libvkx.client.LibVKXClient;
import com.vk.core.network.Network; import com.vk.core.network.Network;
import com.vk.core.util.DeviceIdProvider; import com.vk.core.util.DeviceIdProvider;
import okhttp3.Headers;
import okhttp3.OkHttpClient;
import okhttp3.Request; import okhttp3.Request;
import okhttp3.Response;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import ru.vtosters.hooks.other.Preferences; import ru.vtosters.hooks.other.Preferences;
import ru.vtosters.lite.music.cache.helpers.PlaylistHelper; import ru.vtosters.lite.di.singleton.VtOkHttpClient;
import ru.vtosters.lite.music.cache.MusicCacheImpl; import ru.vtosters.lite.music.cache.MusicCacheImpl;
import ru.vtosters.lite.utils.AccountManagerUtils; import ru.vtosters.lite.utils.AccountManagerUtils;
import ru.vtosters.lite.utils.AndroidUtils; import ru.vtosters.lite.utils.AndroidUtils;
import ru.vtosters.sponsorpost.utils.GzipDecompressor;
import java.io.IOException; import java.io.IOException;
import java.util.Objects;
import static com.vk.core.network.Network.ClientType.CLIENT_API;
import static ru.vtosters.hooks.DateHook.getLocale; import static ru.vtosters.hooks.DateHook.getLocale;
import static ru.vtosters.hooks.GroupsCatalogInjector.injectIntoCatalog; import static ru.vtosters.hooks.GroupsCatalogInjector.injectIntoCatalog;
import static ru.vtosters.hooks.other.Preferences.getBoolValue; import static ru.vtosters.hooks.other.Preferences.getBoolValue;
@@ -28,6 +26,8 @@ import static ru.vtosters.lite.proxy.ProxyUtils.getApi;
import static ru.vtosters.lite.utils.AccountManagerUtils.getUserId; import static ru.vtosters.lite.utils.AccountManagerUtils.getUserId;
public class CatalogJsonInjector { public class CatalogJsonInjector {
private static final OkHttpClient mClient = VtOkHttpClient.getInstance();
public static JSONObject music(JSONObject json) throws JSONException { public static JSONObject music(JSONObject json) throws JSONException {
var catalog = json.optJSONObject("catalog"); var catalog = json.optJSONObject("catalog");
var oldItems = catalog != null ? catalog.optJSONArray("sections") : null; var oldItems = catalog != null ? catalog.optJSONArray("sections") : null;
@@ -41,15 +41,9 @@ public class CatalogJsonInjector {
removeUnsupportedLayouts(blocks); removeUnsupportedLayouts(blocks);
if (Preferences.getBoolValue("playlistsCatalogs", true)) { fetchCatalogId("https://vk.com/audio?section=my_playlists", oldItems);
fetchCatalogId("https://vk.com/audio?section=my_playlists", oldItems);
fetchCatalogId("https://vk.com/audio?section=albums", oldItems); fetchCatalogId("https://vk.com/audio?section=albums", oldItems);
}
if (Preferences.sendMusicMetrics() && Preferences.getBoolValue("playStatCatalog", false)) {
fetchCatalogId("https://vk.com/audio?section=recent", oldItems);
}
setDefaultAudioPage(oldItems, catalog); setDefaultAudioPage(oldItems, catalog);
} }
@@ -58,16 +52,18 @@ public class CatalogJsonInjector {
var noPlaylists = !json.has("playlists"); var noPlaylists = !json.has("playlists");
if (noPlaylists) { if (noPlaylists) {
PlaylistHelper.addCachedPlaylists(new JSONArray()); json.put("playlists", new JSONArray().put(getPlaylist()));
Log.d("catalogInjector", "added new pl");
} else { } else {
PlaylistHelper.addCachedPlaylists(json.optJSONArray("playlists")); json.optJSONArray("playlists").put(getPlaylist());
Log.d("catalogInjector", "added to exist pl");
} }
if (!useOldAppVer || noPlaylists) { if (!useOldAppVer || noPlaylists) {
var newBlocks = new JSONArray(); var newBlocks = new JSONArray();
newBlocks newBlocks
.put(getCatalogHeader("Кешированные плейлисты")) .put(getCatalogHeader())
.put(getCatalogPlaylist()) .put(getCatalogPlaylist())
.put(getCatalogSeparator()); .put(getCatalogSeparator());
@@ -94,7 +90,6 @@ public class CatalogJsonInjector {
var blocks = section.getJSONArray("blocks"); var blocks = section.getJSONArray("blocks");
fixDailyMix(blocks); fixDailyMix(blocks);
removeUnsupportedLayouts(blocks);
if (!isUsersCatalog || MusicCacheImpl.isEmpty() || LibVKXClient.isIntegrationEnabled()) { if (!isUsersCatalog || MusicCacheImpl.isEmpty() || LibVKXClient.isIntegrationEnabled()) {
return; // early return if not users catalog or no tracks or integration enabled return; // early return if not users catalog or no tracks or integration enabled
@@ -103,16 +98,22 @@ public class CatalogJsonInjector {
var noPlaylists = !json.has("playlists"); var noPlaylists = !json.has("playlists");
if (noPlaylists) { if (noPlaylists) {
PlaylistHelper.addCachedPlaylists(new JSONArray()); json.put("playlists", new JSONArray().put(getPlaylist()));
Log.d("catalogInjector", "added new pl");
} else { } else {
PlaylistHelper.addCachedPlaylists(json.optJSONArray("playlists")); try {
json.optJSONArray("playlists").put(getPlaylist());
Log.d("catalogInjector", "added to exist pl");
} catch (Exception e) {
Log.d("catalogInjector", e.toString());
}
} }
if (!useOldAppVer && !noPlaylists) { if (!useOldAppVer && !noPlaylists) {
var newBlocks = new JSONArray(); var newBlocks = new JSONArray();
newBlocks newBlocks
.put(getCatalogHeader("Кешированные плейлисты")) .put(getCatalogHeader())
.put(getCatalogPlaylist()) .put(getCatalogPlaylist())
.put(getCatalogSeparator()); .put(getCatalogSeparator());
@@ -130,15 +131,12 @@ public class CatalogJsonInjector {
if (type.equals("music_playlists") && j.has("playlists_ids")) { if (type.equals("music_playlists") && j.has("playlists_ids")) {
var newarr = new JSONArray(); var newarr = new JSONArray();
var playlists_ids = j.getJSONArray("playlists_ids"); var playlists_ids = j.optJSONArray("playlists_ids");
var savedPlaylists = PlaylistHelper.getCachedPlaylistsIds();
for (int n = 0; n < savedPlaylists.length(); n++) { newarr.put(getUserId() + "_-1");
newarr.put(savedPlaylists.getString(n));
}
for (int n = 0; n < playlists_ids.length(); n++) { for (int n = 0; n < playlists_ids.length(); n++) {
newarr.put(playlists_ids.getString(n)); newarr.put(playlists_ids.optString(n));
} }
Log.d("catalogInjector", "added to pl ids"); Log.d("catalogInjector", "added to pl ids");
@@ -220,35 +218,16 @@ public class CatalogJsonInjector {
} }
} }
private static void removeUnsupportedLayouts(JSONArray blocks) { private static void removeUnsupportedLayouts(JSONArray blocks) throws JSONException {
for (int i = blocks.length() - 1; i >= 0; i--) { for (int i = 0; i < blocks.length(); i++) {
JSONObject block = blocks.optJSONObject(i); var block = blocks.optJSONObject(i);
if (block == null) continue; var layout = block.optJSONObject("layout");
if (layout != null) {
JSONObject layout = block.optJSONObject("layout"); var name = layout.optString("name");
if (layout == null) continue; if (name.equals("header_extended")) {
if (layout.has("top_title")) blocks.remove(i);
String data_type = block.optString("data_type"); layout.put("name", "header");
}
if (Objects.equals(data_type, "music_recommended_playlists") || Objects.equals(data_type, "radiostations")) {
blocks.remove(i); // block
if (i - 1 >= 0) blocks.remove(i - 1); // text
if (i - 2 >= 0) blocks.remove(i - 2); // divider
continue; // Skip to the next iteration after removing
}
String name = layout.optString("name");
if (!name.equals("header_extended")) {
continue; // Skip to the next iteration if not header_extended
}
if (layout.has("top_title")) {
blocks.remove(i);
}
try {
layout.put("name", "header");
} catch (JSONException e) {
e.fillInStackTrace();
} }
} }
} }
@@ -284,12 +263,9 @@ public class CatalogJsonInjector {
+ sectionUrl + sectionUrl
+ "&access_token=" + "&access_token="
+ AccountManagerUtils.getUserToken()) + AccountManagerUtils.getUserToken())
.a("Accept-Encoding", "gzip") .a(Headers.a("User-Agent", Network.l.c().a(), "Content-Type", "application/x-www-form-urlencoded; charset=utf-8")).a();
.a("User-Agent", Network.l.c().a()) try {
.a("Content-Type", "application/x-www-form-urlencoded; charset=utf-8") var json = new JSONObject(mClient.a(request).execute().a().g()).getJSONObject("response");
.a();
try (Response resp = Network.b(CLIENT_API).a(request).execute()) {
var json = new JSONObject(GzipDecompressor.decompressResponse(resp)).getJSONObject("response");
var catalogarr = json.optJSONObject("catalog").optJSONArray("sections").optJSONObject(0); var catalogarr = json.optJSONObject("catalog").optJSONArray("sections").optJSONObject(0);
var title = catalogarr.optString("title"); var title = catalogarr.optString("title");
@@ -15,20 +15,18 @@ import ru.vtosters.lite.downloaders.AudioDownloader;
import ru.vtosters.lite.music.cache.MusicCacheImpl; import ru.vtosters.lite.music.cache.MusicCacheImpl;
import ru.vtosters.lite.music.cache.helpers.PlaylistHelper; import ru.vtosters.lite.music.cache.helpers.PlaylistHelper;
import ru.vtosters.lite.music.cache.helpers.TracklistHelper; import ru.vtosters.lite.music.cache.helpers.TracklistHelper;
import ru.vtosters.lite.utils.AccountManagerUtils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
public class PlaylistInjector { public class PlaylistInjector {
private static final ExecutorService executor = Executors.newCachedThreadPool();
public final static String CHANNEL_NAME = "VTCH"; public final static String CHANNEL_NAME = "VTCH";
private static final ExecutorService executor = Executors.newCachedThreadPool();
public static void injectDownloadPlaylist(Playlist playlist) { public static void injectDownloadPlaylist(Playlist playlist) {
AudioDownloader.cachePlaylist(playlist); executor.submit(() -> AudioDownloader.cachePlaylist(playlist));
} }
public static boolean eligibleForOfflineCaching() { public static boolean eligibleForOfflineCaching() {
@@ -37,10 +35,8 @@ public class PlaylistInjector {
public static Observable<AudioGetPlaylist.c> injectGetPlaylist(AudioGetPlaylist audioGetPlaylist) { public static Observable<AudioGetPlaylist.c> injectGetPlaylist(AudioGetPlaylist audioGetPlaylist) {
try { try {
if (MusicCacheImpl.isEmpty()) { if (!eligibleForOfflineCaching())
return null; return null;
}
var requestArgs = audioGetPlaylist.b(); var requestArgs = audioGetPlaylist.b();
var id = requestArgs.get("id"); var id = requestArgs.get("id");
@@ -48,11 +44,11 @@ public class PlaylistInjector {
var accessKey = requestArgs.get("access_key"); var accessKey = requestArgs.get("access_key");
boolean isVirtualPlaylist = accessKey != null && (accessKey.equals("cache")); boolean isVirtualPlaylist = accessKey != null && (accessKey.equals("cache"));
boolean isAlbumVirtualPlaylist = accessKey != null && (accessKey.equals("cacheAlbum")); boolean isAlbumVirtualPlaylist = accessKey != null && (accessKey.equals("cacheAlbum"));
boolean isOwnCachePlaylist = Objects.equals(ownerId, String.valueOf(AccountManagerUtils.getUserId())) && Objects.equals(id, "-1");
if (TextUtils.isEmpty(id) || (!isVirtualPlaylist && !isAlbumVirtualPlaylist)) { Log.d("PlaylistInjector", "id = " + id + " / owner = " + ownerId + " / access = " + accessKey + " / " + isVirtualPlaylist + " + " + isAlbumVirtualPlaylist);
if (TextUtils.isEmpty(id) || (!isVirtualPlaylist && !isAlbumVirtualPlaylist))
return null; return null;
}
return Observable.a((ObservableOnSubscribe<AudioGetPlaylist.c>) observableEmitter -> { return Observable.a((ObservableOnSubscribe<AudioGetPlaylist.c>) observableEmitter -> {
AudioGetPlaylist.c response = new AudioGetPlaylist.c(); AudioGetPlaylist.c response = new AudioGetPlaylist.c();
@@ -77,7 +73,7 @@ public class PlaylistInjector {
response.c = tracks; response.c = tracks;
} catch (RemoteException | JSONException e) { } catch (RemoteException | JSONException e) {
e.fillInStackTrace(); e.printStackTrace();
response.c = new ArrayList<>(); response.c = new ArrayList<>();
} }
observableEmitter.b(response); observableEmitter.b(response);
@@ -86,18 +82,18 @@ public class PlaylistInjector {
return; return;
} }
if (isOwnCachePlaylist) { if (isVirtualPlaylist) {
response.c = (ArrayList<MusicTrack>) TracklistHelper.getMyCachedMusicTracks(); response.c = (ArrayList<MusicTrack>) TracklistHelper.getMyCachedMusicTracks();
response.b = PlaylistHelper.createCachedPlaylistMetadata(); response.b = PlaylistHelper.createCachedPlaylistMetadata();
} else { } else {
response.c = (ArrayList<MusicTrack>) MusicCacheImpl.getPlaylistSongs(ownerId, id); response.c = (ArrayList<MusicTrack>) TracklistHelper.getTracks(requestArgs.get("owner_id"));
response.b = MusicCacheImpl.getPlaylist(id, ownerId); response.b = PlaylistHelper.createCachedPlaylistMetadata(requestArgs.get("owner_id"));
} }
observableEmitter.b(response); observableEmitter.b(response);
}); });
} catch (Exception e) { } catch (Exception e) {
e.fillInStackTrace(); e.printStackTrace();
} }
return null; return null;
} }
@@ -12,7 +12,6 @@ import org.json.JSONArray;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import ru.vtosters.lite.music.cache.MusicCacheImpl; import ru.vtosters.lite.music.cache.MusicCacheImpl;
import ru.vtosters.lite.music.cache.delegate.PlaylistCacheDbDelegate;
import ru.vtosters.lite.music.cache.helpers.PlaylistHelper; import ru.vtosters.lite.music.cache.helpers.PlaylistHelper;
import ru.vtosters.lite.music.cache.helpers.TracklistHelper; import ru.vtosters.lite.music.cache.helpers.TracklistHelper;
import ru.vtosters.lite.utils.AccountManagerUtils; import ru.vtosters.lite.utils.AccountManagerUtils;
@@ -23,8 +22,6 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import static ru.vtosters.lite.music.cache.helpers.PlaylistHelper.*;
public class TracklistInjector { public class TracklistInjector {
public static boolean eligibleForOfflineCaching() { public static boolean eligibleForOfflineCaching() {
@@ -42,7 +39,7 @@ public class TracklistInjector {
Log.d("TracklistInjector", "Using vkx cache catalog"); Log.d("TracklistInjector", "Using vkx cache catalog");
observableEmitter.b(parser.c(new JSONObject(service.getCacheCatalog()))); observableEmitter.b(parser.c(new JSONObject(service.getCacheCatalog())));
} catch (Exception e) { } catch (Exception e) {
e.fillInStackTrace(); e.printStackTrace();
List<MusicTrack> tracks = new ArrayList<>(); List<MusicTrack> tracks = new ArrayList<>();
List<String> cache = service.getCache(); List<String> cache = service.getCache();
@@ -55,14 +52,14 @@ public class TracklistInjector {
observableEmitter.b(parser.c(createVirtualCatalog(tracks))); observableEmitter.b(parser.c(createVirtualCatalog(tracks)));
} }
} catch (Exception e) { } catch (Exception e) {
e.fillInStackTrace(); e.printStackTrace();
} }
}); });
} else { } else {
if (MusicCacheImpl.isEmpty()) { if (MusicCacheImpl.isEmpty()) {
observableEmitter.b(parser.c(getEmptyCatalog())); observableEmitter.b(parser.c(getEmptyCatalog()));
} else { } else {
observableEmitter.b(parser.c(createVirtualCatalog(TracklistHelper.getMyCachedMusicTracks()))); observableEmitter.b(parser.c(createVirtualCatalog(TracklistHelper.getTracks())));
} }
} }
}); });
@@ -165,12 +162,16 @@ public class TracklistInjector {
boolean noPlaylists = hasNoPlaylists(blocks); boolean noPlaylists = hasNoPlaylists(blocks);
if (noPlaylists) { if (noPlaylists) {
PlaylistHelper.addCachedPlaylists(new JSONArray()); catalogNode.put("playlists", new JSONArray().put(PlaylistHelper.getPlaylist()));
firstSection.put("blocks", createNewBlocks(blocks)); JSONArray newBlocks = createNewBlocks(blocks);
Log.d("catalogInjector", "added cache catalog playlist");
firstSection.put("blocks", newBlocks);
} else { } else {
try { try {
PlaylistHelper.addCachedPlaylists(catalogNode.optJSONArray("playlists")); catalogNode.optJSONArray("playlists").put(PlaylistHelper.getPlaylist());
updatePlaylistsIds(blocks); updatePlaylistsIds(blocks);
} catch (Exception e) { } catch (Exception e) {
@@ -187,7 +188,7 @@ public class TracklistInjector {
return firstSection.getString("url").equals("https://vk.com/audios" + AccountManagerUtils.getUserId() + "?section=all"); return firstSection.getString("url").equals("https://vk.com/audios" + AccountManagerUtils.getUserId() + "?section=all");
} }
private static boolean hasNoPlaylists(JSONArray blocks) { private static boolean hasNoPlaylists(JSONArray blocks) throws JSONException {
return blocks.optJSONObject(0).optJSONArray("buttons").optJSONObject(0).optJSONObject("action").optString("type").equals("create_playlist"); return blocks.optJSONObject(0).optJSONArray("buttons").optJSONObject(0).optJSONObject("action").optString("type").equals("create_playlist");
} }
@@ -195,14 +196,13 @@ public class TracklistInjector {
JSONArray newBlocks = new JSONArray(); JSONArray newBlocks = new JSONArray();
newBlocks newBlocks
.put(PlaylistHelper.getCatalogHeader(AndroidUtils.getString(R.string.cached_tracks_title))) .put(PlaylistHelper.getCatalogHeader())
.put(PlaylistHelper.getCatalogPlaylist()) .put(PlaylistHelper.getCatalogPlaylist())
.put(PlaylistHelper.getCatalogSeparator()); .put(PlaylistHelper.getCatalogSeparator());
for (int i = 0; i < blocks.length(); i++) { for (int i = 0; i < blocks.length(); i++) {
newBlocks.put(blocks.optJSONObject(i)); newBlocks.put(blocks.optJSONObject(i));
} }
return newBlocks; return newBlocks;
} }
@@ -212,15 +212,16 @@ public class TracklistInjector {
String type = j.optString("data_type"); String type = j.optString("data_type");
if (type.equals("music_playlists") && j.has("playlists_ids")) { if (type.equals("music_playlists") && j.has("playlists_ids")) {
JSONArray newarr = PlaylistHelper.getCachedPlaylistsIds(); JSONArray newarr = new JSONArray();
JSONArray playlists_ids = j.optJSONArray("playlists_ids"); JSONArray playlists_ids = j.optJSONArray("playlists_ids");
newarr.put(AccountManagerUtils.getUserId() + "_-1");
for (int n = 0; n < playlists_ids.length(); n++) { for (int n = 0; n < playlists_ids.length(); n++) {
newarr.put(playlists_ids.optString(n)); newarr.put(playlists_ids.optString(n));
} }
j.put("playlists_ids", newarr); j.put("playlists_ids", newarr);
break; break;
} }
} }
@@ -239,19 +240,7 @@ public class TracklistInjector {
audiosBlock.put("layout", layout); audiosBlock.put("layout", layout);
var blocks = new JSONArray(); var blocks = new JSONArray().put(getShuffleButton()).put(audiosBlock);
if (MusicCacheImpl.hasPlaylist()) {
blocks.put(getCatalogHeader("Кешированные плейлисты"))
.put(getCatalogPlaylist());
}
if (!PlaylistCacheDbDelegate.isPlaylistEmpty(AndroidUtils.getGlobalContext(), AccountManagerUtils.getUserId() + "_-1")) {
if (MusicCacheImpl.hasPlaylist()) blocks.put(getCatalogSeparator());
blocks.put(getCatalogHeader(AndroidUtils.getString(R.string.cached_tracks_title)))
.put(getShuffleButton())
.put(audiosBlock);
}
var randomId = getRandomId(); var randomId = getRandomId();
@@ -270,7 +259,6 @@ public class TracklistInjector {
return new JSONObject() return new JSONObject()
.put("catalog", catalog) .put("catalog", catalog)
.put("playlists", addCachedPlaylists(new JSONArray()))
.put("audios", TracklistHelper.tracksToJsons(tracks)); .put("audios", TracklistHelper.tracksToJsons(tracks));
} }
@@ -6,13 +6,12 @@ import android.content.SharedPreferences;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.os.Build; import android.os.Build;
import android.os.StrictMode; import android.os.StrictMode;
import com.vk.core.util.AppContextHolder;
import com.vk.medianative.MediaImageEncoder; import com.vk.medianative.MediaImageEncoder;
import com.vk.medianative.MediaNative; import com.vk.medianative.MediaNative;
import com.vtosters.lite.data.Users; import com.vtosters.lite.data.Users;
import com.vtosters.lite.fragments.SettingsListFragment; import com.vtosters.lite.fragments.SettingsListFragment;
import ru.vtosters.hooks.GmsHook; import ru.vtosters.hooks.GmsHook;
import ru.vtosters.hooks.VKProxy;
import ru.vtosters.hooks.VerificationsHook; import ru.vtosters.hooks.VerificationsHook;
import ru.vtosters.lite.BuildConfig; import ru.vtosters.lite.BuildConfig;
import ru.vtosters.lite.proxy.ProxyUtils; import ru.vtosters.lite.proxy.ProxyUtils;
@@ -24,8 +23,7 @@ import java.security.NoSuchAlgorithmException;
public class Preferences { public class Preferences {
public static void init(Application application) throws Exception { public static void init(Application application) throws Exception {
AppContextHolder.a = application.getApplicationContext(); StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitNetwork().build();
StrictMode.setThreadPolicy(policy); // fix profiles hide hook StrictMode.setThreadPolicy(policy); // fix profiles hide hook
MediaNative.init(application); MediaNative.init(application);
@@ -36,6 +34,7 @@ public class Preferences {
LifecycleUtils.registerActivities(application); LifecycleUtils.registerActivities(application);
AnalyticsHelper.start(application); AnalyticsHelper.start(application);
VKProxy.load();
} // VK Init } // VK Init
public static String getBuildName() { public static String getBuildName() {
@@ -100,8 +99,8 @@ public class Preferences {
return getBoolValue("vkme", false); return getBoolValue("vkme", false);
} }
public static int autocache() { public static boolean autocache() {
return Preferences.getPreferences().getInt("autocaching", 0); return getBoolValue("autocache", false);
} }
public static boolean adsstories() { public static boolean adsstories() {
@@ -292,10 +291,6 @@ public class Preferences {
return getBoolValue("vkme_notifs", false); return getBoolValue("vkme_notifs", false);
} }
public static boolean sendMusicMetrics() {
return getBoolValue("sendMusicMetrics", true);
}
public static boolean screenshotdetect() { public static boolean screenshotdetect() {
return getBoolValue("screenshotdetect", true); return getBoolValue("screenshotdetect", true);
} }
@@ -318,10 +313,6 @@ public class Preferences {
return false; return false;
} }
public static String getId() {
return String.valueOf(AccountManagerUtils.getUserId());
}
public static void updateBuildNumber() { public static void updateBuildNumber() {
try { try {
getPreferences().edit().putLong("setupTime", AndroidUtils.getGlobalContext().getPackageManager().getPackageInfo(AndroidUtils.getPackageName(), 0).lastUpdateTime).apply(); getPreferences().edit().putLong("setupTime", AndroidUtils.getGlobalContext().getPackageManager().getPackageInfo(AndroidUtils.getPackageName(), 0).lastUpdateTime).apply();
@@ -363,14 +354,7 @@ public class Preferences {
} }
public static int compress(int origquality) { public static int compress(int origquality) {
return MediaImageEncoder.needToCompress() ? origquality : 100; if (MediaImageEncoder.needToSkipCompression()) return 100;
} return origquality;
public static String metadataSeparator() {
return getPreferences().getString("metadata_separator", "; ");
}
public static void setMetadataSeparator(String separator) {
getPreferences().edit().putString("metadata_separator", separator).apply();
} }
} }
@@ -24,8 +24,6 @@ import com.vk.core.drawable.RecoloredDrawable;
import com.vk.core.ui.themes.MilkshakeHelper; import com.vk.core.ui.themes.MilkshakeHelper;
import com.vk.core.ui.themes.VKTheme; import com.vk.core.ui.themes.VKTheme;
import com.vk.core.ui.themes.VKThemeHelper; import com.vk.core.ui.themes.VKThemeHelper;
import com.vk.im.engine.models.dialogs.DialogTheme;
import com.vk.im.ui.themes.DefaultThemeProvider;
import com.vtosters.lite.R; import com.vtosters.lite.R;
import com.vtosters.lite.data.ThemeTracker; import com.vtosters.lite.data.ThemeTracker;
import ru.vtosters.lite.deviceinfo.OEMDetector; import ru.vtosters.lite.deviceinfo.OEMDetector;
@@ -34,6 +32,7 @@ import ru.vtosters.lite.themes.ThemesHacks;
import ru.vtosters.lite.themes.ThemesManager; import ru.vtosters.lite.themes.ThemesManager;
import ru.vtosters.lite.ui.wallpapers.WallpapersHooks; import ru.vtosters.lite.ui.wallpapers.WallpapersHooks;
import ru.vtosters.lite.utils.LifecycleUtils; import ru.vtosters.lite.utils.LifecycleUtils;
import ru.vtosters.lite.utils.WebViewColoringUtils;
import java.lang.reflect.Field; import java.lang.reflect.Field;
@@ -45,7 +44,7 @@ public class ThemesUtils {
try { try {
setTheme(theme, LifecycleUtils.getCurrentActivity(), restartActivity); setTheme(theme, LifecycleUtils.getCurrentActivity(), restartActivity);
} catch (Exception e) { } catch (Exception e) {
e.fillInStackTrace(); e.printStackTrace();
} }
} // Apply VKTheme and ImTheme (hard applying without dynamic theme changing) } // Apply VKTheme and ImTheme (hard applying without dynamic theme changing)
@@ -62,27 +61,14 @@ public class ThemesUtils {
activity = LifecycleUtils.getCurrentActivity(); activity = LifecycleUtils.getCurrentActivity();
} }
VKThemeHelper.theme(theme, activity, fl); VKThemeHelper.theme(theme, activity, fl);
if (isMonetTheme() || ThemesManager.canApplyCustomAccent()) { if (isMonetTheme() || ThemesManager.canApplyCustomAccent()) ThemesCore.clear();
ThemesCore.clear(); if (restartActivity) activity.recreate();
ThemesCore.init();
DialogTheme.d.a(new DefaultThemeProvider(VKThemeHelper.k).b()); // reset dialog attrs cache
}
ThemeTracker.a(); ThemeTracker.a();
if (restartActivity) { WebViewColoringUtils.isLoaded = false;
activity.recreate();
}
new WebView(activity).clearCache(true); new WebView(activity).clearCache(true);
WebCachePreloader.e(); WebCachePreloader.e();
} }
public static DialogTheme getDialogTheme() {
if (isMonetTheme()) {
return new DefaultThemeProvider(VKThemeHelper.k).b(); // get directly from theme cuz monet issue
} else {
return DialogTheme.d.a(); // get cached theme
}
}
public static boolean isDarkTheme() { public static boolean isDarkTheme() {
return VKThemeHelper.r(); return VKThemeHelper.r();
} }
@@ -355,7 +341,7 @@ public class ThemesUtils {
} }
public static int getColor(int i) { public static int getColor(int i) {
return ThemesHacks.getHackedColor(getGlobalContext(), i); return ThemesHacks.getHackedColor(LifecycleUtils.getCurrentActivity(), i);
} // Android Support color injector + accent color checker } // Android Support color injector + accent color checker
public static int getAlertStyle() { public static int getAlertStyle() {
@@ -1,11 +1,10 @@
package ru.vtosters.lite.concurrent; package ru.vtosters.lite.concurrent;
import com.vk.core.concurrent.VkExecutors; import com.vk.core.concurrent.VkExecutors;
import io.reactivex.Scheduler;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.ForkJoinPool; import java.util.concurrent.Executors;
import io.reactivex.Scheduler;
public class VTExecutors { public class VTExecutors {
public static ExecutorService getMusicDownloadExecutor() { public static ExecutorService getMusicDownloadExecutor() {
@@ -33,21 +32,7 @@ public class VTExecutors {
} }
private static class LazyHolder { private static class LazyHolder {
private static final int MIN_PARALLELISM = 1; public static final ExecutorService INSTANCE = Executors.newSingleThreadExecutor();
private static final int MAX_PARALLELISM = 7;
private static final int PARALLELISM = clamp(
(Runtime.getRuntime().availableProcessors() >> 1) - 1
);
private static final ExecutorService INSTANCE =
new ForkJoinPool(PARALLELISM,
ForkJoinPool.defaultForkJoinWorkerThreadFactory,
(t, e) -> {},
/* FIFO */ true);
private static int clamp(int value) {
return Math.min(LazyHolder.MAX_PARALLELISM, Math.max(value, LazyHolder.MIN_PARALLELISM));
}
} }
} }
} }
@@ -115,7 +115,7 @@ public class Device {
+ "- Manufacturer: " + this.manufacturerName + "\n" + "- Manufacturer: " + this.manufacturerName + "\n"
+ "- Brand: " + this.brandName + "\n" + "- Brand: " + this.brandName + "\n"
+ "- Model: " + this.modelName + "\n" + "- Model: " + this.modelName + "\n"
+ (isMIUI() || isHyperOs() ? "- Miui/HyperOS Version Code: " + this.MiuiUiVersionCode + "\n" + "- Miui/HyperOS Version Name: " + this.MiuiUiVersionName + "\n" : "") + (isMIUI() ? "- Miui Version Code: " + this.MiuiUiVersionCode + "\n" + "- Miui Version Name: " + this.MiuiUiVersionName + "\n" : "")
+ (isSamsung() ? "- OneUi Major Version: " + this.OneUiMajorVersion + "\n" + "- OneUi Minor Version: " + this.OneUiMinorVersion + "\n" : "") + (isSamsung() ? "- OneUi Major Version: " + this.OneUiMajorVersion + "\n" + "- OneUi Minor Version: " + this.OneUiMinorVersion + "\n" : "")
+ (hasMiuiIncrCode() ? "- Miui Version Incremental Code: " + this.MiuiIncrementalCode + "\n" : "") + (hasMiuiIncrCode() ? "- Miui Version Incremental Code: " + this.MiuiIncrementalCode + "\n" : "")
+ (isEMUI() ? "- Emui Version: " + this.EmuiVersion : ""); + (isEMUI() ? "- Emui Version: " + this.EmuiVersion : "");
@@ -56,7 +56,7 @@ public class OEMDetector {
} }
public static boolean isOEM() { public static boolean isOEM() {
return isMIUI() || isEMUI() || isFlyme() || isZenUI() || isSamsung() || isVivo() || isOneUi() || isHyperOs(); return isMIUI() || isEMUI() || isFlyme() || isZenUI() || isSamsung() || isVivo() || isOneUi();
} }
public static boolean isMIUI() { public static boolean isMIUI() {
@@ -68,15 +68,6 @@ public class OEMDetector {
} }
} }
public static boolean isHyperOs() {
try {
int miuiVer = Integer.parseInt(getMiuiUiVersionCode()); // cuz hyperos gives 816 and etc
return miuiVer > 14 && isMIUI();
} catch (Exception unused) {
return false;
}
}
public static boolean isIntentResolved(Context ctx, Intent intent) { public static boolean isIntentResolved(Context ctx, Intent intent) {
return (intent != null && ctx.getPackageManager().resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) != null); return (intent != null && ctx.getPackageManager().resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) != null);
} }
@@ -133,6 +124,7 @@ public class OEMDetector {
try { try {
return (String) getPropMtd.invoke(null, prop); return (String) getPropMtd.invoke(null, prop);
} catch (Throwable e) { } catch (Throwable e) {
e.printStackTrace();
return null; return null;
} }
} }
@@ -6,6 +6,7 @@ import com.vk.im.engine.models.dialogs.Dialog;
import com.vk.im.engine.models.messages.Msg; import com.vk.im.engine.models.messages.Msg;
import com.vk.im.engine.utils.ImDialogsUtils; import com.vk.im.engine.utils.ImDialogsUtils;
import okhttp3.Headers; import okhttp3.Headers;
import okhttp3.OkHttpClient;
import ru.vtosters.hooks.other.ThemesUtils; import ru.vtosters.hooks.other.ThemesUtils;
import ru.vtosters.lite.concurrent.VTExecutors; import ru.vtosters.lite.concurrent.VTExecutors;
import ru.vtosters.lite.net.Request; import ru.vtosters.lite.net.Request;
@@ -14,49 +15,21 @@ import ru.vtosters.lite.utils.AccountManagerUtils;
import ru.vtosters.lite.utils.AndroidUtils; import ru.vtosters.lite.utils.AndroidUtils;
import ru.vtosters.lite.utils.LifecycleUtils; import ru.vtosters.lite.utils.LifecycleUtils;
import java.io.IOException;
import static com.vk.core.network.Network.ClientType.CLIENT_API;
import static ru.vtosters.lite.proxy.ProxyUtils.getApi;
import static ru.vtosters.lite.utils.AccountManagerUtils.getUserToken;
public class Requests { public class Requests {
public static void hookRead(Dialog dialog) { public static void hookRead(Dialog dialog) {
okhttp3.Request req = new okhttp3.Request.a() Request.makeRequest("https://" + ProxyUtils.getApi() + "/method/messages.markAsRead?start_message_id=" + dialog.F1() + "&peer_id=" + dialog.getId() + "&v=5.119&access_token=" + AccountManagerUtils.getUserToken(), response -> {
.b("https://" + ProxyUtils.getApi() + "/method/messages.markAsRead?start_message_id=" + dialog.F1() + "&peer_id=" + dialog.getId() + "&v=5.119&access_token=" + AccountManagerUtils.getUserToken()) });
.a();
try {
Network.b(CLIENT_API).a(req).execute().close();;
} catch (IOException e) {
throw new RuntimeException(e);
}
} }
public static void hookReadStartMsgTo(Msg dialog) { public static void hookReadStartMsgTo(Msg dialog) {
okhttp3.Request req = new okhttp3.Request.a() Request.makeRequest("https://" + ProxyUtils.getApi() + "/method/messages.markAsRead?start_message_id=" + dialog.C1() + "&peer_id=" + dialog.v1() + "&v=5.119&access_token=" + AccountManagerUtils.getUserToken(), response -> {
.b("https://" + ProxyUtils.getApi() + "/method/messages.markAsRead?start_message_id=" + dialog.C1() + "&peer_id=" + dialog.v1() + "&v=5.119&access_token=" + AccountManagerUtils.getUserToken()) });
.a();
try {
Network.b(CLIENT_API).a(req).execute().close();
} catch (IOException e) {
throw new RuntimeException(e);
}
} }
public static void hookKick(Msg dialog) { public static void hookKick(Msg dialog) {
if (dialog.getFrom().t1() == AccountManagerUtils.getUserId()) return; if (dialog.getFrom().t1() == AccountManagerUtils.getUserId()) return;
Request.makeRequest("https://" + ProxyUtils.getApi() + "/method/messages.removeChatUser?member_id=" + dialog.getFrom().t1() + "&chat_id=" + ImDialogsUtils.c(dialog.v1()) + "&v=5.119&access_token=" + AccountManagerUtils.getUserToken(), response -> {
okhttp3.Request req = new okhttp3.Request.a() });
.b("https://" + ProxyUtils.getApi() + "/method/messages.removeChatUser?member_id=" + dialog.getFrom().t1() + "&chat_id=" + ImDialogsUtils.c(dialog.v1()) + "&v=5.119&access_token=" + AccountManagerUtils.getUserToken())
.a();
try {
Network.b(CLIENT_API).a(req).execute().close();;
} catch (IOException e) {
throw new RuntimeException(e);
}
} }
public static void hookDialogInfo(Dialog dialog) { public static void hookDialogInfo(Dialog dialog) {
@@ -78,7 +51,7 @@ public class Requests {
.a(Headers.a("User-Agent", Network.l.c().a(), "Content-Type", "application/x-www-form-urlencoded; charset=utf-8")) .a(Headers.a("User-Agent", Network.l.c().a(), "Content-Type", "application/x-www-form-urlencoded; charset=utf-8"))
.a(); .a();
Network.b(CLIENT_API).a(request).execute().a().g(); new OkHttpClient().a(request).execute().a().g();
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
@@ -1,36 +1,27 @@
package ru.vtosters.lite.downloaders; package ru.vtosters.lite.downloaders;
import android.os.Environment; import android.os.Environment;
import android.util.Log;
import androidx.core.app.NotificationCompat;
import bruhcollective.itaysonlab.libvkx.client.LibVKXClient; import bruhcollective.itaysonlab.libvkx.client.LibVKXClient;
import com.vk.core.util.ToastUtils; import com.vk.core.util.ToastUtils;
import com.vk.dto.music.MusicTrack; import com.vk.dto.music.MusicTrack;
import com.vk.dto.music.Playlist; import com.vk.dto.music.Playlist;
import com.vtosters.lite.R; import com.vtosters.lite.R;
import org.json.JSONObject;
import ru.vtosters.hooks.music.MusicCacheFilesHook; import ru.vtosters.hooks.music.MusicCacheFilesHook;
import ru.vtosters.hooks.other.Preferences; import ru.vtosters.hooks.other.Preferences;
import ru.vtosters.lite.music.cache.MusicCacheImpl; import ru.vtosters.lite.music.cache.MusicCacheImpl;
import ru.vtosters.lite.music.cache.delegate.PlaylistCacheDbDelegate;
import ru.vtosters.lite.music.cache.helpers.PlaylistHelper;
import ru.vtosters.lite.music.callback.MusicCallbackBuilder; import ru.vtosters.lite.music.callback.MusicCallbackBuilder;
import ru.vtosters.lite.music.converter.playlist.PlaylistConverter; import ru.vtosters.lite.music.converter.playlist.PlaylistConverter;
import ru.vtosters.lite.music.downloader.AudioGet; import ru.vtosters.lite.music.downloader.AudioGet;
import ru.vtosters.lite.music.downloader.PlaylistDownloader; import ru.vtosters.lite.music.downloader.PlaylistDownloader;
import ru.vtosters.lite.music.downloader.ThumbnailPlaylistDownloader;
import ru.vtosters.lite.music.downloader.TrackDownloader; import ru.vtosters.lite.music.downloader.TrackDownloader;
import ru.vtosters.lite.music.interfaces.Callback;
import ru.vtosters.lite.music.notification.MusicNotificationBuilder; import ru.vtosters.lite.music.notification.MusicNotificationBuilder;
import ru.vtosters.lite.utils.AccountManagerUtils; import ru.vtosters.lite.utils.AccountManagerUtils;
import ru.vtosters.lite.utils.AndroidUtils; import ru.vtosters.lite.utils.AndroidUtils;
import ru.vtosters.lite.utils.IOUtils; import ru.vtosters.lite.utils.IOUtils;
import ru.vtosters.lite.utils.music.PlaylistUtils;
import java.io.File; import java.io.File;
import java.util.List; import java.util.concurrent.ExecutorService;
import java.util.Objects; import java.util.concurrent.Executors;
import static ru.vtosters.lite.utils.AndroidUtils.getString; import static ru.vtosters.lite.utils.AndroidUtils.getString;
@@ -41,18 +32,19 @@ import static ru.vtosters.lite.utils.AndroidUtils.getString;
*/ */
public class AudioDownloader { public class AudioDownloader {
public static final ExecutorService executor = Executors.newCachedThreadPool();
public static final String dlpath = Preferences.getBoolValue("dldir", false) ? Environment.DIRECTORY_DOWNLOADS : Environment.DIRECTORY_MUSIC; public static final String dlpath = Preferences.getBoolValue("dldir", false) ? Environment.DIRECTORY_DOWNLOADS : Environment.DIRECTORY_MUSIC;
public static void downloadPlaylist(Playlist playlist) { public static void downloadPlaylist(Playlist playlist) {
List<MusicTrack> tracks = PlaylistConverter.getPlaylist(playlist); var tracks = PlaylistConverter.getPlaylist(playlist);
String playlistName = IOUtils.getValidFileName(playlist.g); var playlistName = IOUtils.getValidFileName(playlist.g);
String musicPath = Environment.getExternalStoragePublicDirectory(dlpath).getAbsolutePath(); var musicPath = Environment.getExternalStoragePublicDirectory(dlpath).getAbsolutePath();
String downloadPath = musicPath + File.separator + playlistName; var downloadPath = musicPath + File.separator + playlistName;
int notificationId = playlistName.hashCode(); var notificationId = playlistName.hashCode();
NotificationCompat.Builder notification = MusicNotificationBuilder.buildPlaylistDownloadNotification(playlistName, notificationId); var notification = MusicNotificationBuilder.buildPlaylistDownloadNotification(playlistName, notificationId);
PlaylistDownloader.downloadPlaylist( PlaylistDownloader.downloadPlaylist(
tracks, tracks,
@@ -68,96 +60,54 @@ public class AudioDownloader {
} }
public static void cacheTrack(MusicTrack track) { public static void cacheTrack(MusicTrack track) {
String trackId = LibVKXClient.asId(track); var trackId = LibVKXClient.asId(track);
if (MusicCacheImpl.isCachedTrack(trackId)) { if (MusicCacheImpl.isCachedTrack(trackId)) {
MusicCacheImpl.removeTrack(trackId); MusicCacheImpl.removeTrack(trackId);
AndroidUtils.sendToast(AndroidUtils.getString("audio_deleted_from_cache")); AndroidUtils.sendToast(AndroidUtils.getString("audio_deleted_from_cache"));
} else { return;
File trackFile = MusicCacheFilesHook.getTrackFile(trackId);
if (!trackFile.exists()) {
trackFile.getParentFile().mkdirs();
}
downloadM3U8(track, true);
} }
var trackFile = MusicCacheFilesHook.getTrackFile(trackId);
if (!trackFile.exists())
trackFile.getParentFile().mkdirs();
executor.submit(() -> downloadM3U8(track, true));
} }
public static void cachePlaylist(Playlist playlist) { public static void cachePlaylist(Playlist playlist) {
List<MusicTrack> tracks = PlaylistConverter.getPlaylist(playlist); var tracks = PlaylistConverter.getPlaylist(playlist);
int notificationId = playlist.g.hashCode(); var notificationId = playlist.g.hashCode();
NotificationCompat.Builder notification = MusicNotificationBuilder.buildPlaylistDownloadNotification(playlist.g, notificationId); var notification = MusicNotificationBuilder.buildPlaylistDownloadNotification(playlist.g, notificationId);
JSONObject thumbs = PlaylistUtils.getThumb(playlist);
if (thumbs != null) {
new ThumbnailPlaylistDownloader().download(null, new Callback() {
@Override
public void onProgress(int progress) {
}
@Override
public void onSuccess() {
PlaylistCacheDbDelegate.addPlaylist(AndroidUtils.getGlobalContext(), playlist);
Log.d("Playlist", "adding to cache with thumbs " + playlist.v1());
}
@Override
public void onFailure(Throwable e) {
throw new RuntimeException(e);
}
@Override
public void onSizeReceived(long size, long header) {
}
}, playlist);
} else {
PlaylistCacheDbDelegate.addPlaylist(AndroidUtils.getGlobalContext(), playlist);
Log.d("Playlist", "adding to cache without thumbs " + playlist.a);
}
PlaylistDownloader.cachePlaylist( PlaylistDownloader.cachePlaylist(
tracks, tracks,
MusicCallbackBuilder.buildPlaylistCallback(tracks.size(), notification, notificationId), MusicCallbackBuilder.buildPlaylistCallback(tracks.size(), notification, notificationId)
playlist
); );
} }
public static void cacheAllAudios() { public static void cacheAllAudios() {
List<MusicTrack> tracks = AudioGet.getAudios(); var tracks = AudioGet.getAudios();
int notificationId = AccountManagerUtils.getUserId(); var notificationId = AccountManagerUtils.getUserId();
NotificationCompat.Builder notification = MusicNotificationBuilder.buildAllAudiosDownloadNotification(notificationId); var notification = MusicNotificationBuilder.buildAllAudiosDownloadNotification(notificationId);
if (tracks == null || tracks.isEmpty()) {
return;
}
PlaylistDownloader.cachePlaylist( PlaylistDownloader.cachePlaylist(
tracks, tracks,
MusicCallbackBuilder.buildPlaylistCallback(tracks.size(), notification, notificationId), MusicCallbackBuilder.buildPlaylistCallback(tracks.size(), notification, notificationId)
PlaylistHelper.createCachedPlaylistMetadata()
); );
} }
public static void downloadAllAudios() { public static void downloadAllAudios() {
List<MusicTrack> tracks = AudioGet.getAudios(); var tracks = AudioGet.getAudios();
int notificationId = AccountManagerUtils.getUserId(); var notificationId = AccountManagerUtils.getUserId();
NotificationCompat.Builder notification = MusicNotificationBuilder.buildAllAudiosDownloadNotification(notificationId); var notification = MusicNotificationBuilder.buildAllAudiosDownloadNotification(notificationId);
String playlistName = "Audios of " + AccountManagerUtils.getUserId(); var playlistName = "Audios of " + AccountManagerUtils.getUserId();
String musicPath = Environment.getExternalStoragePublicDirectory(dlpath).getAbsolutePath(); var musicPath = Environment.getExternalStoragePublicDirectory(dlpath).getAbsolutePath();
String downloadPath = musicPath + File.separator + playlistName; var downloadPath = musicPath + File.separator + playlistName;
if (tracks == null || tracks.isEmpty()) {
return;
}
PlaylistDownloader.downloadPlaylist( PlaylistDownloader.downloadPlaylist(
tracks, tracks,
@@ -168,20 +118,20 @@ public class AudioDownloader {
} }
private static void downloadM3U8(MusicTrack track, boolean cache) { private static void downloadM3U8(MusicTrack track, boolean cache) {
Objects.requireNonNull(track.D, () -> { if (track.D == null) {
ToastUtils.a(getString(R.string.link_audio_error)); ToastUtils.a(getString(R.string.link_audio_error));
return "link must not be null"; return;
}); }
String musicPath = Environment.getExternalStoragePublicDirectory(dlpath).getAbsolutePath();
int tempId = track.d; var musicPath = Environment.getExternalStoragePublicDirectory(dlpath).getAbsolutePath();
String downloadPath = musicPath + File.separator; var tempId = track.d;
NotificationCompat.Builder notification = MusicNotificationBuilder.buildDownloadNotification(track, tempId); var downloadPath = musicPath + File.separator;
var notification = MusicNotificationBuilder.buildDownloadNotification(track, tempId);
Callback cb = MusicCallbackBuilder.buildOneTrackCallback(tempId, notification);
if (cache) { if (cache) {
TrackDownloader.cacheTrack(track, cb, PlaylistHelper.createCachedPlaylistMetadata()); TrackDownloader.cacheTrack(track, MusicCallbackBuilder.buildOneTrackCallback(tempId, notification));
} else { } else {
TrackDownloader.downloadTrack(track, downloadPath, cb); TrackDownloader.downloadTrack(track, downloadPath, MusicCallbackBuilder.buildOneTrackCallback(tempId, notification));
} }
} }
} }
@@ -1,26 +1,29 @@
package ru.vtosters.lite.downloaders; package ru.vtosters.lite.downloaders;
import android.media.MediaScannerConnection; import android.app.DownloadManager;
import android.content.Context;
import android.net.Uri;
import android.os.Environment; import android.os.Environment;
import com.vk.core.network.Network;
import com.vk.dto.common.ImageSize; import com.vk.dto.common.ImageSize;
import com.vk.dto.stories.model.StoryEntry; import com.vk.dto.stories.model.StoryEntry;
import okhttp3.*;
import ru.vtosters.lite.utils.AndroidUtils;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List; import java.util.List;
import static ru.vtosters.lite.downloaders.VideoDownloader.downloadVideo; import static ru.vtosters.lite.downloaders.VideoDownloader.downloadVideo;
import static ru.vtosters.lite.utils.AndroidUtils.getGlobalContext;
public class StoryDownloader { public class StoryDownloader {
public static void downloadStory(StoryEntry story) { public static void downloadStory(StoryEntry story) {
if (story.T1()) { if (story.T1()) {
downloadImage(story); List<ImageSize> imageSizes = story.E.Q.t1();
String url = imageSizes.get(imageSizes.size() - 1).url;
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
request.setTitle("photo" + story.E.a + ".jpg");
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_PICTURES, "photo" + story.E.a + ".jpg");
request.allowScanningByMediaScanner();
((DownloadManager) getGlobalContext().getSystemService(Context.DOWNLOAD_SERVICE)).enqueue(request);
return; return;
} }
// if story is a video // if story is a video
@@ -28,53 +31,4 @@ public class StoryDownloader {
downloadVideo(story.F, null); downloadVideo(story.F, null);
} }
} }
public static void downloadImage(StoryEntry story) {
List<ImageSize> imageSizes = story.E.Q.t1();
String url = imageSizes.get(imageSizes.size() - 1).url;
OkHttpClient client = Network.b(Network.ClientType.CLIENT_IMAGE_LOADER);
Request request = new Request.a()
.b(url)
.a();
client.a(request).a(new Callback() {
@Override
public void a(Call call, IOException e) {
e.fillInStackTrace();
}
@Override
public void a(Call call, Response response) {
ResponseBody responseBody = response.a();
String fileName = "photo" + story.E.a + ".jpg";
File outputDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "/Stories/");
if (!outputDir.exists()) {
outputDir.mkdirs();
}
File outputFile = new File(outputDir, fileName);
try (InputStream inputStream = responseBody.a();
FileOutputStream outputStream = new FileOutputStream(outputFile)) {
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
outputStream.flush();
MediaScannerConnection.scanFile(AndroidUtils.getGlobalContext(), new String[]{outputFile.toString()}, null, null);
AndroidUtils.sendToast("Файл скачан в Pictures/Stories");
} catch (IOException e) {
e.fillInStackTrace();
}
}
});
}
} }
@@ -1,11 +1,9 @@
package ru.vtosters.lite.downloaders; package ru.vtosters.lite.downloaders;
import android.app.NotificationChannel; import android.app.DownloadManager;
import android.app.NotificationManager;
import android.app.ProgressDialog; import android.app.ProgressDialog;
import android.content.Context; import android.content.Context;
import android.media.MediaScannerConnection; import android.net.Uri;
import android.os.Build;
import android.os.Environment; import android.os.Environment;
import android.text.TextUtils; import android.text.TextUtils;
import android.view.View; import android.view.View;
@@ -13,159 +11,37 @@ import android.view.ViewGroup;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import androidx.core.app.NotificationCompat;
import com.vk.core.dialogs.alert.VkAlertDialog; import com.vk.core.dialogs.alert.VkAlertDialog;
import com.vk.core.dialogs.bottomsheet.MenuBottomSheetAction; import com.vk.core.dialogs.bottomsheet.MenuBottomSheetAction;
import com.vk.core.network.Network;
import com.vk.core.util.ToastUtils; import com.vk.core.util.ToastUtils;
import com.vk.dto.common.VideoFile; import com.vk.dto.common.VideoFile;
import com.vk.dto.stories.model.StoryEntry; import com.vk.dto.stories.model.StoryEntry;
import com.vtosters.lite.R; import com.vtosters.lite.R;
import okhttp3.*;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import ru.vtosters.lite.utils.AndroidUtils;
import ru.vtosters.lite.utils.LifecycleUtils; import ru.vtosters.lite.utils.LifecycleUtils;
import ru.vtosters.sponsorpost.utils.GzipDecompressor;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import static com.vk.core.network.Network.ClientType.CLIENT_API;
import static ru.vtosters.hooks.other.ThemesUtils.getTextAttr; import static ru.vtosters.hooks.other.ThemesUtils.getTextAttr;
import static ru.vtosters.lite.net.Request.makeRequest;
import static ru.vtosters.lite.proxy.ProxyUtils.getApi; import static ru.vtosters.lite.proxy.ProxyUtils.getApi;
import static ru.vtosters.lite.utils.AccountManagerUtils.getUserToken; import static ru.vtosters.lite.utils.AccountManagerUtils.getUserToken;
public class VideoDownloader { public class VideoDownloader {
public static int DOWNLOAD_ID = 0; public static final int DOWNLOAD_ID = 0;
public static int OPEN_EXTERNAL_LINK_ID = 1; public static final int OPEN_EXTERNAL_LINK_ID = 1;
public static String channelId = "video_download_channel";
public static String channelName = "Video Download Channel";
public static int notificationId = 1;
public static void addAction(List<MenuBottomSheetAction> actions, int... ints) { public static void addAction(List<MenuBottomSheetAction> actions, int... ints) {
actions.add(new MenuBottomSheetAction(ints[0], ints[1], ints[2], ints[3])); actions.add(new MenuBottomSheetAction(ints[0], ints[1], ints[2], ints[3]));
} }
public static void downloadVideo(VideoFile videoFile, Context context) { public static void downloadVideo(VideoFile videoFile, Context context) {
List<String> qualities = new ArrayList<>(); final List<String> qualities = new ArrayList<>();
List<String> urls = new ArrayList<>(); final List<String> urls = new ArrayList<>();
addVideos(videoFile, qualities, urls);
Context finalContext = context != null ? context : LifecycleUtils.getCurrentActivity();
if (qualities.isEmpty()) {
ToastUtils.a(finalContext.getString(R.string.videodownloaderr));
return;
}
var adapter = new ArrayAdapter(
finalContext,
android.R.layout.simple_list_item_1,
qualities.toArray(new String[0])) {
@Override
public View getView(int position, View convertView, ViewGroup parent) {
TextView textView = (TextView) super.getView(position, convertView, parent);
textView.setTextColor(getTextAttr());
return textView;
}
};
new VkAlertDialog.Builder(finalContext)
.setAdapter(adapter, (dialog, which) -> {
String url = urls.get(which);
NotificationManager notificationManager = (NotificationManager) AndroidUtils.getGlobalContext().getSystemService(Context.NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel notificationChannel = new NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_LOW);
notificationManager.createNotificationChannel(notificationChannel);
}
String notificationTitle = "Загрузка видео";
String notificationText = "Загрузка началась";
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(AndroidUtils.getGlobalContext(), channelId)
.setSmallIcon(android.R.drawable.stat_sys_download)
.setContentTitle(notificationTitle)
.setContentText(notificationText)
.setProgress(0, 0, true)
.setOngoing(true);
notificationManager.notify(notificationId, notificationBuilder.build());
OkHttpClient client = Network.b(Network.ClientType.CLIENT_IMAGE_LOADER);;
Request request = new Request.a()
.b(url)
.a();
client.a(request).a(new Callback() {
@Override
public void a(Call call, IOException e) {
e.fillInStackTrace();
}
@Override
public void a(Call call, Response response) {
ResponseBody responseBody = response.a();
File outputDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES), "/VKVideo/");
if (!outputDir.exists()) {
outputDir.mkdirs();
}
File outputFile = new File(outputDir, videoFile + ".mp4");
try (InputStream inputStream = responseBody.a();
FileOutputStream outputStream = new FileOutputStream(outputFile)) {
byte[] buffer = new byte[4096];
int bytesRead;
long totalBytesRead = 0;
long fileSize = responseBody.d();
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
totalBytesRead += bytesRead;
int progress = (int) ((totalBytesRead * 100) / fileSize);
notificationBuilder.setProgress(100, progress, false)
.setContentText("Загружено " + progress + "%");
notificationManager.notify(notificationId, notificationBuilder.build());
}
outputStream.flush();
MediaScannerConnection.scanFile(finalContext, new String[]{outputFile.toString()}, null, null);
notificationManager.cancel(notificationId);
String successMessage = "Файл успешно скачан";
notificationBuilder.setContentText(successMessage)
.setProgress(0, 0, false)
.setContentTitle(notificationTitle)
.setSmallIcon(android.R.drawable.stat_sys_download_done)
.setOngoing(false);
notificationManager.notify(notificationId, notificationBuilder.build());
} catch (IOException e) {
e.fillInStackTrace();
}
}
});
})
.show();
}
public static void addVideos(VideoFile videoFile, List<String> qualities, List<String> urls) {
if (!TextUtils.isEmpty(videoFile.D)) { if (!TextUtils.isEmpty(videoFile.D)) {
qualities.add("2160p"); qualities.add("2160p");
urls.add(videoFile.D); urls.add(videoFile.D);
@@ -199,6 +75,40 @@ public class VideoDownloader {
qualities.add("240p"); qualities.add("240p");
urls.add(videoFile.e); urls.add(videoFile.e);
} }
Context finalContext = context != null ? context : LifecycleUtils.getCurrentActivity();
if (qualities.isEmpty()) {
ToastUtils.a(finalContext.getString(R.string.videodownloaderr));
return;
}
var adapter = new ArrayAdapter(
finalContext,
android.R.layout.simple_list_item_1,
qualities.toArray(new String[0])) {
@Override
public View getView(int position, View convertView, ViewGroup parent) {
TextView textView = (TextView) super.getView(position, convertView, parent);
textView.setTextColor(getTextAttr());
return textView;
}
};
new VkAlertDialog.Builder(finalContext)
.setAdapter(adapter, (dialog, which) -> {
try {
var url = urls.get(which);
var request = new DownloadManager.Request(Uri.parse(url))
.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
.setTitle(videoFile.toString())
.setDestinationInExternalPublicDir(Environment.DIRECTORY_MOVIES, videoFile + ".mp4");
request.allowScanningByMediaScanner();
((DownloadManager) finalContext.getSystemService(Context.DOWNLOAD_SERVICE)).enqueue(request);
} catch (Throwable ignored) {
}
})
.show();
} }
public static void parseVideoLink(String url, Context ctx) { public static void parseVideoLink(String url, Context ctx) {
@@ -214,35 +124,20 @@ public class VideoDownloader {
progressDialog.setMessage(ctx.getString(R.string.video_dl_progress)); progressDialog.setMessage(ctx.getString(R.string.video_dl_progress));
progressDialog.show(); progressDialog.show();
var req = new Request.a() makeRequest("https://" + getApi() + "/method/stories.getById?stories=" + storyId + "&v=5.99&access_token=" + getUserToken(),
.b("https://" + getApi() + "/method/stories.getById?stories=" + storyId + "&v=5.99&access_token=" + getUserToken()) response -> {
.a("Accept-Encoding", "gzip") progressDialog.cancel();
.a();
Network.b(CLIENT_API).a(req).a(new Callback() {
@Override
public void a(Call call, IOException e) {
e.fillInStackTrace();
Toast.makeText(ctx, ctx.getString(R.string.video_dl_error), Toast.LENGTH_SHORT).show();
}
@Override
public void a(Call call, Response response) throws IOException {
progressDialog.cancel();
try {
JSONObject mainJson = new JSONObject(GzipDecompressor.decompressResponse(response));
JSONObject responseJson = mainJson.getJSONObject("response").optJSONArray("items").optJSONObject(0);
StoryEntry story = new StoryEntry(responseJson);
StoryDownloader.downloadStory(story);
} catch (JSONException e) {
e.fillInStackTrace();
Toast.makeText(ctx, ctx.getString(R.string.video_dl_error), Toast.LENGTH_SHORT).show();
}
}
});
try {
JSONObject mainJson = new JSONObject(response);
JSONObject responseJson = mainJson.getJSONObject("response").optJSONArray("items").optJSONObject(0);
StoryEntry story = new StoryEntry(responseJson);
StoryDownloader.downloadStory(story);
} catch (JSONException e) {
e.printStackTrace();
Toast.makeText(ctx, ctx.getString(R.string.video_dl_error), Toast.LENGTH_SHORT).show();
}
});
return; return;
} }
@@ -254,38 +149,25 @@ public class VideoDownloader {
String videoId = url.split("video")[1]; String videoId = url.split("video")[1];
String ownerId = videoId.split("_")[0]; String ownerId = videoId.split("_")[0];
ProgressDialog progressDialog = new ProgressDialog(ctx); final ProgressDialog progressDialog = new ProgressDialog(ctx);
progressDialog.setMessage(ctx.getString(R.string.video_dl_progress)); progressDialog.setMessage(ctx.getString(R.string.video_dl_progress));
progressDialog.show(); progressDialog.show();
var req = new Request.a() makeRequest("https://" + getApi() + "/method/video.get?owner_id=" + ownerId + "&videos=" + videoId + "&v=5.99&access_token=" + getUserToken(),
.b("https://" + getApi() + "/method/video.get?owner_id=" + ownerId + "&videos=" + videoId + "&v=5.99&access_token=" + getUserToken()) response -> {
.a("Accept-Encoding", "gzip") progressDialog.cancel();
.a();
Network.b(CLIENT_API).a(req).a(new Callback() { try {
@Override JSONObject mainJson = new JSONObject(response);
public void a(Call call, IOException e) { JSONObject responseJson = mainJson.getJSONObject("response");
e.fillInStackTrace(); JSONArray itemsJson = responseJson.getJSONArray("items");
Toast.makeText(ctx, ctx.getString(R.string.video_dl_error), Toast.LENGTH_SHORT).show();
}
@Override VideoFile videoFile = new VideoFile(itemsJson.getJSONObject(0));
public void a(Call call, Response response) throws IOException { downloadVideo(videoFile, ctx);
progressDialog.cancel(); } catch (JSONException e) {
e.printStackTrace();
try { Toast.makeText(ctx, ctx.getString(R.string.video_dl_error), Toast.LENGTH_SHORT).show();
JSONObject mainJson = new JSONObject(GzipDecompressor.decompressResponse(response)); }
JSONObject responseJson = mainJson.getJSONObject("response"); });
JSONArray itemsJson = responseJson.getJSONArray("items");
VideoFile videoFile = new VideoFile(itemsJson.getJSONObject(0));
downloadVideo(videoFile, ctx);
} catch (JSONException e) {
e.fillInStackTrace();
Toast.makeText(ctx, ctx.getString(R.string.video_dl_error), Toast.LENGTH_SHORT).show();
}
}
});
} }
} }
@@ -3,7 +3,6 @@ package ru.vtosters.lite.downloaders.messages;
import android.util.Log; import android.util.Log;
import android.util.SparseArray; import android.util.SparseArray;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.vk.core.network.Network;
import com.vk.core.util.ToastUtils; import com.vk.core.util.ToastUtils;
import com.vtosters.lite.R; import com.vtosters.lite.R;
import okhttp3.Call; import okhttp3.Call;
@@ -13,12 +12,12 @@ import okhttp3.Response;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import ru.vtosters.lite.di.singleton.VtOkHttpClient;
import ru.vtosters.lite.downloaders.messages.items.MiniMsg; import ru.vtosters.lite.downloaders.messages.items.MiniMsg;
import ru.vtosters.lite.downloaders.messages.items.MiniUser; import ru.vtosters.lite.downloaders.messages.items.MiniUser;
import ru.vtosters.lite.proxy.ProxyUtils; import ru.vtosters.lite.proxy.ProxyUtils;
import ru.vtosters.lite.utils.AccountManagerUtils; import ru.vtosters.lite.utils.AccountManagerUtils;
import ru.vtosters.lite.utils.AndroidUtils; import ru.vtosters.lite.utils.AndroidUtils;
import ru.vtosters.sponsorpost.utils.GzipDecompressor;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
@@ -29,8 +28,6 @@ import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import static com.vk.core.network.Network.ClientType.CLIENT_API;
public class MessagesDownloader { public class MessagesDownloader {
private static final int[] VIDEO_QUALITIES = {144, 240, 360, 480, 720, 1080, 1440, 2160}; private static final int[] VIDEO_QUALITIES = {144, 240, 360, 480, 720, 1080, 1440, 2160};
@@ -107,9 +104,8 @@ public class MessagesDownloader {
AccountManagerUtils.getUserToken()); AccountManagerUtils.getUserToken());
var req = new Request.a() var req = new Request.a()
.b(url) .b(url)
.a("Accept-Encoding", "gzip")
.a(); .a();
Network.b(CLIENT_API).a(req).a(new Callback() { VtOkHttpClient.getInstance().a(req).a(new Callback() {
@Override @Override
public void a(Call call, IOException e) { public void a(Call call, IOException e) {
Log.d("MessagesDownloader", e.getMessage()); Log.d("MessagesDownloader", e.getMessage());
@@ -118,7 +114,7 @@ public class MessagesDownloader {
@Override @Override
public void a(Call call, Response response) { public void a(Call call, Response response) {
try { try {
JSONObject obj = new JSONObject(GzipDecompressor.decompressResponse(response)).getJSONObject("response"); JSONObject obj = new JSONObject(response.a().g()).getJSONObject("response");
SparseArray<MiniUser> users = new SparseArray<>(); SparseArray<MiniUser> users = new SparseArray<>();
parseUsers(users, obj.optJSONArray("profiles"), false); parseUsers(users, obj.optJSONArray("profiles"), false);
@@ -1,11 +1,9 @@
package ru.vtosters.lite.foaf; package ru.vtosters.lite.foaf;
import android.annotation.SuppressLint;
import android.app.ProgressDialog; import android.app.ProgressDialog;
import android.content.Context; import android.content.Context;
import android.util.Log; import android.util.Log;
import com.vk.core.dialogs.alert.VkAlertDialog; import com.vk.core.dialogs.alert.VkAlertDialog;
import com.vk.core.network.Network;
import com.vtosters.lite.R; import com.vtosters.lite.R;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
import okhttp3.Request; import okhttp3.Request;
@@ -13,11 +11,8 @@ import okhttp3.Response;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import ru.vtosters.lite.di.singleton.VtOkHttpClient; import ru.vtosters.lite.di.singleton.VtOkHttpClient;
import ru.vtosters.lite.proxy.ProxyUtils;
import ru.vtosters.lite.proxy.api.VikaMobile;
import ru.vtosters.lite.utils.AndroidUtils; import ru.vtosters.lite.utils.AndroidUtils;
import ru.vtosters.lite.utils.LifecycleUtils; import ru.vtosters.lite.utils.LifecycleUtils;
import ru.vtosters.sponsorpost.utils.GzipDecompressor;
import java.io.IOException; import java.io.IOException;
import java.time.LocalDate; import java.time.LocalDate;
@@ -29,7 +24,8 @@ import java.util.Optional;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import static com.vk.core.network.Network.ClientType.CLIENT_API; import static ru.vtosters.lite.proxy.ProxyUtils.getApi;
import static ru.vtosters.lite.proxy.ProxyUtils.isAnyProxyEnabled;
public class FoafBase { public class FoafBase {
private static final Pattern FOAF_REGEX = Pattern.compile("<ya:created dc:date=\"(.+?)\""); private static final Pattern FOAF_REGEX = Pattern.compile("<ya:created dc:date=\"(.+?)\"");
@@ -40,14 +36,13 @@ public class FoafBase {
public static JSONObject getBypassedOnlineInfo(int id) throws JSONException { public static JSONObject getBypassedOnlineInfo(int id) throws JSONException {
Request request = new Request.a() Request request = new Request.a()
.b(API_VKNEXT + id) .b(API_VKNEXT + id)
.a("Accept-Encoding", "gzip")
.a(); .a();
JSONObject dummy = new JSONObject(); JSONObject dummy = new JSONObject();
dummy.put("last_seen", 0); dummy.put("last_seen", 0);
try (Response response = client.a(request).execute()) { try (Response response = client.a(request).execute()) {
JSONObject online_info = new JSONObject(GzipDecompressor.decompressResponse(response)).getJSONObject("response").optJSONObject(Integer.toString(id)); JSONObject online_info = new JSONObject(response.a().g()).getJSONObject("response").optJSONObject(Integer.toString(id));
return online_info == null ? dummy : online_info; return online_info == null ? dummy : online_info;
} catch (Exception e) { } catch (Exception e) {
Log.e("GetBypassedOnlineInfo", e.getMessage()); Log.e("GetBypassedOnlineInfo", e.getMessage());
@@ -58,13 +53,12 @@ public class FoafBase {
public static JSONObject getBypassedOnlineInfo(String ids) { public static JSONObject getBypassedOnlineInfo(String ids) {
Request request = new Request.a() Request request = new Request.a()
.b(API_VKNEXT + ids) .b(API_VKNEXT + ids)
.a("Accept-Encoding", "gzip")
.a(); .a();
JSONObject dummy = new JSONObject(); JSONObject dummy = new JSONObject();
try (Response response = client.a(request).execute()) { try (Response response = client.a(request).execute()) {
JSONObject online_info = new JSONObject(GzipDecompressor.decompressResponse(response)).getJSONObject("response").optJSONObject("response"); JSONObject online_info = new JSONObject(response.a().g()).getJSONObject("response").optJSONObject("response");
return online_info == null ? dummy : online_info; return online_info == null ? dummy : online_info;
} catch (Exception e) { } catch (Exception e) {
Log.e("GetBypassedOnlineInfo1", e.getMessage()); Log.e("GetBypassedOnlineInfo1", e.getMessage());
@@ -72,9 +66,9 @@ public class FoafBase {
} }
} }
@SuppressLint("DefaultLocale")
private static String getLink(int i) { private static String getLink(int i) {
return ProxyUtils.isAnyProxyEnabled() ? String.format("https://%s/_/vk.com/foaf.php?id=%d", ProxyUtils.getApi(), i) : String.format("https://vk.com/foaf.php?id=%d", i); String prefix = isAnyProxyEnabled() ? "https://" + getApi() + "/_/vk.com/foaf.php?id=" : "https://vk.com/foaf.php?id=";
return prefix + i;
} }
public static void loadAndShow(Context context, int i) { public static void loadAndShow(Context context, int i) {
@@ -85,13 +79,12 @@ public class FoafBase {
Request request = new Request.a() Request request = new Request.a()
.b(getLink(i)) .b(getLink(i))
.a("Accept-Encoding", "gzip")
.a(); .a();
try (Response response = Network.b(CLIENT_API).a(request).execute()) { try (Response response = client.a(request).execute()) {
LifecycleUtils.getCurrentActivity().runOnUiThread(() -> { LifecycleUtils.getCurrentActivity().runOnUiThread(() -> {
try { try {
showFoafInfo(progressDialog, context, i, GzipDecompressor.decompressResponse(response)); showFoafInfo(progressDialog, context, i, response.a().g());
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@@ -127,7 +120,6 @@ public class FoafBase {
} }
} }
@SuppressLint("DefaultLocale")
private static void showFoafDialog(Context context, int i, String normalHumanDate, long daysPassed) { private static void showFoafDialog(Context context, int i, String normalHumanDate, long daysPassed) {
String message = String.format("%s %d%s %s%s %d", context.getString(R.string.foafid), i, context.getString(R.string.foafregdate), normalHumanDate, context.getString(R.string.foafdate), daysPassed); String message = String.format("%s %d%s %s%s %d", context.getString(R.string.foafid), i, context.getString(R.string.foafregdate), normalHumanDate, context.getString(R.string.foafdate), daysPassed);
new VkAlertDialog.Builder(context) new VkAlertDialog.Builder(context)
@@ -2,24 +2,19 @@ package ru.vtosters.lite.music;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import com.vk.dto.music.MusicTrack; import com.vk.dto.music.MusicTrack;
import com.vtosters.lite.R; import com.vtosters.lite.R;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.net.URLEncoder;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
import okhttp3.Request; import okhttp3.Request;
import okhttp3.Response; import okhttp3.Response;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import ru.vtosters.lite.di.singleton.VtOkHttpClient; import ru.vtosters.lite.di.singleton.VtOkHttpClient;
import ru.vtosters.lite.utils.AndroidUtils; import ru.vtosters.lite.utils.AndroidUtils;
import ru.vtosters.lite.utils.music.MusicTrackUtils;
import ru.vtosters.sponsorpost.utils.GzipDecompressor; import java.io.IOException;
import java.net.URLEncoder;
public class Genius { public class Genius {
private static final String KEY = "Bearer ZTejoT_ojOEasIkT9WrMBhBQOz6eYKK5QULCMECmOhvwqjRZ6WbpamFe3geHnvp3"; private static final String KEY = "Bearer ZTejoT_ojOEasIkT9WrMBhBQOz6eYKK5QULCMECmOhvwqjRZ6WbpamFe3geHnvp3";
@@ -27,10 +22,10 @@ public class Genius {
private static final OkHttpClient client = VtOkHttpClient.getInstance(); private static final OkHttpClient client = VtOkHttpClient.getInstance();
public static String getTextMusic(MusicTrack musictrack) { public static String getTextMusic(MusicTrack musictrack) {
String uid = musictrack.y1(); var uid = musictrack.y1();
String artist = removeExtras(MusicTrackUtils.getArtists(musictrack)); var artist = removeExtras(musictrack.C);
String title = removeExtras(musictrack.f); var title = removeExtras(musictrack.f);
int duration = musictrack.h; var duration = musictrack.h;
if (TextUtils.isEmpty(uid) || TextUtils.isEmpty(artist) || TextUtils.isEmpty(title) || duration == 0) { if (TextUtils.isEmpty(uid) || TextUtils.isEmpty(artist) || TextUtils.isEmpty(title) || duration == 0) {
Log.d("Genius", "grabTrackInfo: " + "Empty track, info: " + artist + " - " + title + " - " + duration + " - " + uid); Log.d("Genius", "grabTrackInfo: " + "Empty track, info: " + artist + " - " + title + " - " + duration + " - " + uid);
@@ -47,18 +42,17 @@ public class Genius {
} }
Request request = new Request.a() Request request = new Request.a()
.b(String.format("%s/search/songs?q=%s&from_background=0&page=1", URL, URLEncoder.encode(artist + " " + track))) .b(URL + "/search/songs?q=" + URLEncoder.encode(artist + " " + track) + "&from_background=0" + "&page=1")
.a("User-Agent", "Genius/5.21.3 (Android; Android 13; Google Pixel 4 XL)") .a("User-Agent", "Genius/5.14.0 (Android; Android 13; Google Pixel 4 XL)")
.a("Authorization", KEY) .a("Authorization", KEY)
.a("x-genius-app-background-request", "0") .a("x-genius-app-background-request", "0")
.a("x-genius-logged-out", "true") .a("x-genius-logged-out", "true")
.a("host", "api.genius.com") .a("host", "api.genius.com")
.a("Content-Type", "application/x-www-form-urlencoded") .a("Content-Type", "application/x-www-form-urlencoded")
.a("Accept-Encoding", "gzip")
.b() .b()
.a(); .a();
try (Response resp = client.a(request).execute()) { try (Response resp = client.a(request).execute()) {
String payload = GzipDecompressor.decompressResponse(resp); String payload = resp.a().g();
JSONArray sections = new JSONObject(payload).getJSONObject("response").getJSONArray("sections"); JSONArray sections = new JSONObject(payload).getJSONObject("response").getJSONArray("sections");
for (int i = 0; i < sections.length(); i++) { for (int i = 0; i < sections.length(); i++) {
@@ -92,18 +86,17 @@ public class Genius {
private static String getText(String track) { private static String getText(String track) {
Request request = new Request.a() Request request = new Request.a()
.b(String.format("%s%s?text_format=plain", URL, track)) .b(URL + track + "?text_format=plain")
.a("User-Agent", "Genius/5.21.3 (Android; Android 13; Google Pixel 4 XL)") .a("User-Agent", "Genius/5.14.0 (Android; Android 13; Google Pixel 4 XL)")
.a("Authorization", KEY) .a("Authorization", KEY)
.a("x-genius-app-background-request", "0") .a("x-genius-app-background-request", "0")
.a("x-genius-logged-out", "true") .a("x-genius-logged-out", "true")
.a("host", "api.genius.com") .a("host", "api.genius.com")
.a("Content-Type", "application/x-www-form-urlencoded") .a("Content-Type", "application/x-www-form-urlencoded")
.a("Accept-Encoding", "gzip")
.b() .b()
.a(); .a();
try (Response resp = client.a(request).execute()) { try {
JSONObject payload = new JSONObject(GzipDecompressor.decompressResponse(resp)); JSONObject payload = new JSONObject(client.a(request).execute().a().g());
if (!payload.has("response")) { if (!payload.has("response")) {
return AndroidUtils.getString("error_no_text"); return AndroidUtils.getString("error_no_text");
@@ -3,14 +3,18 @@ package ru.vtosters.lite.music;
import android.os.RemoteException; import android.os.RemoteException;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import bruhcollective.itaysonlab.libvkx.client.LibVKXClient;
import com.vk.dto.music.MusicTrack; import com.vk.dto.music.MusicTrack;
import com.vtosters.lite.R; import com.vtosters.lite.R;
import okhttp3.*;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import ru.vtosters.hooks.other.Preferences;
import ru.vtosters.lite.di.singleton.VtOkHttpClient;
import ru.vtosters.lite.downloaders.AudioDownloader;
import ru.vtosters.lite.music.cache.MusicCacheImpl;
import ru.vtosters.lite.utils.AndroidUtils;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
@@ -19,21 +23,6 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.TreeMap; import java.util.TreeMap;
import bruhcollective.itaysonlab.libvkx.client.LibVKXClient;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.FormBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import ru.vtosters.hooks.other.Preferences;
import ru.vtosters.lite.di.singleton.VtOkHttpClient;
import ru.vtosters.lite.downloaders.AudioDownloader;
import ru.vtosters.lite.music.cache.MusicCacheImpl;
import ru.vtosters.lite.utils.AccountManagerUtils;
import ru.vtosters.lite.utils.AndroidUtils;
import ru.vtosters.lite.utils.music.MusicTrackUtils;
public class LastFMScrobbler { public class LastFMScrobbler {
private static final String KEY = "5965d63402414776c54c266db0211746"; private static final String KEY = "5965d63402414776c54c266db0211746";
private static final String KEY_SECRET = "d982180eed13275bb948e41cf225f88f"; private static final String KEY_SECRET = "d982180eed13275bb948e41cf225f88f";
@@ -43,7 +32,7 @@ public class LastFMScrobbler {
public static void grabMusicTrack(MusicTrack musictrack) { public static void grabMusicTrack(MusicTrack musictrack) {
var uid = musictrack.y1(); var uid = musictrack.y1();
var artist = MusicTrackUtils.getArtists(musictrack); var artist = musictrack.C;
var title = musictrack.f; var title = musictrack.f;
var duration = musictrack.h; var duration = musictrack.h;
var track_id = musictrack.d; var track_id = musictrack.d;
@@ -51,14 +40,13 @@ public class LastFMScrobbler {
var access_key = !TextUtils.isEmpty(musictrack.J) ? musictrack.J : ""; var access_key = !TextUtils.isEmpty(musictrack.J) ? musictrack.J : "";
var album_id = musictrack.I; var album_id = musictrack.I;
var album = album_id != null ? album_id.getTitle() : null; var album = album_id != null ? album_id.getTitle() : null;
var isOwnTrack = musictrack.e == AccountManagerUtils.getUserId();
if (TextUtils.isEmpty(uid) || TextUtils.isEmpty(artist) || TextUtils.isEmpty(title) || duration == 0) { if (TextUtils.isEmpty(uid) || TextUtils.isEmpty(artist) || TextUtils.isEmpty(title) || duration == 0) {
Log.d("Scrobbler", "grabTrackInfo: " + "Empty track, info: " + artist + " - " + title + " - " + duration + " - " + uid); Log.d("Scrobbler", "grabTrackInfo: " + "Empty track, info: " + artist + " - " + title + " - " + duration + " - " + uid);
return; return;
} }
if (needToCache(isOwnTrack) && !MusicCacheImpl.isCachedTrack(uid)) { if (Preferences.autocache() && !MusicCacheImpl.isCachedTrack(uid)) {
if (LibVKXClient.isIntegrationEnabled()) { if (LibVKXClient.isIntegrationEnabled()) {
LibVKXClient.getInstance().runOnService(service -> { LibVKXClient.getInstance().runOnService(service -> {
try { try {
@@ -75,12 +63,6 @@ public class LastFMScrobbler {
scrobbleTrack(duration, artist, title, uid, album); scrobbleTrack(duration, artist, title, uid, album);
} }
private static boolean needToCache(boolean isOwnTrack) {
int autocache = Preferences.autocache();
return autocache == 2 || (autocache == 1 && isOwnTrack);
}
public static void scrobbleTrack( public static void scrobbleTrack(
long duration, long duration,
String artist, String artist,
@@ -101,12 +83,12 @@ public class LastFMScrobbler {
params.put("sk", getSessionKey()); params.put("sk", getSessionKey());
params.put("artist[0]", artist); params.put("artist[0]", artist);
params.put("track[0]", title); params.put("track[0]", title);
if (!TextUtils.isEmpty(album)) { if (!TextUtils.isEmpty(album))
params.put("album[0]", album); params.put("album[0]", album);
}
params.put("duration[0]", String.valueOf(duration)); params.put("duration[0]", String.valueOf(duration));
params.put("timestamp[0]", String.valueOf(System.currentTimeMillis() / 1000)); params.put("timestamp[0]", String.valueOf(System.currentTimeMillis() / 1000));
fetch(params, new Callback() { fetch(params, new Callback() {
@Override @Override
public void a(Call call, IOException e) { public void a(Call call, IOException e) {
@@ -168,9 +150,8 @@ public class LastFMScrobbler {
params.put("format", "json"); params.put("format", "json");
var reqBodyBuilder = new FormBody.a(StandardCharsets.UTF_8); var reqBodyBuilder = new FormBody.a(StandardCharsets.UTF_8);
for (var entry : params.entrySet()) { for (var entry : params.entrySet())
reqBodyBuilder.a(entry.getKey(), entry.getValue()); reqBodyBuilder.a(entry.getKey(), entry.getValue());
}
var req = new Request.a() var req = new Request.a()
.b(URL) .b(URL)
@@ -183,7 +164,6 @@ public class LastFMScrobbler {
public static void fetchSession(JSONObject json) throws JSONException { public static void fetchSession(JSONObject json) throws JSONException {
if (!json.has("session")) { if (!json.has("session")) {
Log.d("Scrobbler", "Auth failed - no session"); Log.d("Scrobbler", "Auth failed - no session");
AndroidUtils.sendToast("Ошибка входа");
return; return;
} }
@@ -197,8 +177,6 @@ public class LastFMScrobbler {
.putBoolean("lastfm_enabled", true) .putBoolean("lastfm_enabled", true)
.apply(); .apply();
AndroidUtils.sendToast("Успешный вход");
Log.d("Scrobbler", "Auth success as " + name + ", key " + key); Log.d("Scrobbler", "Auth success as " + name + ", key " + key);
} }
@@ -1,16 +1,16 @@
package ru.vtosters.lite.music.cache.delegate; package ru.vtosters.lite.music.cache;
import android.content.Context; import android.content.Context;
import com.vk.dto.music.MusicTrack; import com.vk.dto.music.MusicTrack;
import ru.vtosters.lite.music.cache.db.MusicCacheDb;
import java.util.List; import java.util.List;
import ru.vtosters.lite.music.cache.db.MusicCacheDb;
public class MusicCacheDbDelegate { public class MusicCacheDbDelegate {
private static MusicCacheDb connectToDb(Context context) { static synchronized private MusicCacheDb connectToDb(Context context) {
return new MusicCacheDb(context); synchronized (MusicCacheDb.class) {
return new MusicCacheDb(context);
}
} }
public static void addTrack(Context context, public static void addTrack(Context context,
@@ -43,9 +43,27 @@ public class MusicCacheDbDelegate {
} }
} }
public static MusicTrack getTrackById(Context context, String trackId) { public static List<MusicTrack> getAllTracks(Context context) {
try (var db = connectToDb(context)) { try (var db = connectToDb(context)) {
return db.getTrackById(trackId); return db.getAllTracks();
}
}
public static List<MusicTrack> getAlbumById(Context context, String albumId) {
try (var db = connectToDb(context)) {
return db.getAlbum(albumId);
}
}
public static List<MusicTrack> getFirstAlbumTrack(Context context, String albumId) {
try (var db = connectToDb(context)) {
return db.getFirstAlbumTrack(albumId);
}
}
public static List<MusicTrack> getPlaylist(Context context) {
try (var db = connectToDb(context)) {
return db.getPlaylist();
} }
} }
@@ -55,12 +73,6 @@ public class MusicCacheDbDelegate {
} }
} }
public static boolean isEmpty(Context context) {
try (var db = connectToDb(context)) {
return db.isDatabaseEmpty();
}
}
public static boolean isCachedTrack(Context context, String trackId) { public static boolean isCachedTrack(Context context, String trackId) {
try (var db = connectToDb(context)) { try (var db = connectToDb(context)) {
return db.isCachedTrack(trackId); return db.isCachedTrack(trackId);
@@ -2,21 +2,14 @@ package ru.vtosters.lite.music.cache;
import android.os.RemoteException; import android.os.RemoteException;
import android.text.TextUtils; import android.text.TextUtils;
import com.vk.dto.music.MusicTrack;
import java.util.List;
import bruhcollective.itaysonlab.libvkx.ILibVkxService; import bruhcollective.itaysonlab.libvkx.ILibVkxService;
import bruhcollective.itaysonlab.libvkx.client.LibVKXClient; import bruhcollective.itaysonlab.libvkx.client.LibVKXClient;
import bruhcollective.itaysonlab.libvkx.client.LibVKXClientImpl; import bruhcollective.itaysonlab.libvkx.client.LibVKXClientImpl;
import com.vk.dto.music.Playlist; import com.vk.dto.music.MusicTrack;
import ru.vtosters.lite.music.cache.delegate.MusicCacheDbDelegate;
import ru.vtosters.lite.music.cache.delegate.PlaylistCacheDbDelegate;
import ru.vtosters.lite.utils.AccountManagerUtils;
import ru.vtosters.lite.utils.AndroidUtils; import ru.vtosters.lite.utils.AndroidUtils;
import ru.vtosters.lite.utils.music.MusicCacheStorageUtils; import ru.vtosters.lite.utils.music.MusicCacheStorageUtils;
import ru.vtosters.lite.utils.music.MusicTrackUtils;
import java.util.List;
public class MusicCacheImpl { public class MusicCacheImpl {
public static void addTrack(MusicTrack track) { public static void addTrack(MusicTrack track) {
@@ -26,7 +19,7 @@ public class MusicCacheImpl {
track.I != null ? track.I.getId() + "" : "-1", track.I != null ? track.I.getId() + "" : "-1",
track.f, track.f,
!TextUtils.isEmpty(track.g) ? track.g : "", !TextUtils.isEmpty(track.g) ? track.g : "",
MusicTrackUtils.getArtists(track), track.C,
track.I != null ? track.I.getTitle() : "", track.I != null ? track.I.getTitle() : "",
track.K, track.K,
track.h, track.h,
@@ -38,33 +31,25 @@ public class MusicCacheImpl {
MusicCacheStorageUtils.removeTrackDirById(trackId); MusicCacheStorageUtils.removeTrackDirById(trackId);
} }
public static List<MusicTrack> getAllOwnTracks() { public static List<MusicTrack> getAllTracks() {
return PlaylistCacheDbDelegate.getTracksInPlaylist(AndroidUtils.getGlobalContext(), AccountManagerUtils.getUserId() + "_-1"); return MusicCacheDbDelegate.getAllTracks(AndroidUtils.getGlobalContext());
} }
public static List<MusicTrack> getPlaylistSongs(String owner_id, String playlist_id) { public static List<MusicTrack> getAlbumById(String albumId) {
return PlaylistCacheDbDelegate.getTracksInPlaylist(AndroidUtils.getGlobalContext(), owner_id + "_" + playlist_id); // get songs from playlist return MusicCacheDbDelegate.getAlbumById(AndroidUtils.getGlobalContext(), albumId);
} }
public static Playlist getPlaylist(String playlist_id, String owner_id) { public static List<MusicTrack> getFirstAlbumTrack(String albumId) {
return getPlaylistById(owner_id + "_" + playlist_id); // get playlist from db return MusicCacheDbDelegate.getFirstAlbumTrack(AndroidUtils.getGlobalContext(), albumId);
} }
public static List<Playlist> getPlaylists() { public static List<MusicTrack> getPlaylist() {
return PlaylistCacheDbDelegate.getAllPlaylists(AndroidUtils.getGlobalContext()); // get all cached playlists return MusicCacheDbDelegate.getPlaylist(AndroidUtils.getGlobalContext());
}
public static Playlist getPlaylistById(String query) {
return PlaylistCacheDbDelegate.getPlaylistById(AndroidUtils.getGlobalContext(), query); // get playlist from db
}
public static boolean hasPlaylist() {
return !PlaylistCacheDbDelegate.isPlaylistsDbEmpty(AndroidUtils.getGlobalContext()); // has playlists or not
} }
public static long getTracksCount() { public static long getTracksCount() {
return !LibVKXClient.isIntegrationEnabled() return !LibVKXClient.isIntegrationEnabled()
? PlaylistCacheDbDelegate.getTracksCountInPlaylist(AndroidUtils.getGlobalContext(),AccountManagerUtils.getUserId() + "_-1") ? MusicCacheDbDelegate.getTracksCount(AndroidUtils.getGlobalContext())
: LibVKXClient.getInstance().runOnServiceSync( : LibVKXClient.getInstance().runOnServiceSync(
new LibVKXClientImpl.LibVKXActionGeneric<Long>() { new LibVKXClientImpl.LibVKXActionGeneric<Long>() {
@Override @Override
@@ -72,7 +57,7 @@ public class MusicCacheImpl {
try { try {
return (long) service.getCache().size(); return (long) service.getCache().size();
} catch (RemoteException e) { } catch (RemoteException e) {
e.fillInStackTrace(); e.printStackTrace();
return defaultValue(); return defaultValue();
} }
} }
@@ -89,12 +74,11 @@ public class MusicCacheImpl {
} }
public static boolean isEmpty() { public static boolean isEmpty() {
return !LibVKXClient.isIntegrationEnabled() && MusicCacheDbDelegate.isEmpty(AndroidUtils.getGlobalContext()); return !LibVKXClient.isIntegrationEnabled() && getTracksCount() == 0L;
} }
public static void clear() { public static void clear() {
MusicCacheDbDelegate.drop(AndroidUtils.getGlobalContext()); MusicCacheDbDelegate.drop(AndroidUtils.getGlobalContext());
PlaylistCacheDbDelegate.drop(AndroidUtils.getGlobalContext());
MusicCacheStorageUtils.clear(); MusicCacheStorageUtils.clear();
} }
} }
@@ -8,14 +8,13 @@ import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteOpenHelper;
import android.net.Uri; import android.net.Uri;
import android.util.Log; import android.util.Log;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.vk.dto.music.AlbumLink; import com.vk.dto.music.AlbumLink;
import com.vk.dto.music.MusicTrack; import com.vk.dto.music.MusicTrack;
import com.vk.dto.music.Thumb; import com.vk.dto.music.Thumb;
import org.json.JSONObject; import org.json.JSONObject;
import ru.vtosters.lite.utils.AndroidUtils;
import ru.vtosters.lite.utils.music.MusicCacheStorageUtils;
import java.io.File; import java.io.File;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
@@ -24,12 +23,6 @@ import java.net.URLDecoder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import ru.vtosters.lite.music.cache.delegate.PlaylistCacheDbDelegate;
import ru.vtosters.lite.utils.AccountManagerUtils;
import ru.vtosters.lite.utils.AndroidUtils;
import ru.vtosters.lite.utils.music.MusicCacheStorageUtils;
import ru.vtosters.lite.utils.music.MusicTrackUtils;
/** /**
* <p class="note"><strong>Note:</strong> the {@link AutoCloseable} interface was * <p class="note"><strong>Note:</strong> the {@link AutoCloseable} interface was
* first added in the {@link android.os.Build.VERSION_CODES#Q} release for the {@link SQLiteOpenHelper} </p> * first added in the {@link android.os.Build.VERSION_CODES#Q} release for the {@link SQLiteOpenHelper} </p>
@@ -123,14 +116,12 @@ public class MusicCacheDb extends SQLiteOpenHelper implements AutoCloseable { //
vals.put(Constants.COLUMN_ALBUM_ID, track.I != null ? track.I.getId() + "" : "-1"); vals.put(Constants.COLUMN_ALBUM_ID, track.I != null ? track.I.getId() + "" : "-1");
vals.put(Constants.COLUMN_TITLE, track.f); vals.put(Constants.COLUMN_TITLE, track.f);
vals.put(Constants.COLUMN_SUBTITLE, track.g); vals.put(Constants.COLUMN_SUBTITLE, track.g);
vals.put(Constants.COLUMN_ARTIST, MusicTrackUtils.getArtists(track)); vals.put(Constants.COLUMN_ARTIST, track.C);
vals.put(Constants.COLUMN_ALBUM_TITLE, track.I != null ? track.I.getTitle() : ""); vals.put(Constants.COLUMN_ALBUM_TITLE, track.I != null ? track.I.getTitle() : "");
vals.put(Constants.COLUMN_EXPLICIT, Boolean.compare(track.K, true)); vals.put(Constants.COLUMN_EXPLICIT, Boolean.compare(track.K, true));
vals.put(Constants.COLUMN_DURATION, track.h); vals.put(Constants.COLUMN_DURATION, track.h);
vals.put(Constants.COLUMN_HAS_ARTWORK, track.I != null && track.I.u1() != null); vals.put(Constants.COLUMN_HAS_ARTWORK, track.I != null && track.I.u1() != null);
db.insert(Constants.TABLE_NAME, null, vals); db.insert(Constants.TABLE_NAME, null, vals);
PlaylistCacheDbDelegate.addTrackToPlaylist(AndroidUtils.getGlobalContext(), AccountManagerUtils.getUserId() + "_-1", track.y1());
} }
} }
} }
@@ -174,30 +165,18 @@ public class MusicCacheDb extends SQLiteOpenHelper implements AutoCloseable { //
return DatabaseUtils.queryNumEntries(getReadableDatabase(), Constants.TABLE_NAME); return DatabaseUtils.queryNumEntries(getReadableDatabase(), Constants.TABLE_NAME);
} }
public boolean isDatabaseEmpty() {
SQLiteDatabase db = getReadableDatabase();
Cursor cursor = db.rawQuery("SELECT EXISTS(SELECT 1 FROM " + Constants.TABLE_NAME + " LIMIT 1)", null);
cursor.moveToFirst();
boolean isEmpty = cursor.getInt(0) == 0;
cursor.close();
return isEmpty;
}
public MusicTrack getTrackById(String trackId) {
SQLiteDatabase db = getReadableDatabase();
Cursor cursor = db.query(Constants.TABLE_NAME, null, Constants.COLUMN_TRACK_ID + " = ?", new String[]{trackId}, null, null, null);
MusicTrack track = null;
if (cursor.moveToFirst()) {
track = fromCursor(cursor);
}
cursor.close();
return track;
}
public List<MusicTrack> getAllTracks() { public List<MusicTrack> getAllTracks() {
return getTracksWithCursor(getReadableDatabase().query(Constants.TABLE_NAME, null, null, null, null, null, null)); return getTracksWithCursor(getReadableDatabase().query(Constants.TABLE_NAME, null, null, null, null, null, null));
} }
public List<MusicTrack> getAlbum(String albumId) {
return getAlbumTracks(albumId, null);
}
public List<MusicTrack> getFirstAlbumTrack(String albumId) {
return getAlbumTracks(albumId, "0,1");
}
private List<MusicTrack> getAlbumTracks(String albumId, @Nullable String limit) { private List<MusicTrack> getAlbumTracks(String albumId, @Nullable String limit) {
return getTracksWithCursor(getReadableDatabase().query( return getTracksWithCursor(getReadableDatabase().query(
Constants.TABLE_NAME, Constants.TABLE_NAME,
@@ -236,7 +215,7 @@ public class MusicCacheDb extends SQLiteOpenHelper implements AutoCloseable { //
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
public @interface Constants { public @interface Constants {
int DV_VERSION = 0x3; int DV_VERSION = 0x2;
String DB_NAME = "vt_lite_cache.db"; String DB_NAME = "vt_lite_cache.db";
String TABLE_NAME = "tracks"; String TABLE_NAME = "tracks";
//columns //columns
@@ -1,306 +0,0 @@
package ru.vtosters.lite.music.cache.db;
import android.annotation.SuppressLint;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import com.vk.dto.music.MusicTrack;
import com.vk.dto.music.Playlist;
import org.json.JSONException;
import org.json.JSONObject;
import ru.vtosters.lite.music.cache.MusicCacheImpl;
import ru.vtosters.lite.music.cache.helpers.PlaylistHelper;
import ru.vtosters.lite.music.cache.helpers.TracklistHelper;
import ru.vtosters.lite.utils.music.MusicCacheStorageUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
import static ru.vtosters.lite.utils.music.MusicCacheStorageUtils.*;
public class PlaylistCacheDb extends SQLiteOpenHelper implements AutoCloseable {
public PlaylistCacheDb(Context context) {
super(context, Constants.DB_NAME, null, Constants.DV_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(Constants.CREATE_QUERY);
db.execSQL(Constants.CREATE_PLAYLIST_TRACKS_QUERY);
}
private static Playlist fromCursor(Cursor cur) throws JSONException {
@SuppressLint("Range")
String photoString = cur.getString(cur.getColumnIndex(Constants.COLUMN_PHOTO));
JSONObject photo = null;
try {
photo = new JSONObject(photoString);
} catch (JSONException e) {
// ok and
}
@SuppressLint("Range")
JSONObject playlist = PlaylistHelper.generatePlaylist(
cur.getInt(cur.getColumnIndex(Constants.COLUMN_ID)),
cur.getInt(cur.getColumnIndex(Constants.COLUMN_OWNER_ID)),
Boolean.parseBoolean(cur.getString(cur.getColumnIndex(Constants.COLUMN_IS_EXPLICIT))),
cur.getString(cur.getColumnIndex(Constants.COLUMN_TITLE)),
cur.getString(cur.getColumnIndex(Constants.COLUMN_DESCRIPTION)),
photo);
Log.d("Playlist", "generated " + Playlist.U.a(playlist).v1());
return Playlist.U.a(playlist);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL(Constants.DROP_QUERY);
onCreate(db);
}
public void addPlaylist(int id, int ownerId, boolean isExplicit, String title, String description, JSONObject photo) {
ContentValues values = new ContentValues();
values.put(Constants.COLUMN_ID, id);
values.put(Constants.COLUMN_OWNER_ID, ownerId);
values.put(Constants.COLUMN_IS_EXPLICIT, isExplicit);
values.put(Constants.COLUMN_TITLE, title);
values.put(Constants.COLUMN_DESCRIPTION, description);
values.put(Constants.COLUMN_PHOTO, String.valueOf(photo));
getWritableDatabase().insert(Constants.TABLE_NAME, null, values);
}
private void deletePlaylist(int id, int ownerId) {
getWritableDatabase().delete(Constants.TABLE_NAME, Constants.COLUMN_ID + " = ? AND " + Constants.COLUMN_OWNER_ID + " = ?", new String[]{String.valueOf(id), String.valueOf(ownerId)});
}
public boolean isCachedPlaylist(int id, int ownerId) {
Cursor cursor = getReadableDatabase().query(Constants.TABLE_NAME, new String[]{Constants.COLUMN_ID}, Constants.COLUMN_ID + " = ? AND " + Constants.COLUMN_OWNER_ID + " = ?", new String[]{String.valueOf(id), String.valueOf(ownerId)}, null, null, null);
boolean exists = cursor.getCount() > 0;
cursor.close();
return exists;
}
public long getPlaylistsCount() {
return DatabaseUtils.queryNumEntries(getReadableDatabase(), Constants.TABLE_NAME);
}
public void deletePlaylistWithTracks(String playlistId) {
SQLiteDatabase db = getWritableDatabase();
String[] parts = playlistId.split("_");
int id = Integer.parseInt(parts[1]);
int ownerId = Integer.parseInt(parts[0]);
// Get all tracks in the playlist
List<MusicTrack> tracks = getTracksInPlaylist(playlistId);
// Delete the playlist
deletePlaylist(id, ownerId);
// Delete each track from the playlist_tracks table
for (MusicTrack track : tracks) {
String trackId = track.y1();
removeTrackFromPlaylist(playlistId, trackId);
removePlaylistThumb(playlistId);
if (!isTrackInAnyOtherPlaylist(trackId, playlistId)) {
MusicCacheImpl.removeTrack(trackId);
Log.d("Playlist", "removed " + trackId);
}
}
// Delete all entries from the playlist_tracks table for this playlist
db.delete(Constants.TABLE_PLAYLIST_TRACKS, Constants.COLUMN_PLAYLIST_ID + " = ?", new String[]{playlistId});
}
private boolean isTrackInAnyOtherPlaylist(String trackId, String playlistId) {
SQLiteDatabase db = getReadableDatabase();
Cursor cursor = db.query(
Constants.TABLE_PLAYLIST_TRACKS,
null,
Constants.COLUMN_TRACK_ID + " = ? AND " + Constants.COLUMN_PLAYLIST_ID + " != ?",
new String[]{trackId, playlistId},
null, null, null
);
boolean exists = cursor.getCount() > 0;
cursor.close();
return exists;
}
public List<Playlist> getAllPlaylists() {
Cursor cursor = getReadableDatabase().query(Constants.TABLE_NAME, null, null, null, null, null, null);
List<Playlist> playlists = getPlaylistsWithCursor(cursor);
cursor.close();
return playlists;
}
public Playlist getPlaylistById(int id, int ownerId) {
SQLiteDatabase db = getReadableDatabase();
Cursor cursor = db.query(Constants.TABLE_NAME, null, Constants.COLUMN_ID + " = ? AND " + Constants.COLUMN_OWNER_ID + " = ?", new String[]{String.valueOf(id), String.valueOf(ownerId)}, null, null, null);
Playlist playlist = null;
if (cursor.moveToFirst()) {
try {
playlist = fromCursor(cursor);
} catch (JSONException e) {
throw new RuntimeException(e);
}
}
cursor.close();
return playlist;
}
private List<Playlist> getPlaylistsWithCursor(Cursor cur) {
List<Playlist> playlists = new ArrayList<>();
while (cur.moveToNext()) {
try {
playlists.add(fromCursor(cur));
} catch (JSONException e) {
throw new RuntimeException(e);
}
}
return playlists;
}
public long getTracksCountInPlaylist(String playlistId) {
SQLiteDatabase db = getReadableDatabase();
Cursor cursor = db.query(
Constants.TABLE_PLAYLIST_TRACKS,
new String[]{Constants.COLUMN_TRACK_ID},
Constants.COLUMN_PLAYLIST_ID + " = ?",
new String[]{playlistId},
null, null, null
);
long count = cursor.getCount();
cursor.close();
return count;
}
public List<String> getAllPlaylistIds() {
SQLiteDatabase db = getReadableDatabase();
List<String> playlistIds = new ArrayList<>();
Cursor cursor = db.query(
Constants.TABLE_NAME,
new String[]{Constants.COLUMN_ID, Constants.COLUMN_OWNER_ID},
null, null, null, null, null
);
if (cursor.moveToFirst()) {
do {
String playlistId = cursor.getString(cursor.getColumnIndexOrThrow(Constants.COLUMN_OWNER_ID)) + "_" + cursor.getString(cursor.getColumnIndexOrThrow(Constants.COLUMN_ID));
playlistIds.add(playlistId);
} while (cursor.moveToNext());
}
cursor.close();
return playlistIds;
}
public boolean isPlaylistsDbEmpty() {
SQLiteDatabase db = getReadableDatabase();
Cursor cursor = db.rawQuery("SELECT EXISTS(SELECT 1 FROM " + Constants.TABLE_NAME + " LIMIT 1)", null);
cursor.moveToFirst();
boolean isEmpty = cursor.getInt(0) == 0;
cursor.close();
return isEmpty;
}
public void addTrackToPlaylist(String playlistId, String trackId) {
ContentValues values = new ContentValues();
values.put(Constants.COLUMN_PLAYLIST_ID, playlistId);
values.put(Constants.COLUMN_TRACK_ID, trackId);
getWritableDatabase().insert(Constants.TABLE_PLAYLIST_TRACKS, null, values);
}
public void removeTrackFromPlaylist(String playlistId, String trackId) {
getWritableDatabase().delete(Constants.TABLE_PLAYLIST_TRACKS, Constants.COLUMN_PLAYLIST_ID + " = ? AND " + Constants.COLUMN_TRACK_ID + " = ?", new String[]{playlistId, trackId});
}
public boolean isPlaylistEmpty(String playlistId) {
SQLiteDatabase db = getReadableDatabase();
Cursor cursor = db.rawQuery("SELECT EXISTS(SELECT 1 FROM " + Constants.TABLE_PLAYLIST_TRACKS + " WHERE " + Constants.COLUMN_PLAYLIST_ID + " = ? LIMIT 1)", new String[]{playlistId});
cursor.moveToFirst();
boolean isEmpty = cursor.getInt(0) == 0;
cursor.close();
return isEmpty;
}
public List<MusicTrack> getTracksInPlaylist(String playlistId) {
SQLiteDatabase db = getReadableDatabase();
Cursor cursor = db.query(
Constants.TABLE_PLAYLIST_TRACKS,
new String[]{Constants.COLUMN_TRACK_ID},
Constants.COLUMN_PLAYLIST_ID + " = ?",
new String[]{playlistId},
null, null, null
);
List<MusicTrack> tracks = new ArrayList<>();
while (cursor.moveToNext()) {
@SuppressLint("Range")
String trackId = cursor.getString(cursor.getColumnIndex(Constants.COLUMN_TRACK_ID));
MusicTrack track = TracklistHelper.getTrack(trackId);
Log.d("Playlist", "add " + trackId + " to playlist " + playlistId);
if (track != null) {
tracks.add(track);
}
}
cursor.close();
return tracks;
}
@Override
public void onConfigure(SQLiteDatabase db) {
super.onConfigure(db);
db.setForeignKeyConstraintsEnabled(true);
}
@Retention(RetentionPolicy.SOURCE)
public @interface Constants {
int DV_VERSION = 0x2;
String DB_NAME = "vt_lite_cache_playlists.db";
String TABLE_NAME = "playlists";
//columns
String COLUMN_ID = "id";
String COLUMN_OWNER_ID = "owner_id";
String COLUMN_IS_EXPLICIT = "is_explicit";
String COLUMN_TITLE = "title";
String COLUMN_DESCRIPTION = "description";
String COLUMN_PHOTO = "photo";
// tracks
String COLUMN_TRACK_ID = "track_id";
String TABLE_PLAYLIST_TRACKS = "playlist_tracks";
String COLUMN_PLAYLIST_ID = "playlist_id";
//queries
String CREATE_QUERY = "create table " + TABLE_NAME
+ "("
+ COLUMN_ID + " INTEGER PRIMARY KEY autoincrement,"
+ COLUMN_OWNER_ID + " INTEGER not null,"
+ COLUMN_IS_EXPLICIT + " TEXT not null,"
+ COLUMN_TITLE + " TEXT not null,"
+ COLUMN_DESCRIPTION + " TEXT not null,"
+ COLUMN_PHOTO + " TEXT not null"
+ ")";
String CREATE_PLAYLIST_TRACKS_QUERY = "create table " + TABLE_PLAYLIST_TRACKS + "("
+ COLUMN_PLAYLIST_ID + " TEXT not null,"
+ COLUMN_TRACK_ID + " TEXT not null"
+ ")";
String DROP_QUERY = "drop table if exists " + TABLE_NAME;
}
}
@@ -1,149 +0,0 @@
package ru.vtosters.lite.music.cache.delegate;
import android.content.Context;
import android.net.Uri;
import android.util.Log;
import com.vk.dto.music.MusicTrack;
import com.vk.dto.music.Playlist;
import org.json.JSONException;
import org.json.JSONObject;
import ru.vtosters.lite.music.cache.db.PlaylistCacheDb;
import ru.vtosters.lite.utils.music.MusicCacheStorageUtils;
import ru.vtosters.lite.utils.music.PlaylistUtils;
import java.util.List;
public class PlaylistCacheDbDelegate {
static synchronized private PlaylistCacheDb connectToDb(Context context) {
synchronized (PlaylistCacheDb.class) {
return new PlaylistCacheDb(context);
}
}
public static void addPlaylist(Context context, Playlist playlist) {
try (var db = connectToDb(context)) {
db.addPlaylist(playlist.a, playlist.b, playlist.C, playlist.g, playlist.B, generatePhotoJSON(playlist));
}
}
public static JSONObject generatePhotoJSON(Playlist playlist) {
if (PlaylistUtils.getThumb(playlist) != null) {
JSONObject photo = new JSONObject();
String playlistId = playlist.v1();
try {
photo.put("height", 300);
photo.put("width", 300);
addPhotoSizeToJSON(photo, playlistId, 600, "photo_600");
addPhotoSizeToJSON(photo, playlistId, 34, "photo_34");
addPhotoSizeToJSON(photo, playlistId, 1200, "photo_1200");
addPhotoSizeToJSON(photo, playlistId, 68, "photo_68");
addPhotoSizeToJSON(photo, playlistId, 135, "photo_135");
addPhotoSizeToJSON(photo, playlistId, 300, "photo_300");
addPhotoSizeToJSON(photo, playlistId, 270, "photo_270");
return photo;
} catch (JSONException e) {
throw new RuntimeException(e);
}
} else {
return null;
}
}
private static void addPhotoSizeToJSON(JSONObject photo, String playlistId, int size, String key) throws JSONException {
photo.put(key, Uri.fromFile(MusicCacheStorageUtils.getPlaylistThumb(playlistId, size)).toString());
Log.d("Playlist", "thumb link " + Uri.fromFile(MusicCacheStorageUtils.getPlaylistThumb(playlistId, size)).toString());
}
public static void deletePlaylist(Context context, String playlistId) {
try (var db = connectToDb(context)) {
db.deletePlaylistWithTracks(playlistId);
}
}
public static long getTracksCountInPlaylist(Context context, String playlistId) {
try (var db = connectToDb(context)) {
return db.getTracksCountInPlaylist(playlistId);
}
}
public static boolean isPlaylistsDbEmpty(Context context) {
try (var db = connectToDb(context)) {
return db.isPlaylistsDbEmpty();
}
}
public static void removeAllPlaylists(Context context) {
List<String> playlistIds = getAllPlaylistIds(context);
for (String playlistId : playlistIds) {
deletePlaylist(context, playlistId);
}
}
public static List<String> getAllPlaylistIds(Context context) {
try (var db = connectToDb(context)) {
return db.getAllPlaylistIds();
}
}
public static boolean isCachedPlaylist(Context context, String id) {
try (var db = connectToDb(context)) {
String[] parts = id.split("_");
int playlistId = Integer.parseInt(parts[1]);
int ownerId = Integer.parseInt(parts[0]);
return db.isCachedPlaylist(playlistId, ownerId);
}
}
public static long getPlaylistsCount(Context context) {
try (var db = connectToDb(context)) {
return db.getPlaylistsCount();
}
}
public static List<Playlist> getAllPlaylists(Context context) {
try (var db = connectToDb(context)) {
return db.getAllPlaylists();
}
}
public static Playlist getPlaylistById(Context context, String playlistId) {
try (var db = connectToDb(context)) {
String[] parts = playlistId.split("_");
int id = Integer.parseInt(parts[1]);
int ownerId = Integer.parseInt(parts[0]);
return db.getPlaylistById(id, ownerId);
}
}
public static void addTrackToPlaylist(Context context, String playlistId, String trackId) {
try (var db = connectToDb(context)) {
db.addTrackToPlaylist(playlistId, trackId);
}
}
public static void removeTrackFromPlaylist(Context context, String playlistId, String trackId) {
try (var db = connectToDb(context)) {
db.removeTrackFromPlaylist(playlistId, trackId);
}
}
public static List<MusicTrack> getTracksInPlaylist(Context context, String playlistId) {
try (var db = connectToDb(context)) {
return db.getTracksInPlaylist(playlistId);
}
}
public static boolean isPlaylistEmpty(Context context, String playlistId) {
try (var db = connectToDb(context)) {
return db.isPlaylistEmpty(playlistId);
}
}
public static void drop(Context context) {
context.deleteDatabase(PlaylistCacheDb.Constants.DB_NAME);
}
}
@@ -1,79 +1,92 @@
package ru.vtosters.lite.music.cache.helpers; package ru.vtosters.lite.music.cache.helpers;
import android.util.Log; import android.net.Uri;
import com.vk.dto.music.Playlist; import com.vk.dto.music.Playlist;
import com.vtosters.lite.R; import com.vtosters.lite.R;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import ru.vtosters.lite.music.cache.MusicCacheImpl; import ru.vtosters.lite.music.cache.MusicCacheImpl;
import ru.vtosters.lite.music.cache.delegate.PlaylistCacheDbDelegate;
import ru.vtosters.lite.utils.AccountManagerUtils;
import ru.vtosters.lite.utils.AndroidUtils; import ru.vtosters.lite.utils.AndroidUtils;
import ru.vtosters.lite.utils.NetworkUtils; import ru.vtosters.lite.utils.music.MusicCacheStorageUtils;
import ru.vtosters.lite.utils.music.PlaylistUtils;
import java.util.ArrayList;
import java.util.List;
import static ru.vtosters.lite.utils.AccountManagerUtils.getUserId;
public class PlaylistHelper { public class PlaylistHelper {
public static Playlist createCachedPlaylistMetadata() { public static Playlist createCachedPlaylistMetadata() {
try { try {
return new Playlist(getCachedSongsPlaylist()); return new Playlist(getPlaylist());
} catch (JSONException e) { } catch (JSONException e) {
e.printStackTrace();
return null; return null;
} }
} }
public static JSONArray addCachedPlaylists(JSONArray jsonArray) { public static Playlist createCachedPlaylistMetadata(String id) {
var track = TracklistHelper.getTracksOne(id).get(0);
var albumLink = track.I;
var thumb = albumLink.u1();
return createAlbum(
thumb != null ? thumb.h(600) : "",
albumLink.getTitle(),
track.C,
track.y1(),
albumLink.getId()
);
}
public static Playlist createAlbum(String photo, String title, String artist, String trackId, int albumId) {
try { try {
if (!PlaylistCacheDbDelegate.isPlaylistEmpty(AndroidUtils.getGlobalContext(), AccountManagerUtils.getUserId() + "_-1")) { var jPhoto = new JSONObject();
jsonArray.put(getCachedSongsPlaylist()); if (!photo.isEmpty())
} jPhoto.put("height", 600)
.put("width", 600)
if (MusicCacheImpl.hasPlaylist()) { .putOpt("photo_300", Uri.fromFile(MusicCacheStorageUtils.getTrackThumb(trackId, 300)).toString())
for (Playlist playlist : MusicCacheImpl.getPlaylists()) { .putOpt("photo_600", Uri.fromFile(MusicCacheStorageUtils.getTrackThumb(trackId, 600)).toString());
jsonArray.put(generatePlaylist(playlist.a, playlist.b, playlist.C, playlist.g, playlist.B, PlaylistUtils.getThumb(playlist))); var obj = new JSONObject()
.put("id", -2)
Log.d("PlaylistHelper", "Playlist cache added: " + playlist.a + " " + playlist.b + " " + playlist.C + " " + playlist.g + " " + playlist.B); .put("owner_id", albumId)
} .put("type", 1)
} .put("album_type", "main_only")
.put("title", title)
.put("description", albumId + "/ " + AndroidUtils.getString(R.string.cached_album_warning))
.put("main_artists", new JSONArray()
.put(new JSONObject().put("name", artist)
.put("id", "-1")
.put("domain", "-1")))
.putOpt("photo", jPhoto)
.put("count", 0);
return new Playlist(obj);
} catch (JSONException e) { } catch (JSONException e) {
e.fillInStackTrace(); e.printStackTrace();
return null;
} }
return jsonArray;
} }
public static JSONObject generatePlaylist(int id, int owner_id, boolean is_explicit, String title, String description, JSONObject photo) throws JSONException { public static List<Playlist> getAlbumPlaylists() {
JSONObject playlist = new JSONObject() List<Playlist> list = new ArrayList<>();
.put("id", id) for (var track : MusicCacheImpl.getPlaylist()) {
.put("owner_id", owner_id) var albumLink = track.I;
.put("type", 0) var thumb = albumLink.u1();
.put("album_type", "playlist") list.add(createAlbum(
.put("is_explicit", is_explicit) thumb != null ? thumb.h(600) : "",
.put("title", title) albumLink.getTitle(),
.put("description", description) track.C,
.put("access_key", "cache") track.y1(),
.put("genres", new JSONArray()) albumLink.getId()
.put("is_following", false) ));
.put("followers", 0)
.put("plays", 0)
.put("create_time", 0)
.put("update_time", 0)
.put("subtitle", "")
.put("meta", new JSONObject()
.put("view", "compact"))
.put("count", 0);
if (photo != null) {
playlist.put("photo", photo);
} }
return list;
return playlist;
} }
public static JSONObject getCachedSongsPlaylist() throws JSONException { public static JSONObject getPlaylist() throws JSONException {
return new JSONObject() return new JSONObject()
.put("id", -1) .put("id", -1)
.put("owner_id", AccountManagerUtils.getUserId()) .put("owner_id", getUserId())
.put("type", 0) .put("type", 0)
.put("album_type", "playlist") .put("album_type", "playlist")
.put("title", AndroidUtils.getString(R.string.cached_tracks_title)) .put("title", AndroidUtils.getString(R.string.cached_tracks_title))
@@ -101,20 +114,6 @@ public class PlaylistHelper {
.put("count", 0); .put("count", 0);
} }
public static JSONArray getCachedPlaylistsIds() {
JSONArray arr = new JSONArray();
if (NetworkUtils.isNetworkConnected() && !PlaylistCacheDbDelegate.isPlaylistEmpty(AndroidUtils.getGlobalContext(), AccountManagerUtils.getUserId() + "_-1")) {
arr.put(AccountManagerUtils.getUserId() + "_-1");
}
for (String playlist : PlaylistCacheDbDelegate.getAllPlaylistIds(AndroidUtils.getGlobalContext())) {
arr.put(playlist);
}
return arr;
}
public static JSONObject getCatalogPlaylist() throws JSONException { public static JSONObject getCatalogPlaylist() throws JSONException {
return new JSONObject() return new JSONObject()
.put("id", "cache") .put("id", "cache")
@@ -122,17 +121,18 @@ public class PlaylistHelper {
.put("layout", new JSONObject() .put("layout", new JSONObject()
.put("name", "large_slider") .put("name", "large_slider")
.put("is_editable", 0) .put("is_editable", 0)
.put("owner_id", AccountManagerUtils.getUserId())) .put("owner_id", getUserId()))
.put("playlists_ids", getCachedPlaylistsIds()); .put("playlists_ids", new JSONArray()
.put(getUserId() + "_-1"));
} }
public static JSONObject getCatalogHeader(String text) throws JSONException { public static JSONObject getCatalogHeader() throws JSONException {
return new JSONObject() return new JSONObject()
.put("id", "cache") .put("id", "cache")
.put("data_type", "none") .put("data_type", "none")
.put("layout", new JSONObject() .put("layout", new JSONObject()
.put("name", "header") .put("name", "header")
.put("title", text)); .put("title", AndroidUtils.getString(R.string.cached_tracks_title)));
} }
public static JSONObject getCatalogSeparator() throws JSONException { public static JSONObject getCatalogSeparator() throws JSONException {
@@ -8,8 +8,6 @@ import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import ru.vtosters.hooks.other.Preferences; import ru.vtosters.hooks.other.Preferences;
import ru.vtosters.lite.music.cache.MusicCacheImpl; import ru.vtosters.lite.music.cache.MusicCacheImpl;
import ru.vtosters.lite.music.cache.delegate.MusicCacheDbDelegate;
import ru.vtosters.lite.utils.AndroidUtils;
import ru.vtosters.lite.utils.music.MusicCacheStorageUtils; import ru.vtosters.lite.utils.music.MusicCacheStorageUtils;
import java.io.File; import java.io.File;
@@ -24,19 +22,24 @@ public class TracklistHelper {
} }
public static List<MusicTrack> getTracks() { public static List<MusicTrack> getTracks() {
List<MusicTrack> tracks = MusicCacheImpl.getAllOwnTracks(); List<MusicTrack> tracks = MusicCacheImpl.getAllTracks();
boolean doNotinvertOrder = Preferences.getBoolValue("invertCachedTracks", false); boolean doNotinvertOrder = Preferences.getBoolValue("invertCachedTracks", false);
if (doNotinvertOrder) { if (!doNotinvertOrder) {
Collections.reverse(tracks); Collections.reverse(tracks);
} }
return tracks; return tracks;
} }
public static MusicTrack getTrack(String id) {
return MusicCacheDbDelegate.getTrackById(AndroidUtils.getGlobalContext(), id); public static List<MusicTrack> getTracks(String id) {
return MusicCacheImpl.getAlbumById(id);
}
public static List<MusicTrack> getTracksOne(String id) {
return MusicCacheImpl.getFirstAlbumTrack(id);
} }
public static JSONArray tracksToIds(List<MusicTrack> tracks) { public static JSONArray tracksToIds(List<MusicTrack> tracks) {
@@ -1,12 +1,9 @@
package ru.vtosters.lite.music.callback; package ru.vtosters.lite.music.callback;
import android.util.Log; import android.util.Log;
import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat; import androidx.core.app.NotificationManagerCompat;
import com.vtosters.lite.R; import com.vtosters.lite.R;
import ru.vtosters.lite.downloaders.notifications.NotificationChannels; import ru.vtosters.lite.downloaders.notifications.NotificationChannels;
import ru.vtosters.lite.music.interfaces.Callback; import ru.vtosters.lite.music.interfaces.Callback;
import ru.vtosters.lite.utils.AndroidUtils; import ru.vtosters.lite.utils.AndroidUtils;
@@ -43,12 +40,12 @@ public class MusicCallbackBuilder {
} catch (UnsatisfiedLinkError e) { } catch (UnsatisfiedLinkError e) {
Log.e("AudioDownloader", "native libs"); Log.e("AudioDownloader", "native libs");
Log.e("AudioDownloader", e.getMessage()); Log.e("AudioDownloader", e.getMessage());
onFailure(e); onFailure();
} }
} }
@Override @Override
public void onFailure(Throwable e) { public void onFailure() {
notification.setContentText(AndroidUtils.getString(R.string.load_audio_error)).setProgress(0, 0, false); notification.setContentText(AndroidUtils.getString(R.string.load_audio_error)).setProgress(0, 0, false);
notificationManager.notify(notificationId, notification.build()); notificationManager.notify(notificationId, notification.build());
} }
@@ -77,11 +74,10 @@ public class MusicCallbackBuilder {
public void onSuccess() { public void onSuccess() {
notification.setContentText(AndroidUtils.getString(R.string.player_download_finished)); notification.setContentText(AndroidUtils.getString(R.string.player_download_finished));
notification.setProgress(0, 0, false); notification.setProgress(0, 0, false);
notification.setSmallIcon(android.R.drawable.stat_sys_download_done);
} }
@Override @Override
public void onFailure(Throwable e) { public void onFailure() {
notification.setContentText(AndroidUtils.getString(R.string.load_audio_error)).setProgress(0, 0, false); notification.setContentText(AndroidUtils.getString(R.string.load_audio_error)).setProgress(0, 0, false);
notificationManager.notify(notificationId, notification.build()); notificationManager.notify(notificationId, notification.build());
} }
@@ -6,13 +6,12 @@ import com.vk.dto.music.MusicTrack;
import com.vk.dto.music.Playlist; import com.vk.dto.music.Playlist;
import java8.util.concurrent.CompletableFuture; import java8.util.concurrent.CompletableFuture;
import okhttp3.Headers; import okhttp3.Headers;
import okhttp3.OkHttpClient;
import okhttp3.Request; import okhttp3.Request;
import okhttp3.Response;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import ru.vtosters.lite.utils.AccountManagerUtils; import ru.vtosters.lite.utils.AccountManagerUtils;
import ru.vtosters.lite.utils.AndroidUtils; import ru.vtosters.lite.utils.AndroidUtils;
import ru.vtosters.sponsorpost.utils.GzipDecompressor;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
@@ -20,7 +19,6 @@ import java.util.List;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static com.vk.core.network.Network.ClientType.CLIENT_API;
import static ru.vtosters.hooks.DateHook.getLocale; import static ru.vtosters.hooks.DateHook.getLocale;
import static ru.vtosters.lite.proxy.ProxyUtils.getApi; import static ru.vtosters.lite.proxy.ProxyUtils.getApi;
@@ -52,19 +50,17 @@ public class PlaylistConverter {
var request = new Request.a() var request = new Request.a()
.b(requestUrl) .b(requestUrl)
.a("Accept-Encoding", "gzip") .a(Headers.a("User-Agent", Network.l.c().a(), "Content-Type", "application/x-www-form-urlencoded; charset=utf-8")).a();
.a("User-Agent", Network.l.c().a())
.a("Content-Type", "application/x-www-form-urlencoded; charset=utf-8")
.a();
try { try {
var response = CompletableFuture.supplyAsync(() -> { var response = CompletableFuture.supplyAsync(() -> {
try (Response resp = Network.b(CLIENT_API).a(request).execute()) { try {
return GzipDecompressor.decompressResponse(resp); return new OkHttpClient().a(request).execute().a().g();
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
}).get(); }
).get();
var jsonObj = new JSONObject(response); var jsonObj = new JSONObject(response);
if (!jsonObj.has("response")) if (!jsonObj.has("response"))
return null; return null;
@@ -0,0 +1,45 @@
package ru.vtosters.lite.music.converter.ts;
import com.arthenica.ffmpegkit.FFmpegKit;
import com.vk.dto.music.Artist;
import com.vk.dto.music.MusicTrack;
import ru.vtosters.hooks.other.Preferences;
import ru.vtosters.lite.music.downloader.M3UDownloader;
import ru.vtosters.lite.utils.IOUtils;
import java.io.File;
import java.util.stream.Collectors;
public class FFMpeg {
/**
* @param in Merged .ts file via {@link ru.vtosters.lite.music.converter.ts.TSMerger}
* @param out Output .mp3 file
* @param track Track to be converted
* @return True if success, else false
*/
public static void convert(File in, String out, MusicTrack track) {
// convert .ts file to .mp3 file via ffmpeg
var cmd = " -y -i \"" + in.getAbsolutePath() + '"'
+ " -map 0 -dn"
+ " -loglevel error"
+ " -hide_banner"
+ " -write_id3v2 1"
+ setMetadata(track)
+ " -c copy \"" + out + '"';
var ses = FFmpegKit.execute(cmd);
if (ses.getReturnCode().isValueCancel()) throw new RuntimeException(ses.getLogsAsString());
}
private static String setMetadata(MusicTrack track) {
return Preferences.getBoolValue("setMetaData", true) ? " -metadata title=\"" + IOUtils.getValidFileName(M3UDownloader.getTitle(track)) + '"'
+ (track.L == null || track.I == null
? " -metadata artist=\"" + normalizeMetadata(track.C) + '"'
: " -metadata artist=\"" + normalizeMetadata(track.L.stream().map(Artist::w1).collect(Collectors.joining(", "))) + '"'
+ " -metadata album=\"" + normalizeMetadata(track.I.getTitle()) + '"') : "";
}
private static String normalizeMetadata(String in) {
return in.replaceAll("[\\\\/:*?\"<>|]", "");
}
}
@@ -1,79 +0,0 @@
package ru.vtosters.lite.music.converter.ts;
import android.util.Log;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
public class MpegDemuxer {
public static void convert(byte[] inBytes, String out) throws IOException {
try (InputStream inStream = new ByteArrayInputStream(inBytes)) {
convert(inStream, out);
}
}
public static void convert(InputStream in, String out) throws IOException {
int packetSize = 188;
int desiredPid = 256;
var buffer = new byte[packetSize];
var packet = ByteBuffer.wrap(buffer).order(ByteOrder.BIG_ENDIAN);
try (var fis = new BufferedInputStream(in); var fos = new BufferedOutputStream(new FileOutputStream(out))) {
while (fis.read(buffer) != -1) {
packet.clear();
int firstByte = packet.get() & 0xFF;
int secondByte = packet.get() & 0xFF;
int thirdByte = packet.get() & 0xFF;
int fourthByte = packet.get() & 0xFF;
int syncByte = firstByte;
int transportErrorIndicator = (secondByte & 0x80) >> 7;
int payloadUnitStartIndicator = (secondByte & 0x40) >> 6;
int pid = ((secondByte & 0x1F) << 8) | (thirdByte & 0xFF);
int adaptationFieldControl = (fourthByte & 0x30) >> 4;
if (syncByte != 0x47) {
Log.d("MpegDemuxer", "Invalid sync byte: 0x" + Integer.toHexString(syncByte));
continue;
}
if (transportErrorIndicator == 1) {
Log.d("MpegDemuxer", "Transport error indicator is set");
continue;
}
if (adaptationFieldControl == 2 || adaptationFieldControl == 3) {
int adaptationFieldLength = packet.get() & 0xFF;
packet.position(packet.position() + adaptationFieldLength);
}
if (pid == desiredPid) {
if (payloadUnitStartIndicator == 1) {
packet.position(packet.position() + 4);
int pesPacketLength = packet.getShort() & 0xFFFF;
if (pesPacketLength != 0) {
packet.position(packet.position() + 2);
int pesHeaderDataLength = packet.get() & 0xFF;
packet.position(packet.position() + pesHeaderDataLength);
} else {
Log.v("MpegDemuxer", "PES packet length is zero, that shouldn't happen in audio stream");
}
}
fos.write(packet.array(), packet.position(), packetSize - packet.position());
}
}
}
}
}
@@ -0,0 +1,70 @@
package ru.vtosters.lite.music.converter.ts;
import android.os.Build;
import ru.vtosters.lite.utils.IOUtils;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.Comparator;
public class TSMerger {
/**
* Merge multiple .ts files into one .ts file
*
* @param in Directory with .ts files
*/
public static void merge(File in, File out) {
File[] tsesList = in.listFiles();
if (tsesList == null) {
return;
}
ByteArrayOutputStream mergedByteArrayStream = new ByteArrayOutputStream();
sortTsesList(tsesList);
for (File file : tsesList) {
if (file.getName().endsWith(".ts")) {
try {
mergedByteArrayStream.write(IOUtils.readFully(file));
} catch (IOException e) {
return;
}
}
}
writeOutputFile(out, mergedByteArrayStream);
}
private static void sortTsesList(File[] tsesList) {
Arrays.sort(tsesList, Comparator.comparingInt(TSMerger::getFileIndex));
}
private static int getFileIndex(File file) {
String fileName = file.getName();
int dotIndex = fileName.indexOf('.', 1);
return Integer.parseInt(fileName.substring(0, dotIndex));
}
private static void writeOutputFile(File out, ByteArrayOutputStream mergedByteArrayStream) {
byte[] bytes = mergedByteArrayStream.toByteArray();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
try {
Files.write(out.toPath(), bytes);
} catch (IOException ignored) {
// ignored
}
} else {
try (FileOutputStream fos = new FileOutputStream(out)) {
fos.write(bytes);
} catch (IOException ignored) {
// ignored
}
}
}
}
@@ -6,12 +6,11 @@ import com.vk.core.util.DeviceIdProvider;
import com.vk.dto.music.MusicTrack; import com.vk.dto.music.MusicTrack;
import java8.util.concurrent.CompletableFuture; import java8.util.concurrent.CompletableFuture;
import okhttp3.Headers; import okhttp3.Headers;
import okhttp3.OkHttpClient;
import okhttp3.Request; import okhttp3.Request;
import okhttp3.Response;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import ru.vtosters.lite.utils.AndroidUtils; import ru.vtosters.lite.utils.AndroidUtils;
import ru.vtosters.sponsorpost.utils.GzipDecompressor;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
@@ -19,7 +18,6 @@ import java.util.List;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static com.vk.core.network.Network.ClientType.CLIENT_API;
import static ru.vtosters.hooks.DateHook.getLocale; import static ru.vtosters.hooks.DateHook.getLocale;
import static ru.vtosters.lite.proxy.ProxyUtils.getApi; import static ru.vtosters.lite.proxy.ProxyUtils.getApi;
import static ru.vtosters.lite.utils.AccountManagerUtils.getUserId; import static ru.vtosters.lite.utils.AccountManagerUtils.getUserId;
@@ -46,14 +44,12 @@ public class AudioGet {
var request = new Request.a() var request = new Request.a()
.b(requestUrl) .b(requestUrl)
.a("Accept-Encoding", "gzip") .a(Headers.a("User-Agent", Network.l.c().a(), "Content-Type", "application/x-www-form-urlencoded; charset=utf-8")).a();
.a("User-Agent", Network.l.c().a())
.a("Content-Type", "application/x-www-form-urlencoded; charset=utf-8")
.a();
try { try {
var response = CompletableFuture.supplyAsync(() -> { var response = CompletableFuture.supplyAsync(() -> {
try (Response resp = Network.b(CLIENT_API).a(request).execute()) { try {
return GzipDecompressor.decompressResponse(resp); return new OkHttpClient().a(request).execute().a().g();
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@@ -1,65 +0,0 @@
package ru.vtosters.lite.music.downloader;
import com.vk.dto.music.MusicTrack;
import com.vk.dto.music.Playlist;
import ru.vtosters.lite.music.cache.MusicCacheImpl;
import ru.vtosters.lite.music.cache.delegate.PlaylistCacheDbDelegate;
import ru.vtosters.lite.music.interfaces.Callback;
import ru.vtosters.lite.music.interfaces.ITrackDownloader;
import ru.vtosters.lite.utils.AndroidUtils;
public final class CachedDownloader implements ITrackDownloader {
private final ITrackDownloader origin;
public CachedDownloader(ITrackDownloader origin) {
this.origin = origin;
}
@Override
public void download(MusicTrack track, Callback callback, Playlist playlist) {
origin.download(track, new Callback() {
@Override
public void onProgress(int progress) {
callback.onProgress(progress);
}
@Override
public void onSuccess() {
new ThumbnailTrackDownloader().download(track, new Callback() {
@Override public void onProgress(int progress) {
}
@Override public void onSuccess() {
}
@Override
public void onFailure(Throwable e) {
callback.onFailure(e);
}
@Override public void onSizeReceived(long size, long header) {
}
}, null);
PlaylistCacheDbDelegate.addTrackToPlaylist(AndroidUtils.getGlobalContext(), playlist.v1(), track.y1());
MusicCacheImpl.addTrack(track);
callback.onSuccess();
}
@Override
public void onFailure(Throwable e) {
callback.onFailure(e);
}
@Override
public void onSizeReceived(long size, long header) {
callback.onSizeReceived(size, header);
}
}, playlist);
}
}
@@ -1,56 +0,0 @@
package ru.vtosters.lite.music.downloader;
import android.util.Log;
import com.mpatric.mp3agic.ID3v23Tag;
import com.mpatric.mp3agic.InvalidDataException;
import com.mpatric.mp3agic.Mp3File;
import com.mpatric.mp3agic.NotSupportedException;
import com.mpatric.mp3agic.UnsupportedTagException;
import com.vk.dto.music.MusicTrack;
import com.vk.dto.music.Artist;
import java.io.File;
import java.io.IOException;
import java.util.stream.Collectors;
import ru.vtosters.hooks.other.Preferences;
public class ID3Tagger {
public static void tag(File file, MusicTrack track) {
try {
// https://github.com/mpatric/mp3agic/issues/69#issuecomment-57897874
// TODO: get rid of file duplication
var oldFile = new File(file.getAbsolutePath().replace(".mp3", "_old.mp3"));
if (!file.renameTo(oldFile)) {
Log.e("ID3Tagger", "Failed to rename file");
return;
}
var mp3File = new Mp3File(oldFile);
if (mp3File.hasId3v2Tag()) {
var id3v2Tag = new ID3v23Tag();
if (track.L == null || track.I == null) {
id3v2Tag.setArtist(track.C);
} else {
var artists = track.L.stream().map(Artist::w1).collect(Collectors.joining(Preferences.metadataSeparator()));
id3v2Tag.setArtist(artists);
id3v2Tag.setAlbum(track.I.getTitle());
}
id3v2Tag.setTitle(Mp3Downloader.getTitle(track));
mp3File.setId3v2Tag(id3v2Tag);
mp3File.save(file.getAbsolutePath());
if (!oldFile.delete()) {
Log.e("ID3Tagger", "Failed to delete file");
}
}
} catch (IOException | UnsupportedTagException | InvalidDataException |
NotSupportedException e) {
Log.e("ID3Tagger", "Failed to tag file", e);
}
}
}
@@ -0,0 +1,181 @@
package ru.vtosters.lite.music.downloader;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
import com.google.android.exoplayer2.source.hls.playlist.e;
import com.google.android.exoplayer2.source.hls.playlist.f;
import com.google.android.exoplayer2.source.hls.playlist.f.a;
import com.vk.dto.music.MusicTrack;
import java8.util.concurrent.CompletableFuture;
import java8.util.concurrent.CompletionException;
import ru.vtosters.lite.music.cache.MusicCacheImpl;
import ru.vtosters.lite.music.converter.ts.FFMpeg;
import ru.vtosters.lite.music.converter.ts.TSMerger;
import ru.vtosters.lite.music.interfaces.Callback;
import ru.vtosters.lite.music.interfaces.ITrackDownloader;
import ru.vtosters.lite.utils.AndroidUtils;
import ru.vtosters.lite.utils.IOUtils;
import ru.vtosters.lite.utils.music.MusicCacheStorageUtils;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
public class M3UDownloader
implements ITrackDownloader {
public static M3UDownloader getInstance() {
return Holder.INSTANCE;
}
static void parse(
File outDir,
Callback callback,
MusicTrack track,
boolean cache)
throws IOException {
var parser = new com.google.android.exoplayer2.source.hls.playlist.h(e.a(track.D));
var baseUri = track.D.substring(0, track.D.lastIndexOf("/") + 1);
var segments = ((f) parser.a(Uri.parse(baseUri), IOUtils.openStream(track.D))).o;
var progress = new AtomicInteger();
callback.onProgress(5);
var resultTs = getResultTsFile(track);
var resultMp3 = getResultMp3File(outDir, cache, track);
try {
CompletableFuture.allOf(createFutures(baseUri, segments, progress, callback, track))
.thenRun(() -> {
if (cache) {
try {
ThumbnailDownloader.downloadThumbnails(track);
} catch (IOException e) {
throw new RuntimeException("Failed to download thumbs", e);
}
}
})
.thenRun(() -> {
try {
TSMerger.merge(getTsesDir(track), resultTs);
} catch (Throwable e) {
throw new RuntimeException("Failed to merge ts files", e);
}
})
.thenRun(() -> {
try {
FFMpeg.convert(resultTs, resultMp3.getAbsolutePath(), track);
} catch (Throwable e) {
throw new RuntimeException("FFmpeg error", e);
}
})
.thenRun(() -> callback.onProgress(10 + Math.round(80.0f * progress.addAndGet(1) / segments.size())))
.thenRun(() -> {
if (cache) {
MusicCacheImpl.addTrack(track);
}
})
.whenComplete((unused, tr) -> {
if (tr != null) {
IOUtils.deleteRecursive(MusicCacheStorageUtils.getTrackDirById(track.y1()));
callback.onFailure();
} else {
callback.onSuccess();
}
IOUtils.deleteRecursive(getTsesDir(track));
})
.join();
} catch (CompletionException e) {
Log.e("M3UDownloader", "Failed to download track", e.getCause());
}
}
@SuppressWarnings("rawtypes")
static CompletableFuture[] createFutures(
String baseUri,
List<a> segments,
AtomicInteger progress,
Callback callback,
MusicTrack track) {
var futures = new CompletableFuture[segments.size()];
for (var i = 0; i < segments.size(); ++i) {
var segment = segments.get(i);
var filename = i + ".ts";
futures[i] = CompletableFuture.runAsync(() -> processSegment(baseUri, segment, filename, progress, segments.size(), callback, track));
}
return futures;
}
private static void processSegment(
String baseUri,
a segment,
String filename,
AtomicInteger progress,
int size,
Callback callback,
MusicTrack track) {
try {
var buff = IOUtils.readFully(IOUtils.openStream(baseUri + segment.a/*url*/));
if (!TextUtils.isEmpty(segment.g)) {
var cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
var cipherKey = new SecretKeySpec(IOUtils.readFully(IOUtils.openStream(segment.g)), "AES");
var cipherBytes = segment.h.getBytes();
//if IV doesn't pass, it must be created manually and filled with zeros
if (cipherBytes.length != 16) Arrays.fill((cipherBytes = new byte[16]), (byte) 0x0);
var cipherIv = new IvParameterSpec(cipherBytes);
cipher.init(Cipher.DECRYPT_MODE, cipherKey, cipherIv);
buff = cipher.doFinal(buff);
}
IOUtils.writeToFile(new File(getTsesDir(track), filename), buff);
callback.onProgress(10 + Math.round(80.0f * progress.addAndGet(1) / size));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static String getTitle(MusicTrack track) {
return track.f + (!TextUtils.isEmpty(track.g) ? +'(' + track.g + ')' : "");
}
static File getTsesDir(MusicTrack track) {
var filesDir = AndroidUtils.getGlobalContext().getFilesDir();
var tsesDir = new File(filesDir, String.valueOf(track.d));
tsesDir.mkdirs();
return tsesDir;
}
static File getResultTsFile(MusicTrack track) {
return new File(getTsesDir(track), "result.ts");
}
static File getResultMp3File(File outDir, boolean cache, MusicTrack track) {
return new File(outDir, IOUtils.getValidFileName((cache ? "track" : track.C + " - " + getTitle(track)) + ".mp3"));
}
@Override
public void downloadTrack(
MusicTrack track,
File outDir,
Callback callback,
boolean cache) {
if (track.D.isEmpty()) {
Log.d("TrackDownloader", "link error: " + track.y1() + ", title: " + M3UDownloader.getTitle(track));
callback.onFailure();
return;
}
try {
parse(outDir, callback, track, cache);
} catch (IOException e) {
callback.onFailure();
Log.e("M3UDownloader", e + "");
throw new RuntimeException(e);
}
}
// Initialization-on-demand
private static class Holder {
private static final M3UDownloader INSTANCE = new M3UDownloader();
}
}
@@ -0,0 +1,76 @@
//package ru.vtosters.lite.music;
//
//import android.text.TextUtils;
//import android.util.Log;
//import android.widget.Toast;
//
//import com.vk.dto.music.MusicTrack;
//
//import org.json.JSONException;
//import org.json.JSONObject;
//
//import java.io.File;
//import java.io.IOException;
//
//import okhttp3.Call;
//import okhttp3.Callback;
//import okhttp3.OkHttpClient;
//import okhttp3.Request;
//import okhttp3.Response;
//import ru.vtosters.lite.downloaders.AudioDownloader;
//import ru.vtosters.lite.music.interfaces.ITrackDownloader;
//import ru.vtosters.lite.utils.AndroidUtils;
//
//public class MP3Downloader implements ITrackDownloader {
// private static final String URL_FORMAT = "https://api.vk.com/method/audio.getById?v=5.131&access_token=%s&audios=%s&copyright=";
//
// private static final OkHttpClient client = new OkHttpClient();
//
// public static void downloadMP3(MusicTrack track) {
// var token = AndroidUtils.getPreferences().getString("vk_admin_token", "");
// if (token.isEmpty()) {
// Toast.makeText(AndroidUtils.getGlobalContext(), "Обновите токен VK Admin в настройках", Toast.LENGTH_SHORT).show();
// return;
// }
//
// var request = new Request.a()
// .b(String.format(URL_FORMAT, token, track.y1()))
// .a();
// client.a(request).a(new Callback() {
// @Override
// public void a(Call call, IOException e) {
// Log.d("MP3Downloader", e + "");
// }
//
// @Override
// public void a(Call call, Response response) throws IOException {
// var payload = response.a().g();
// Log.d("MP3Downloader", payload);
// try {
// JSONObject jsonObj = new JSONObject(payload);
// if (!jsonObj.has("response"))
// return;
// JSONObject responseNode = new JSONObject(payload).getJSONArray("response")
// .getJSONObject(0);
// if (!responseNode.has("url"))
// return;
// var url = responseNode.optString("url");
// Log.d("MP3Downloader", url);
// if (TextUtils.isEmpty(url))
// return;
// if (VKM3UParser.isM3U8(url)) {
// AudioDownloader.downloadM3U8(track);
// return;
// }
// } catch (JSONException e) {
// Log.d("MP3Downloader", e + "");
// }
// }
// });
// }
//
// @Override
// public void downloadTrack(String url, File outDir, ru.vtosters.lite.music.interfaces.Callback callback) {
//
// }
//}
@@ -1,111 +0,0 @@
package ru.vtosters.lite.music.downloader;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
import com.google.android.exoplayer2.source.hls.playlist.e;
import com.google.android.exoplayer2.source.hls.playlist.f;
import com.google.android.exoplayer2.source.hls.playlist.f.a;
import com.vk.dto.music.MusicTrack;
import com.vk.dto.music.Playlist;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.security.GeneralSecurityException;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import ru.vtosters.hooks.other.Preferences;
import ru.vtosters.lite.music.converter.ts.MpegDemuxer;
import ru.vtosters.lite.music.interfaces.Callback;
import ru.vtosters.lite.music.interfaces.ITrackDownloader;
import ru.vtosters.lite.utils.IOUtils;
public final class Mp3Downloader implements ITrackDownloader {
private final File outputFile;
public Mp3Downloader(File outputFile) {
this.outputFile = outputFile;
}
@Override
public void download(MusicTrack track, Callback callback, Playlist playlist) {
String uri = track.D;
if (Objects.requireNonNull(uri).isEmpty()) {
String msg = "link error: " + track.y1() + ", title: " + Mp3Downloader.getTitle(track);
Log.d("Mp3Downloader", msg);
throw new RuntimeException(msg);
}
if (Objects.requireNonNull(uri).contains("master.m3u8?siren=1")) {
try {
String content = IOUtils.readAllLines(new URL(uri).openStream());
String replacement = content.split("\n")[2].trim();
uri = uri.replace("master.m3u8?siren=1", replacement);
} catch (IOException e) {
callback.onFailure(e);
return;
}
}
var tsParser = new com.google.android.exoplayer2.source.hls.playlist.h(e.a(uri));
var baseUri = uri.substring(0, uri.lastIndexOf("/") + 1);
try {
var segments = ((f) tsParser.a(Uri.parse(baseUri), IOUtils.openStream(uri))).o;
byte[] buff = getMergedTs(baseUri, segments, callback);
MpegDemuxer.convert(buff, outputFile.getAbsolutePath());
if (Preferences.getBoolValue("setMetaData", false)) {
ID3Tagger.tag(outputFile, track);
}
callback.onSuccess();
} catch (IOException | GeneralSecurityException e) {
callback.onFailure(e);
}
}
private static byte[] getMergedTs(String baseUri, List<a> segments, Callback callback) throws IOException, GeneralSecurityException {
byte[] total = new byte[0];
int size = segments.size();
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
int progress = 0;
for (var segment : segments) {
var buff = IOUtils.readFully(IOUtils.openStream(baseUri + segment.a/*url*/));
if (!TextUtils.isEmpty(segment.g)) {
var cipherKey = new SecretKeySpec(IOUtils.readFully(IOUtils.openStream(segment.g)), "AES");
var cipherBytes = segment.h.getBytes();
//if IV doesn't pass, it must be created manually
if (cipherBytes.length != 16) cipherBytes = new byte[16];
var cipherIv = new IvParameterSpec(cipherBytes);
cipher.init(Cipher.DECRYPT_MODE, cipherKey, cipherIv);
buff = cipher.doFinal(buff);
}
// copy on write
final int len = total.length, n = buff.length;
byte[] newBytes = Arrays.copyOf(total, len + n);
System.arraycopy(buff, 0, newBytes, len, n);
total = newBytes;
callback.onProgress(10 + Math.round(80.0F * (++progress) / size));
}
return total;
}
static String getTitle(MusicTrack track) {
return track.f + (!TextUtils.isEmpty(track.g) ? '(' + track.g + ')' : "");
}
}
@@ -1,67 +1,63 @@
package ru.vtosters.lite.music.downloader; package ru.vtosters.lite.music.downloader;
import android.util.Log; import android.util.Log;
import com.vk.dto.music.MusicTrack; import com.vk.dto.music.MusicTrack;
import com.vk.dto.music.Playlist;
import ru.vtosters.lite.music.interfaces.Callback; import ru.vtosters.lite.music.interfaces.Callback;
import java.io.File; import java.io.File;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java8.util.concurrent.CompletableFuture;
public class PlaylistDownloader { public class PlaylistDownloader {
public static void downloadPlaylist(List<MusicTrack> playlist, String playlistName, String path, Callback callback) { public static void downloadPlaylist(List<MusicTrack> playlist, String playlistName, String path, Callback callback) {
var outDir = new File(path); var outDir = new File(path);
if (!outDir.exists()) if (!outDir.exists())
if (outDir.mkdirs()) Log.v("PlaylistDownloader", "Directory created"); if (outDir.mkdirs()) Log.v("PlaylistDownloader", "Directory created");
else Log.e("PlaylistDownloader", "Directory creation failed"); else Log.e("PlaylistDownloader", "Directory creation failed");
Callback delegate = new ProgressCallback(callback); var downloadedTracks = new AtomicInteger();
CompletableFuture.allOf(playlist for (var musicTrack : playlist)
.stream() TrackDownloader.downloadTrack(musicTrack, path, new Callback() {
.map(x -> { @Override
Callback.CompletableFutureCallback d = new public void onProgress(int progress) {
Callback.CompletableFutureCallback(delegate); }
TrackDownloader.downloadTrack(x, path, d);
return d; @Override
}) public void onSuccess() {
.toArray(CompletableFuture[]::new)); callback.onProgress(downloadedTracks.incrementAndGet());
}
@Override
public void onFailure() {
callback.onFailure();
}
@Override
public void onSizeReceived(long size, long header) {
}
});
} }
public static void cachePlaylist(List<MusicTrack> playlist, Callback callback, Playlist playlistId) { public static void cachePlaylist(List<MusicTrack> playlist, Callback callback) {
Callback delegate = new ProgressCallback(callback); var downloadedTracks = new AtomicInteger();
CompletableFuture.allOf(playlist for (var musicTrack : playlist)
.stream() TrackDownloader.cacheTrack(musicTrack, new Callback() {
.map(x -> { @Override
Callback.CompletableFutureCallback d = new Callback.CompletableFutureCallback(delegate); public void onProgress(int progress) {
TrackDownloader.cacheTrack(x, d, playlistId); }
return d;
})
.toArray(CompletableFuture[]::new));
}
public static final class ProgressCallback implements Callback { @Override
private final AtomicInteger progress = new AtomicInteger(); public void onSuccess() {
private final Callback origin; callback.onProgress(downloadedTracks.incrementAndGet());
}
public ProgressCallback(Callback origin) { @Override
this.origin = origin; public void onFailure() {
} callback.onFailure();
}
@Override public void onProgress(int progress) {} @Override
public void onSizeReceived(long size, long header) {
@Override }
public void onSuccess() { });
origin.onProgress(progress.incrementAndGet());
}
@Override
public void onFailure(Throwable e) {
origin.onFailure(e);
}
@Override public void onSizeReceived(long size, long header) {}
} }
} }
@@ -0,0 +1,43 @@
package ru.vtosters.lite.music.downloader;
import bruhcollective.itaysonlab.libvkx.client.LibVKXClient;
import com.vk.dto.music.MusicTrack;
import org.json.JSONArray;
import org.json.JSONObject;
import ru.vtosters.lite.utils.IOUtils;
import ru.vtosters.lite.utils.music.MusicCacheStorageUtils;
import java.io.IOException;
import java.net.URL;
public class ThumbnailDownloader {
public static void downloadThumbnails(MusicTrack track) throws IOException {
JSONObject json = track.J();
JSONObject album = json.optJSONObject("album");
if (album == null) return;
JSONObject thumb = album.optJSONObject("thumb");
if (thumb == null) return;
JSONArray sizes = thumb.optJSONArray("sizes");
String trackId = LibVKXClient.asId(track);
if (sizes == null) {
JSONArray names = thumb.names();
if (names == null) return;
for (int i = 0; i < names.length(); ++i) {
String name = names.optString(i);
downloadThumbnail(thumb.optString(name), Integer.parseInt(name.substring(6)), trackId);
}
} else {
for (int i = 0; i < sizes.length(); ++i) {
JSONObject size = sizes.optJSONObject(i);
int width = size.optInt("width");
String src = size.optString("src");
downloadThumbnail(src, width, trackId);
}
}
}
private static void downloadThumbnail(String url, int res, String trackId) throws IOException {
IOUtils.writeToFile(MusicCacheStorageUtils.getTrackThumb(trackId, res), IOUtils.readFully(new URL(url).openStream()));
}
}
@@ -1,57 +0,0 @@
package ru.vtosters.lite.music.downloader;
import android.util.Log;
import com.vk.dto.music.MusicTrack;
import com.vk.dto.music.Playlist;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import ru.vtosters.lite.music.interfaces.Callback;
import ru.vtosters.lite.music.interfaces.ITrackDownloader;
import ru.vtosters.lite.utils.IOUtils;
import ru.vtosters.lite.utils.music.MusicCacheStorageUtils;
import ru.vtosters.lite.utils.music.PlaylistUtils;
import java.io.IOException;
import java.net.URL;
import java.util.List;
public class ThumbnailPlaylistDownloader implements ITrackDownloader {
@Override
public void download(MusicTrack track, Callback callback, Playlist playlist) {
JSONObject json = PlaylistUtils.getThumb(playlist); // get thumbnail photo
if (json == null) {
callback.onFailure(new IOException("No thumbnail found"));
return;
}
try {
List<String> sizes = List.of("600", "1200", "68", "135", "300", "270"); // define the order of sizes to download
JSONArray sizesArray = json.getJSONArray("sizes");
for (String size : sizes) {
for (int i = 0; i < sizesArray.length(); i++) {
JSONObject sizeObj = sizesArray.getJSONObject(i);
if (sizeObj.getInt("width") == Integer.parseInt(size)) {
String url = sizeObj.getString("src");
if (url.isEmpty()) continue;
Log.d("Playlist", "downloading thumb " + url);
downloadThumbnailPlaylist(url, Integer.parseInt(size), playlist.v1());
}
}
}
callback.onSuccess();
} catch (IOException | JSONException e) {
callback.onFailure(e);
throw new RuntimeException(e);
}
}
private static void downloadThumbnailPlaylist(String url, int res, String playlistId) throws IOException {
IOUtils.writeToFile(MusicCacheStorageUtils.getPlaylistThumb(playlistId, res), IOUtils.readFully(new URL(url).openStream()));
}
}
@@ -1,54 +0,0 @@
package ru.vtosters.lite.music.downloader;
import com.vk.dto.music.MusicTrack;
import com.vk.dto.music.Playlist;
import org.json.JSONArray;
import org.json.JSONObject;
import java.io.IOException;
import java.net.URL;
import bruhcollective.itaysonlab.libvkx.client.LibVKXClient;
import ru.vtosters.lite.music.interfaces.Callback;
import ru.vtosters.lite.music.interfaces.ITrackDownloader;
import ru.vtosters.lite.utils.IOUtils;
import ru.vtosters.lite.utils.music.MusicCacheStorageUtils;
public final class ThumbnailTrackDownloader implements ITrackDownloader {
@Override
public void download(MusicTrack track, Callback callback, Playlist playlist) {
JSONObject json = track.J();
JSONObject album = json.optJSONObject("album");
if (album == null) return;
JSONObject thumb = album.optJSONObject("thumb");
if (thumb == null) return;
JSONArray sizes = thumb.optJSONArray("sizes");
String trackId = LibVKXClient.asId(track);
try {
if (sizes == null) {
JSONArray names = thumb.names();
if (names == null) return;
for (int i = 0; i < names.length(); ++i) {
String name = names.optString(i);
downloadThumbnail(thumb.optString(name), Integer.parseInt(name.substring(6)), trackId);
}
} else {
for (int i = 0; i < sizes.length(); ++i) {
JSONObject size = sizes.optJSONObject(i);
int width = size.optInt("width");
String src = size.optString("src");
downloadThumbnail(src, width, trackId);
}
}
callback.onSuccess();
} catch (IOException e) {
callback.onFailure(e);
throw new RuntimeException(e);
}
}
private static void downloadThumbnail(String url, int res, String trackId) throws IOException {
IOUtils.writeToFile(MusicCacheStorageUtils.getTrackThumb(trackId, res), IOUtils.readFully(new URL(url).openStream()));
}
}
@@ -1,45 +1,51 @@
package ru.vtosters.lite.music.downloader; package ru.vtosters.lite.music.downloader;
import android.util.Log; import android.util.Log;
import com.vk.dto.music.MusicTrack;
import java.io.File;
import bruhcollective.itaysonlab.libvkx.client.LibVKXClient; import bruhcollective.itaysonlab.libvkx.client.LibVKXClient;
import com.vk.dto.music.Playlist; import com.vk.dto.music.MusicTrack;
import ru.vtosters.lite.concurrent.VTExecutors; import ru.vtosters.lite.concurrent.VTExecutors;
import ru.vtosters.lite.music.cache.MusicCacheImpl; import ru.vtosters.lite.music.cache.MusicCacheImpl;
import ru.vtosters.lite.music.interfaces.Callback; import ru.vtosters.lite.music.interfaces.Callback;
import ru.vtosters.lite.utils.IOUtils; import ru.vtosters.lite.music.interfaces.ITrackDownloader;
import ru.vtosters.lite.utils.music.MusicCacheStorageUtils; import ru.vtosters.lite.utils.music.MusicCacheStorageUtils;
import ru.vtosters.lite.utils.music.MusicTrackUtils;
public final class TrackDownloader { import java.io.File;
private TrackDownloader() { }
public class TrackDownloader {
public static void downloadTrack(MusicTrack track, String path, Callback callback) { public static void downloadTrack(MusicTrack track, String path, Callback callback) {
File outDir = new File(path); if (track.D.isEmpty()) {
if (!outDir.exists()) { Log.d("TrackDownloader", "link error: " + track.y1() + ", title: " + M3UDownloader.getTitle(track));
if (outDir.mkdir()) { callback.onFailure();
Log.v("TrackDownloader", "Directory created");
} else {
Log.e("TrackDownloader", "Directory creation failed");
}
}
File outputFile = new File(outDir, IOUtils.getValidFileName(MusicTrackUtils.getArtists(track) + " - " + Mp3Downloader.getTitle(track)) + ".mp3");
VTExecutors.getMusicDownloadExecutor().submit(() -> new Mp3Downloader(outputFile).download(track, callback, null));
}
public static void cacheTrack(MusicTrack track, Callback callback, Playlist playlist) {
if (MusicCacheImpl.isCachedTrack(LibVKXClient.asId(track))) {
return; return;
} }
File outputFile = MusicCacheStorageUtils.getTrackFile(LibVKXClient.asId(track)); download(track, path, callback, M3UDownloader.getInstance(), false);
}
VTExecutors.getMusicDownloadExecutor().submit(() -> new CachedDownloader(new Mp3Downloader(outputFile)).download(track, callback, playlist)); public static void cacheTrack(MusicTrack track, Callback callback) {
if (MusicCacheImpl.isCachedTrack(LibVKXClient.asId(track))) {
callback.onSuccess();
return;
} else if (track.D.isEmpty()) {
Log.d("TrackDownloader", "link error: " + track.y1() + ", title: " + M3UDownloader.getTitle(track));
callback.onFailure();
return;
}
var path = MusicCacheStorageUtils.getTrackDirById(LibVKXClient.asId(track)).getAbsolutePath();
download(track, path, callback, M3UDownloader.getInstance(), true);
}
private static void download(MusicTrack track, String path, Callback callback, ITrackDownloader downloader, boolean cache) {
File outDir = new File(path);
if (!outDir.exists())
if (outDir.mkdir())
Log.v("TrackDownloader", "Directory created");
else
Log.e("TrackDownloader", "Directory creation failed");
VTExecutors.getMusicDownloadExecutor().execute(
() -> downloader.downloadTrack(track, outDir, callback, cache)
);
} }
} }
@@ -1,46 +1,11 @@
package ru.vtosters.lite.music.interfaces; package ru.vtosters.lite.music.interfaces;
import java8.util.concurrent.CompletableFuture;
public interface Callback { public interface Callback {
void onProgress(int progress); void onProgress(int progress);
void onSuccess(); void onSuccess();
void onFailure(Throwable e); void onFailure();
void onSizeReceived(long size, long header); void onSizeReceived(long size, long header);
class CompletableFutureCallback
extends CompletableFuture<Void>
implements Callback {
private final Callback origin;
public CompletableFutureCallback(Callback origin) {
this.origin = origin;
}
@Override
public void onProgress(int progress) {
origin.onProgress(progress);
}
@Override
public void onSuccess() {
complete(null);
origin.onSuccess();
}
@Override
public void onFailure(Throwable e) {
completeExceptionally(e);
origin.onFailure(e);
}
@Override
public void onSizeReceived(long size, long header) {
origin.onSizeReceived(size, header);
}
}
} }
@@ -1,7 +1,8 @@
package ru.vtosters.lite.music.interfaces; package ru.vtosters.lite.music.interfaces;
import com.vk.dto.music.MusicTrack; import com.vk.dto.music.MusicTrack;
import com.vk.dto.music.Playlist;
import java.io.File;
/** /**
* Interface for track downloaders * Interface for track downloaders
@@ -11,8 +12,8 @@ public interface ITrackDownloader {
* Download track * Download track
* *
* @param track Music track * @param track Music track
* @param outDir Directory where to download the track
* @param callback Callback to call when download status changes * @param callback Callback to call when download status changes
* @param playlist
*/ */
void download(MusicTrack track, Callback callback, Playlist playlist); void downloadTrack(MusicTrack track, File outDir, Callback callback, boolean cache);
} }
@@ -0,0 +1,38 @@
package ru.vtosters.lite.proxy;
import android.util.Log;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import ru.vtosters.lite.di.singleton.VtOkHttpClient;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
public class GithubList {
private static final OkHttpClient mClient = VtOkHttpClient.getInstance();
public static ArrayList<String> getProxy(String url, String proxyname) throws IOException {
Request link = new Request.a()
.b(url)
.a();
try {
var response = mClient.a(link).execute().a().g();
if (!response.contains(":")) {
Log.d(proxyname, "No proxy found");
return null;
}
Log.d(proxyname, response);
return new ArrayList<>(Arrays.asList(response.split("\n")));
} catch (Exception e) {
Log.d(proxyname, "Failed to fetch data, link: " + link);
return null;
}
}
}
@@ -5,7 +5,6 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.util.ArrayMap; import android.util.ArrayMap;
import android.util.Log; import android.util.Log;
import com.vk.core.network.Network;
import ru.vtosters.hooks.other.Preferences; import ru.vtosters.hooks.other.Preferences;
import ru.vtosters.lite.proxy.http.CustomHttp; import ru.vtosters.lite.proxy.http.CustomHttp;
import ru.vtosters.lite.proxy.https.CustomHttps; import ru.vtosters.lite.proxy.https.CustomHttps;
@@ -63,15 +62,16 @@ public class ProxyUtils {
} }
public static Boolean isAnyProxyEnabled() { public static Boolean isAnyProxyEnabled() {
return (isApiProxyEnabled() || isVikaProxyEnabled()) && !isVKProxyEnabled(); return isApiProxyEnabled() || isVikaProxyEnabled();
} }
public static Boolean isZaboronaEnabled() { public static Boolean isZaboronaEnabled() {
return Preferences.getString("proxy").equals("zaborona"); return Preferences.getString("proxy").equals("zaborona");
} }
public static Boolean isVKProxyEnabled() { public static Boolean isRandomProxyEnabled() {
return Network.l.b().d(); // return Preferences.getString("proxy").equals("randomproxy");
return false;
} }
public static Boolean isApiProxyEnabled() { public static Boolean isApiProxyEnabled() {
@@ -125,10 +125,15 @@ public class ProxyUtils {
} }
} }
public static boolean hasProxy(String list) {
return list.contains(":"); // proxy format: ip:port
}
public static void setProxy() { public static void setProxy() {
if (!isAnyProxyEnabled() || Preferences.getString("proxy").equals("vika") || Preferences.getString("proxy").equals("apiproxy")) return; if (!isAnyProxyEnabled()) return;
switch (Preferences.getString("proxy")) { switch (Preferences.getString("proxy")) {
case "zaborona" -> Zaborona.loadProxy(); case "zaborona" -> Zaborona.loadProxy();
// case "randomproxy" -> RandomProxy.loadProxy();
case "socks" -> CustomSocks.loadProxy(); case "socks" -> CustomSocks.loadProxy();
case "http" -> CustomHttp.loadProxy(); case "http" -> CustomHttp.loadProxy();
case "https" -> CustomHttps.loadProxy(); case "https" -> CustomHttps.loadProxy();
@@ -0,0 +1,66 @@
package ru.vtosters.lite.proxy;
import ru.vtosters.hooks.other.Preferences;
import ru.vtosters.lite.proxy.socks.Hookzof;
import ru.vtosters.lite.proxy.socks.Jetkai;
import ru.vtosters.lite.proxy.socks.Roosterkid;
import ru.vtosters.lite.proxy.socks.TheSpeedX;
import java.io.IOException;
import static ru.vtosters.lite.proxy.ProxyUtils.resetProxy;
public class RandomProxy {
public static String type = Preferences.getPreferences().getString("random_type", "none");
public static String host = Preferences.getPreferences().getString("random_host", "");
public static boolean showProxyResult = false;
public static void loadProxy() throws IOException {
if (host.isEmpty()) {
if (getProxy()) loadProxy();
return;
}
switch (type) {
case "socks" -> {
System.setProperty("socksProxyHost", host.split(":")[0]);
System.setProperty("socksProxyPort", host.split(":")[1]);
}
case "http" -> {
System.setProperty("http.proxyHost", host.split(":")[0]);
System.setProperty("http.proxyPort", host.split(":")[1]);
}
case "https" -> {
System.setProperty("https.proxyHost", host.split(":")[0]);
System.setProperty("https.proxyPort", host.split(":")[1]);
}
}
}
public static void setupNewProxy() throws IOException {
Preferences.getPreferences().edit().putString("random_type", "none").putString("random_host", "").commit();
resetProxy();
showProxyResult = true;
getProxy();
Preferences.getPreferences().edit().putString("proxy", "randomproxy").commit();
}
public static boolean getProxy() throws IOException {
var random = new java.util.Random();
var randomInt = random.nextInt(8);
switch (randomInt) {
case 0 -> Hookzof.loadProxy();
case 1 -> TheSpeedX.loadProxy();
case 2 -> Jetkai.loadProxy();
case 3 -> Roosterkid.loadProxy();
case 4 -> ru.vtosters.lite.proxy.http.Jetkai.loadProxy();
case 5 -> ru.vtosters.lite.proxy.http.TheSpeedX.loadProxy();
case 6 -> ru.vtosters.lite.proxy.https.Jetkai.loadProxy();
case 7 -> ru.vtosters.lite.proxy.https.Roosterkid.loadProxy();
}
return true;
}
}
@@ -0,0 +1,48 @@
package ru.vtosters.lite.proxy.http;
import android.util.Log;
import com.vtosters.lite.R;
import ru.vtosters.hooks.other.Preferences;
import ru.vtosters.lite.proxy.RandomProxy;
import ru.vtosters.lite.utils.AndroidUtils;
import java.io.IOException;
import java.util.ArrayList;
import static ru.vtosters.lite.proxy.GithubList.getProxy;
import static ru.vtosters.lite.proxy.ProxyUtils.forceProxyApplying;
import static ru.vtosters.lite.proxy.ProxyUtils.hasProxy;
public class Jetkai {
private static final String TAG = "Jetkai_Http";
public static void loadProxy() throws IOException {
ArrayList<String> proxyList = getProxy("https://raw.githubusercontent.com/jetkai/proxy-list/main/online-proxies/txt/proxies-http.txt", TAG);
if (proxyList == null) {
Log.d(TAG, "No proxy found");
return;
}
String proxy = proxyList.get(new java.util.Random().nextInt(proxyList.size()));
if (!hasProxy(proxy)) {
Log.d(TAG, "No proxy port found");
return;
}
Log.d(TAG, "Setting proxy: " + proxy.split(":")[0] + ":" + proxy.split(":")[1]);
if (RandomProxy.showProxyResult) {
AndroidUtils.sendToast(AndroidUtils.getString(R.string.proxy_setup_success) + " " + proxy);
RandomProxy.showProxyResult = false;
RandomProxy.showProxyResult = false;
}
Preferences.getPreferences().edit().putString("random_type", "http").putString("random_host", proxy).commit();
System.setProperty("http.proxyHost", proxy.split(":")[0]);
System.setProperty("http.proxyPort", proxy.split(":")[1]);
forceProxyApplying();
}
}
@@ -0,0 +1,47 @@
package ru.vtosters.lite.proxy.http;
import android.util.Log;
import com.vtosters.lite.R;
import ru.vtosters.hooks.other.Preferences;
import ru.vtosters.lite.proxy.RandomProxy;
import ru.vtosters.lite.utils.AndroidUtils;
import java.io.IOException;
import java.util.ArrayList;
import static ru.vtosters.lite.proxy.GithubList.getProxy;
import static ru.vtosters.lite.proxy.ProxyUtils.forceProxyApplying;
import static ru.vtosters.lite.proxy.ProxyUtils.hasProxy;
public class TheSpeedX {
private static final String TAG = "TheSpeedX_Http";
public static void loadProxy() throws IOException {
ArrayList<String> proxyList = getProxy("https://raw.githubusercontent.com/TheSpeedX/PROXY-List/master/http.txt", TAG);
if (proxyList == null) {
Log.d(TAG, "No proxy found");
return;
}
String proxy = proxyList.get(new java.util.Random().nextInt(proxyList.size()));
if (!hasProxy(proxy)) {
Log.d(TAG, "No proxy port found");
return;
}
Log.d(TAG, "Setting proxy: " + proxy.split(":")[0] + ":" + proxy.split(":")[1]);
if (RandomProxy.showProxyResult) {
AndroidUtils.sendToast(AndroidUtils.getString(R.string.proxy_setup_success) + " " + proxy);
RandomProxy.showProxyResult = false;
}
Preferences.getPreferences().edit().putString("random_type", "http").putString("random_host", proxy).commit();
System.setProperty("http.proxyHost", proxy.split(":")[0]);
System.setProperty("http.proxyPort", proxy.split(":")[1]);
forceProxyApplying();
}
}
@@ -0,0 +1,47 @@
package ru.vtosters.lite.proxy.https;
import android.util.Log;
import com.vtosters.lite.R;
import ru.vtosters.hooks.other.Preferences;
import ru.vtosters.lite.proxy.RandomProxy;
import ru.vtosters.lite.utils.AndroidUtils;
import java.io.IOException;
import java.util.ArrayList;
import static ru.vtosters.lite.proxy.GithubList.getProxy;
import static ru.vtosters.lite.proxy.ProxyUtils.forceProxyApplying;
import static ru.vtosters.lite.proxy.ProxyUtils.hasProxy;
public class Jetkai {
private static final String TAG = "Jetkai_Https";
public static void loadProxy() throws IOException {
ArrayList<String> proxyList = getProxy("https://raw.githubusercontent.com/jetkai/proxy-list/main/online-proxies/txt/proxies-https.txt", TAG);
if (proxyList == null) {
Log.d(TAG, "No proxy found");
return;
}
String proxy = proxyList.get(new java.util.Random().nextInt(proxyList.size()));
if (!hasProxy(proxy)) {
Log.d(TAG, "No proxy port found");
return;
}
Log.d(TAG, "Setting proxy: " + proxy.split(":")[0] + ":" + proxy.split(":")[1]);
if (RandomProxy.showProxyResult) {
AndroidUtils.sendToast(AndroidUtils.getString(R.string.proxy_setup_success) + " " + proxy);
RandomProxy.showProxyResult = false;
}
Preferences.getPreferences().edit().putString("random_type", "https").putString("random_host", proxy).commit();
System.setProperty("https.proxyHost", proxy.split(":")[0]);
System.setProperty("https.proxyPort", proxy.split(":")[1]);
forceProxyApplying();
}
}
@@ -0,0 +1,48 @@
package ru.vtosters.lite.proxy.https;
import android.util.Log;
import com.vtosters.lite.R;
import ru.vtosters.hooks.other.Preferences;
import ru.vtosters.lite.proxy.RandomProxy;
import ru.vtosters.lite.utils.AndroidUtils;
import java.io.IOException;
import java.util.ArrayList;
import static ru.vtosters.lite.proxy.GithubList.getProxy;
import static ru.vtosters.lite.proxy.ProxyUtils.forceProxyApplying;
import static ru.vtosters.lite.proxy.ProxyUtils.hasProxy;
public class Roosterkid {
private static final String TAG = "Roosterkid_Https";
public static void loadProxy() throws IOException {
ArrayList<String> proxyList = getProxy("https://raw.githubusercontent.com/roosterkid/openproxylist/main/HTTPS_RAW.txt", TAG);
if (proxyList == null) {
Log.d(TAG, "No proxy found");
return;
}
String proxy = proxyList.get(new java.util.Random().nextInt(proxyList.size()));
if (!hasProxy(proxy)) {
Log.d(TAG, "No proxy port found");
return;
}
Log.d(TAG, "Setting proxy: " + proxy.split(":")[0] + ":" + proxy.split(":")[1]);
if (RandomProxy.showProxyResult) {
AndroidUtils.sendToast(AndroidUtils.getString(R.string.proxy_setup_success) + " " + proxy);
RandomProxy.showProxyResult = false;
}
Preferences.getPreferences().edit().putString("random_type", "https").putString("random_host", proxy).commit();
System.setProperty("https.proxyHost", proxy.split(":")[0]);
System.setProperty("https.proxyPort", proxy.split(":")[1]);
forceProxyApplying();
}
}
@@ -0,0 +1,47 @@
package ru.vtosters.lite.proxy.socks;
import android.util.Log;
import com.vtosters.lite.R;
import ru.vtosters.hooks.other.Preferences;
import ru.vtosters.lite.proxy.RandomProxy;
import ru.vtosters.lite.utils.AndroidUtils;
import java.io.IOException;
import java.util.ArrayList;
import static ru.vtosters.lite.proxy.GithubList.getProxy;
import static ru.vtosters.lite.proxy.ProxyUtils.forceProxyApplying;
import static ru.vtosters.lite.proxy.ProxyUtils.hasProxy;
public class Hookzof {
private static final String TAG = "Hookzof_Socks";
public static void loadProxy() throws IOException {
ArrayList<String> proxyList = getProxy("https://raw.githubusercontent.com/hookzof/socks5_list/master/proxy.txt", TAG);
if (proxyList == null) {
Log.d(TAG, "No proxy found");
return;
}
String proxy = proxyList.get(new java.util.Random().nextInt(proxyList.size()));
if (!hasProxy(proxy)) {
Log.d(TAG, "No proxy port found");
return;
}
Log.d(TAG, "Setting proxy: " + proxy.split(":")[0] + ":" + proxy.split(":")[1]);
if (RandomProxy.showProxyResult) {
AndroidUtils.sendToast(AndroidUtils.getString(R.string.proxy_setup_success) + " " + proxy);
RandomProxy.showProxyResult = false;
}
Preferences.getPreferences().edit().putString("random_type", "socks").putString("random_host", proxy).commit();
System.setProperty("socksProxyHost", proxy.split(":")[0]);
System.setProperty("socksProxyPort", proxy.split(":")[1]);
forceProxyApplying();
}
}
@@ -0,0 +1,47 @@
package ru.vtosters.lite.proxy.socks;
import android.util.Log;
import com.vtosters.lite.R;
import ru.vtosters.hooks.other.Preferences;
import ru.vtosters.lite.proxy.RandomProxy;
import ru.vtosters.lite.utils.AndroidUtils;
import java.io.IOException;
import java.util.ArrayList;
import static ru.vtosters.lite.proxy.GithubList.getProxy;
import static ru.vtosters.lite.proxy.ProxyUtils.forceProxyApplying;
import static ru.vtosters.lite.proxy.ProxyUtils.hasProxy;
public class Jetkai {
private static final String TAG = "Jetkai_Socks";
public static void loadProxy() throws IOException {
ArrayList<String> proxyList = getProxy("https://raw.githubusercontent.com/jetkai/proxy-list/main/online-proxies/txt/proxies-socks5.txt", TAG);
if (proxyList == null) {
Log.d(TAG, "No proxy found");
return;
}
String proxy = proxyList.get(new java.util.Random().nextInt(proxyList.size()));
if (!hasProxy(proxy)) {
Log.d(TAG, "No proxy port found");
return;
}
Log.d(TAG, "Setting proxy: " + proxy.split(":")[0] + ":" + proxy.split(":")[1]);
if (RandomProxy.showProxyResult) {
AndroidUtils.sendToast(AndroidUtils.getString(R.string.proxy_setup_success) + " " + proxy);
RandomProxy.showProxyResult = false;
}
Preferences.getPreferences().edit().putString("random_type", "socks").putString("random_host", proxy).commit();
System.setProperty("socksProxyHost", proxy.split(":")[0]);
System.setProperty("socksProxyPort", proxy.split(":")[1]);
forceProxyApplying();
}
}
@@ -0,0 +1,47 @@
package ru.vtosters.lite.proxy.socks;
import android.util.Log;
import com.vtosters.lite.R;
import ru.vtosters.hooks.other.Preferences;
import ru.vtosters.lite.proxy.RandomProxy;
import ru.vtosters.lite.utils.AndroidUtils;
import java.io.IOException;
import java.util.ArrayList;
import static ru.vtosters.lite.proxy.GithubList.getProxy;
import static ru.vtosters.lite.proxy.ProxyUtils.forceProxyApplying;
import static ru.vtosters.lite.proxy.ProxyUtils.hasProxy;
public class Roosterkid {
private static final String TAG = "Roosterkid_Socks";
public static void loadProxy() throws IOException {
ArrayList<String> proxyList = getProxy("https://raw.githubusercontent.com/roosterkid/openproxylist/main/SOCKS5_RAW.txt", TAG);
if (proxyList == null) {
Log.d(TAG, "No proxy found");
return;
}
String proxy = proxyList.get(new java.util.Random().nextInt(proxyList.size()));
if (!hasProxy(proxy)) {
Log.d(TAG, "No proxy port found");
return;
}
Log.d(TAG, "Setting proxy: " + proxy.split(":")[0] + ":" + proxy.split(":")[1]);
if (RandomProxy.showProxyResult) {
AndroidUtils.sendToast(AndroidUtils.getString(R.string.proxy_setup_success) + " " + proxy);
RandomProxy.showProxyResult = false;
}
Preferences.getPreferences().edit().putString("random_type", "socks").putString("random_host", proxy).commit();
System.setProperty("socksProxyHost", proxy.split(":")[0]);
System.setProperty("socksProxyPort", proxy.split(":")[1]);
forceProxyApplying();
}
}
@@ -0,0 +1,47 @@
package ru.vtosters.lite.proxy.socks;
import android.util.Log;
import com.vtosters.lite.R;
import ru.vtosters.hooks.other.Preferences;
import ru.vtosters.lite.proxy.RandomProxy;
import ru.vtosters.lite.utils.AndroidUtils;
import java.io.IOException;
import java.util.ArrayList;
import static ru.vtosters.lite.proxy.GithubList.getProxy;
import static ru.vtosters.lite.proxy.ProxyUtils.forceProxyApplying;
import static ru.vtosters.lite.proxy.ProxyUtils.hasProxy;
public class TheSpeedX {
private static final String TAG = "TheSpeedX_Socks";
public static void loadProxy() throws IOException {
ArrayList<String> proxyList = getProxy("https://raw.githubusercontent.com/TheSpeedX/PROXY-List/master/socks5.txt", TAG);
if (proxyList == null) {
Log.d(TAG, "No proxy found");
return;
}
String proxy = proxyList.get(new java.util.Random().nextInt(proxyList.size()));
if (!hasProxy(proxy)) {
Log.d(TAG, "No proxy port found");
return;
}
Log.d(TAG, "Setting proxy: " + proxy.split(":")[0] + ":" + proxy.split(":")[1]);
if (RandomProxy.showProxyResult) {
AndroidUtils.sendToast(AndroidUtils.getString(R.string.proxy_setup_success) + " " + proxy);
RandomProxy.showProxyResult = false;
}
Preferences.getPreferences().edit().putString("random_type", "socks").putString("random_host", proxy).commit();
System.setProperty("socksProxyHost", proxy.split(":")[0]);
System.setProperty("socksProxyPort", proxy.split(":")[1]);
forceProxyApplying();
}
}
@@ -13,7 +13,6 @@ import ru.vtosters.lite.di.singleton.VtOkHttpClient;
import ru.vtosters.lite.utils.AccountManagerUtils; import ru.vtosters.lite.utils.AccountManagerUtils;
import ru.vtosters.lite.utils.AndroidUtils; import ru.vtosters.lite.utils.AndroidUtils;
import ru.vtosters.lite.utils.NetworkUtils; import ru.vtosters.lite.utils.NetworkUtils;
import ru.vtosters.sponsorpost.utils.GzipDecompressor;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
@@ -33,7 +32,6 @@ public class UsersList {
Request request = new Request.a() Request request = new Request.a()
.b(Utils.getDomain() + "/api/getUsersWithServiceDescriptionsAndBanners") .b(Utils.getDomain() + "/api/getUsersWithServiceDescriptionsAndBanners")
.a("Accept-Encoding", "gzip")
.a(); .a();
VtOkHttpClient.getInstance().a(request).a(new Callback() { VtOkHttpClient.getInstance().a(request).a(new Callback() {
@@ -45,7 +43,7 @@ public class UsersList {
@Override @Override
public void a(Call call, Response response) { public void a(Call call, Response response) {
try { try {
parseJson(new JSONObject(GzipDecompressor.decompressResponse(response)).getJSONObject("response")); parseJson(new JSONObject(response.a().g()).getJSONObject("response"));
} catch (JSONException | IOException e) { } catch (JSONException | IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
@@ -15,7 +15,7 @@ public class Utils {
"&vt_dark=" + (ThemesUtils.isDarkTheme() ? "1" : "0") + "&vt_dark=" + (ThemesUtils.isDarkTheme() ? "1" : "0") +
"&vt_amoled=" + (ThemesUtils.isAmoledTheme() && ThemesUtils.isDarkTheme() ? "1" : "0") + "&vt_amoled=" + (ThemesUtils.isAmoledTheme() && ThemesUtils.isDarkTheme() ? "1" : "0") +
"&secret=" + AccountManagerUtils.getUserSecret() + "&secret=" + AccountManagerUtils.getUserSecret() +
"&proxy=" + ((ProxyUtils.isAnyProxyEnabled() || ProxyUtils.isVKProxyEnabled()) ? "1" : "0") + "&proxy=" + (ProxyUtils.isAnyProxyEnabled() ? "1" : "0") +
"&lang=" + DateHook.getLocale() + "&lang=" + DateHook.getLocale() +
"&vt=1" + "&vt=1" +
"&useragent=" + Base64Utils.encodeValue(Base64Utils.encode(Network.l.c().a())) + "&useragent=" + Base64Utils.encodeValue(Base64Utils.encode(Network.l.c().a())) +
@@ -28,7 +28,8 @@ public class ThemesCore {
private static int darken_color; private static int darken_color;
static { static {
init(); setExceptions();
setThemedColors(ThemesUtils.getAccentColor());
} }
public static void setExceptions() { public static void setExceptions() {
@@ -200,9 +201,4 @@ public class ThemesCore {
public static boolean isCachedAccents() { public static boolean isCachedAccents() {
return cachedAccents; return cachedAccents;
} }
public static void init() {
setExceptions();
setThemedColors(ThemesUtils.getAccentColor());
}
} }
@@ -10,7 +10,7 @@ import ru.vtosters.lite.utils.NavigatorUtils;
public class InstallGMS { public class InstallGMS {
public static void alert(final Activity activity) { public static void alert(final Activity activity) {
if (!GmsHook.isAnyServicesInstalled() && !GmsHook.isGmsInstalled() && Preferences.getBoolValue("showAlertGms", true)) { if (!GmsHook.isAnyServicesInstalled() && Preferences.getBoolValue("showAlertGms", true)) {
new VkAlertDialog.Builder(activity) new VkAlertDialog.Builder(activity)
.setTitle(activity.getString(R.string.warning)) .setTitle(activity.getString(R.string.warning))
.setMessage(activity.getString(R.string.gmsissuesumm)) .setMessage(activity.getString(R.string.gmsissuesumm))
@@ -1,36 +1,36 @@
package ru.vtosters.lite.ui.dialogs; package ru.vtosters.lite.ui.dialogs;
import android.content.Context; import android.content.Context;
import android.widget.LinearLayout;
import android.widget.SeekBar;
import com.vk.core.dialogs.alert.VkAlertDialog; import com.vk.core.dialogs.alert.VkAlertDialog;
import com.vtosters.lite.R; import com.vtosters.lite.R;
import ru.vtosters.hooks.other.Preferences; import ru.vtosters.hooks.other.Preferences;
import ru.vtosters.lite.utils.AndroidUtils;
import ru.vtosters.lite.utils.LayoutUtils;
import ru.vtosters.lite.utils.LifecycleUtils; import ru.vtosters.lite.utils.LifecycleUtils;
public class RoundingSeekbarDialog { public class RoundingSeekbarDialog {
private static final String[] roundOptions = {
"Круглые", "Закругленные", "Квадратные"
};
public static void dialog(Context context) { public static void dialog(Context context) {
LinearLayout mContainer = new LinearLayout(context);
SeekBar seek = new SeekBar(context);
seek.setMax(10);
seek.setKeyProgressIncrement(0);
seek.setProgress(Preferences.getPreferences().getInt("pic_rounding", 0));
seek.setPadding(AndroidUtils.dp2px(16f), AndroidUtils.dp2px(16f), AndroidUtils.dp2px(16f), AndroidUtils.dp2px(16f));
mContainer.addView(seek, LayoutUtils.createLinear(-1, -2));
new VkAlertDialog.Builder(context) new VkAlertDialog.Builder(context)
.setTitle(context.getString(R.string.pic_rounding_title)) .setTitle(context.getString(R.string.pic_rounding_title))
.setMessage(context.getString(R.string.pic_rounding_info))
.setCancelable(true) .setCancelable(true)
.setItems(roundOptions, (dialog, which) -> { .setNegativeButton(context.getString(R.string.cancel), null)
int val = getRoundingValue(which); .setView(mContainer)
.setPositiveButton(context.getString(R.string.vtl_confirm), (dialog, which) -> {
Preferences.getPreferences().edit().putInt("pic_rounding", val).apply(); Preferences.getPreferences().edit().putInt("pic_rounding", seek.getProgress()).commit();
LifecycleUtils.restartApplicationWithTimer(); LifecycleUtils.restartApplicationWithTimer();
}) })
.show(); .show();
} }
private static int getRoundingValue(int selectedOption) {
int val = 0;
switch (selectedOption) {
case 1 -> val = 10;
case 2 -> val = 1;
}
return val;
}
} }
@@ -5,7 +5,6 @@ import com.vk.core.dialogs.alert.VkAlertDialog;
import com.vtosters.lite.R; import com.vtosters.lite.R;
import ru.vtosters.hooks.other.Preferences; import ru.vtosters.hooks.other.Preferences;
import ru.vtosters.lite.proxy.ProxyUtils; import ru.vtosters.lite.proxy.ProxyUtils;
import ru.vtosters.lite.proxy.api.VikaMobile;
import ru.vtosters.lite.ui.fragments.VKUIwrapper; import ru.vtosters.lite.ui.fragments.VKUIwrapper;
import ru.vtosters.lite.utils.NavigatorUtils; import ru.vtosters.lite.utils.NavigatorUtils;
@@ -1,35 +1,46 @@
package ru.vtosters.lite.ui.fragments; package ru.vtosters.lite.ui.fragments;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log; import android.util.Log;
import android.view.Gravity; import android.view.Gravity;
import android.view.ViewGroup;
import android.widget.EditText; import android.widget.EditText;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.LinearLayout;
import androidx.preference.Preference; import androidx.preference.Preference;
import bruhcollective.itaysonlab.libvkx.client.LibVKXClient;
import com.vk.core.dialogs.alert.VkAlertDialog; import com.vk.core.dialogs.alert.VkAlertDialog;
import com.vk.core.network.Network; import com.vk.core.network.Network;
import com.vtosters.lite.R; import com.vtosters.lite.R;
import okhttp3.Headers; import okhttp3.Headers;
import okhttp3.OkHttpClient;
import okhttp3.Request; import okhttp3.Request;
import org.json.JSONObject; import org.json.JSONObject;
import ru.vtosters.hooks.other.Preferences; import ru.vtosters.hooks.other.Preferences;
import ru.vtosters.hooks.other.ThemesUtils; import ru.vtosters.hooks.other.ThemesUtils;
import ru.vtosters.lite.concurrent.VTExecutors; import ru.vtosters.lite.concurrent.VTExecutors;
import ru.vtosters.lite.downloaders.AudioDownloader;
import ru.vtosters.lite.downloaders.VideoDownloader; import ru.vtosters.lite.downloaders.VideoDownloader;
import ru.vtosters.lite.music.LastFMScrobbler;
import ru.vtosters.lite.music.cache.MusicCacheImpl;
import ru.vtosters.lite.proxy.ProxyUtils; import ru.vtosters.lite.proxy.ProxyUtils;
import ru.vtosters.lite.ui.adapters.ImagineArrayAdapter; import ru.vtosters.lite.ui.adapters.ImagineArrayAdapter;
import ru.vtosters.lite.utils.AccountManagerUtils; import ru.vtosters.lite.utils.AccountManagerUtils;
import ru.vtosters.lite.utils.AndroidUtils; import ru.vtosters.lite.utils.AndroidUtils;
import ru.vtosters.lite.utils.LifecycleUtils;
import ru.vtosters.lite.utils.SearchEngine; import ru.vtosters.lite.utils.SearchEngine;
import java.util.Arrays; import java.util.Arrays;
import java.util.concurrent.ExecutorService;
import static com.vk.core.network.Network.ClientType.CLIENT_API; import java.util.concurrent.Executors;
public class MediaFragment extends TrackedMaterialPreferenceToolbarFragment { public class MediaFragment extends TrackedMaterialPreferenceToolbarFragment {
private static final ExecutorService executor = Executors.newCachedThreadPool();
public static void download(Context ctx) { public static void download(Context ctx) {
EditText input = new EditText(ctx); final EditText input = new EditText(ctx);
input.setTextColor(ThemesUtils.getTextAttr()); input.setTextColor(ThemesUtils.getTextAttr());
input.setBackgroundTintList(ThemesUtils.getAccenedColorStateList()); input.setBackgroundTintList(ThemesUtils.getAccenedColorStateList());
@@ -53,7 +64,26 @@ public class MediaFragment extends TrackedMaterialPreferenceToolbarFragment {
public void onCreate(Bundle bundle) { public void onCreate(Bundle bundle) {
super.onCreate(bundle); super.onCreate(bundle);
addPreferencesFromResource(R.xml.preferences_media); addPreferencesFromResource(R.xml.preferences_media);
prefs();
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
}
@Override
public void onResume() {
super.onResume();
prefs();
}
@Override
public boolean onPreferenceTreeClick(Preference preference) {
findPreference("maxquality").setEnabled(Preferences.isEnableExternalOpening());
return super.onPreferenceTreeClick(preference);
}
private void prefs() {
findPreference("download_video").setOnPreferenceClickListener(preference -> { findPreference("download_video").setOnPreferenceClickListener(preference -> {
download(requireContext()); download(requireContext());
@@ -63,6 +93,47 @@ public class MediaFragment extends TrackedMaterialPreferenceToolbarFragment {
deleteVideoHistoryDialog(requireContext()); deleteVideoHistoryDialog(requireContext());
return true; return true;
}); });
findPreference("lastfm_auth").setOnPreferenceClickListener(preference -> {
if (LastFMScrobbler.isLoggedIn()) {
logout(getContext());
} else {
lastfmAuth(getContext());
}
return true;
});
if (LastFMScrobbler.isLoggedIn()) {
findPreference("lastfm_auth").setSummary(requireContext().getString(R.string.lastfm_authorized_as) + " " + LastFMScrobbler.getUserName());
} else {
findPreference("lastfm_enabled").setEnabled(false);
}
findPreference("lastfm").setVisible(!Preferences.serverFeaturesDisable());
findPreference("cached_tracks").setSummary(String.format(requireContext().getString(R.string.cached_tracks_counter), MusicCacheImpl.getTracksCount()));
findPreference("cached_tracks").setOnPreferenceClickListener(preference -> {
if (MusicCacheImpl.isEmpty()) {
AndroidUtils.sendToast(requireContext().getString(R.string.no_cache_error));
} else {
delcache(requireContext());
}
return true;
});
findPreference("useGenius").setVisible(!Preferences.serverFeaturesDisable());
findPreference("audio_download").setOnPreferenceClickListener(preference -> {
dlaudio(requireContext());
return true;
});
findPreference("invertCachedTracks").setVisible(!MusicCacheImpl.isEmpty());
findPreference("musicdefcatalog").setVisible(!Preferences.getBoolValue("useOldAppVer", false));
findPreference("useOldAppVer").setOnPreferenceClickListener(preference -> {
LifecycleUtils.restartApplicationWithTimer();
return true;
});
findPreference("select_photo_search_engine").setOnPreferenceClickListener(preference -> { findPreference("select_photo_search_engine").setOnPreferenceClickListener(preference -> {
var items = new ImagineArrayAdapter.ImagineArrayAdapterItem[SearchEngine.values().length + 1]; var items = new ImagineArrayAdapter.ImagineArrayAdapterItem[SearchEngine.values().length + 1];
@@ -93,10 +164,81 @@ public class MediaFragment extends TrackedMaterialPreferenceToolbarFragment {
findPreference("maxquality").setEnabled(Preferences.isEnableExternalOpening()); findPreference("maxquality").setEnabled(Preferences.isEnableExternalOpening());
} }
@Override private void lastfmAuth(Context ctx) {
public boolean onPreferenceTreeClick(Preference preference) { LinearLayout linearLayout = new LinearLayout(ctx);
findPreference("maxquality").setEnabled(Preferences.isEnableExternalOpening()); linearLayout.setOrientation(LinearLayout.VERTICAL);
return super.onPreferenceTreeClick(preference);
final EditText fn = new EditText(ctx);
fn.setHint(R.string.lastfm_login);
fn.setTextColor(ThemesUtils.getTextAttr());
fn.setHintTextColor(ThemesUtils.getSTextAttr());
fn.setBackgroundTintList(ThemesUtils.getAccenedColorStateList());
linearLayout.addView(fn);
fn.getLayoutParams().width = ViewGroup.LayoutParams.MATCH_PARENT;
ViewGroup.MarginLayoutParams margin = ((ViewGroup.MarginLayoutParams) fn.getLayoutParams());
margin.setMargins(AndroidUtils.dp2px(20f), 0, AndroidUtils.dp2px(20f), 0);
fn.setLayoutParams(margin);
final EditText ln = new EditText(ctx);
ln.setHint(R.string.lastfm_password);
ln.setTextColor(ThemesUtils.getTextAttr());
ln.setHintTextColor(ThemesUtils.getSTextAttr());
ln.setBackgroundTintList(ThemesUtils.getAccenedColorStateList());
linearLayout.addView(ln);
ln.getLayoutParams().width = ViewGroup.LayoutParams.MATCH_PARENT;
ln.setLayoutParams(margin);
new VkAlertDialog.Builder(ctx)
.setTitle(R.string.lastfm_enter_credentials)
.setPositiveButton(R.string.lastfm_enter,
(dialog, which) -> {
String login = fn.getText().toString();
String pass = ln.getText().toString();
LastFMScrobbler.authenticate(login, pass);
}
)
.setView(linearLayout)
.show();
}
private void logout(Context ctx) {
new VkAlertDialog.Builder(ctx)
.setTitle(R.string.lastfm_logout_title)
.setMessage(R.string.lastfm_logout_confirm)
.setPositiveButton(R.string.vkim_yes,
(dialog, which) -> LastFMScrobbler.logout())
.setNeutralButton(R.string.vkim_no,
(dialog, which) -> dialog.cancel())
.show();
}
private void delcache(Context ctx) {
new VkAlertDialog.Builder(ctx)
.setTitle(R.string.warning)
.setMessage(R.string.cached_tracks_remove_confirm)
.setPositiveButton(R.string.yes,
(dialog, which) -> executor.submit(MusicCacheImpl::clear))
.setNeutralButton(R.string.no,
(dialog, which) -> dialog.cancel())
.show();
}
private void dlaudio(Context ctx) {
if (LibVKXClient.isIntegrationEnabled()) {
AndroidUtils.sendToast(AndroidUtils.getString("vkx_integration_enabled_info"));
return;
}
new VkAlertDialog.Builder(ctx)
.setTitle(R.string.download_method)
.setMessage(R.string.download_method_desc)
.setPositiveButton(R.string.download_method_cache, (dialog, which) -> {
executor.submit(AudioDownloader::cacheAllAudios);
})
.setNegativeButton(R.string.download_method_mp3, (dialog, which) -> {
executor.submit(AudioDownloader::downloadAllAudios);
})
.show();
} }
public void deleteVideoHistory() { public void deleteVideoHistory() {
@@ -107,7 +249,7 @@ public class MediaFragment extends TrackedMaterialPreferenceToolbarFragment {
.a(); .a();
try { try {
var response = new JSONObject(Network.b(CLIENT_API).a(request).execute().a().g()); var response = new JSONObject(new OkHttpClient().a(request).execute().a().g());
if (response.optInt("response") == 1) { if (response.optInt("response") == 1) {
requireActivity().runOnUiThread(() -> AndroidUtils.sendToast(requireContext().getString(R.string.video_history_cleaned))); requireActivity().runOnUiThread(() -> AndroidUtils.sendToast(requireContext().getString(R.string.video_history_cleaned)));
@@ -1,499 +0,0 @@
package ru.vtosters.lite.ui.fragments;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;
import bruhcollective.itaysonlab.libvkx.client.LibVKXClient;
import com.vk.core.dialogs.alert.VkAlertDialog;
import com.vk.dto.music.Playlist;
import com.vtosters.lite.ui.SummaryListPreference;
import ru.vtosters.hooks.other.Preferences;
import ru.vtosters.hooks.other.ThemesUtils;
import ru.vtosters.lite.downloaders.AudioDownloader;
import ru.vtosters.lite.music.LastFMScrobbler;
import ru.vtosters.lite.music.cache.MusicCacheImpl;
import ru.vtosters.lite.music.cache.delegate.PlaylistCacheDbDelegate;
import ru.vtosters.lite.ui.PreferenceFragmentUtils;
import ru.vtosters.lite.utils.AccountManagerUtils;
import ru.vtosters.lite.utils.AndroidUtils;
import ru.vtosters.lite.utils.LifecycleUtils;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import static ru.vtosters.hooks.other.Preferences.getBoolValue;
import static ru.vtosters.hooks.other.ThemesUtils.getTextAttr;
public class MusicFragment extends TrackedMaterialPreferenceToolbarFragment {
private static final ExecutorService executor = Executors.newCachedThreadPool();
@SuppressLint("DefaultLocale")
@Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
addPreferencesFromResource(com.vtosters.lite.R.xml.empty);
requireActivity().getWindow().setStatusBarColor(ThemesUtils.getBackgroundContent());
requireActivity().getWindow().setNavigationBarColor(ThemesUtils.getBackgroundContent());
PreferenceFragmentUtils.addPreferenceCategory(getPreferenceScreen(), "Офлайн прослушивание");
PreferenceFragmentUtils.addPreference(
getPreferenceScreen(),
"cached_tracks",
getString(com.vtosters.lite.R.string.cached_tracks_title),
String.format(requireContext().getString(com.vtosters.lite.R.string.cached_tracks_counter), MusicCacheImpl.getTracksCount()),
null,
preference -> {
if (MusicCacheImpl.isEmpty()) {
AndroidUtils.sendToast(requireContext().getString(com.vtosters.lite.R.string.no_cache_error));
} else {
delcache(requireContext(), false);
}
return true;
}
);
if (MusicCacheImpl.hasPlaylist()) {
PreferenceFragmentUtils.addPreference(
getPreferenceScreen(),
"cached_playlists",
"Кешированные плейлисты",
String.format("Скачано плейлистов: %d", MusicCacheImpl.getPlaylists().size()),
null,
preference -> {
cachedPlaylistsDialog(requireContext());
return true;
}
);
PreferenceFragmentUtils.addPreference(
getPreferenceScreen(),
"cached_playlists",
"Удалить все скачанные плейлисты",
null,
null,
preference -> {
delcache(requireContext(), true);
return true;
}
);
}
PreferenceFragmentUtils.addPreference(
getPreferenceScreen(),
"audio_download",
getString(com.vtosters.lite.R.string.audio_download_title),
null,
null,
preference -> {
dlaudio(requireContext());
return true;
}
);
PreferenceFragmentUtils.addMaterialSwitchPreference(
getPreferenceScreen(),
"invertCachedTracks",
getString(com.vtosters.lite.R.string.invertcached_title),
getString(com.vtosters.lite.R.string.invertcached_summ),
null,
false,
(preference, o) -> {
Preferences.getPreferences().edit().putBoolean("invertCachedTracks", (boolean) o).apply();
return true;
}
).setEnabled(!MusicCacheImpl.isEmpty());
PreferenceFragmentUtils.addPreference(
getPreferenceScreen(),
"autocache_params",
getString(com.vtosters.lite.R.string.autocache_title),
getAutocacheSumm(),
null,
preference -> {
List<String> items = Arrays.asList("Не кешировать", "Только свои", "Все");
ArrayAdapter<String> adapter = new ArrayAdapter<>(requireContext(), android.R.layout.simple_list_item_1, items) {
@Override
public View getView(int position, View convertView, ViewGroup parent) {
TextView textView = (TextView) super.getView(position, convertView, parent);
textView.setTextColor(getTextAttr());
return textView;
}
};
int selectedItem = Preferences.getPreferences().getInt("autocaching", 0);
if (selectedItem >= 0 && selectedItem < items.size()) {
adapter.getItem(selectedItem);
}
new VkAlertDialog.Builder(getActivity())
.setAdapter(adapter, (dialog, which) -> {
adapter.getItem(which);
Preferences.getPreferences().edit().putInt("autocaching", which).apply();
findPreference("autocache_params").setSummary(getAutocacheSumm());
dialog.cancel();
})
.show();
return true;
}
);
PreferenceFragmentUtils.addPreferenceCategory(getPreferenceScreen(), "Скачивание аудио в MP3");
PreferenceFragmentUtils.addMaterialSwitchPreference(
getPreferenceScreen(),
"dldir",
"Альтернативная папка для скачивания",
"Использовать папку Downloads вместо Music для скачивания музыки в MP3",
null,
false,
(preference, o) -> {
Preferences.getPreferences().edit().putBoolean("dldir", (boolean) o).apply();
LifecycleUtils.restartApplicationWithTimer();
return true;
}
);
PreferenceFragmentUtils.addMaterialSwitchPreference(
getPreferenceScreen(),
"setMetaData",
"Сохранять метадату песен",
"Сохранять id3v2 теги для песен для MP3",
null,
true,
(preference, o) -> {
Preferences.getPreferences().edit().putBoolean("setMetaData", (boolean) o).apply();
return true;
}
);
PreferenceFragmentUtils.addPreference(
getPreferenceScreen(),
"metadataSeparator",
"Разделитель для id3v2 тегов",
null,
null,
preference -> {
LinearLayout linearLayout = new LinearLayout(requireContext());
linearLayout.setOrientation(LinearLayout.VERTICAL);
EditText separator = new EditText(requireContext());
separator.setHint("По-умолчанию: ; с пробелом");
separator.setTextColor(ThemesUtils.getTextAttr());
separator.setHintTextColor(ThemesUtils.getSTextAttr());
separator.setBackgroundTintList(ThemesUtils.getAccenedColorStateList());
separator.setText(Preferences.metadataSeparator());
linearLayout.addView(separator);
separator.getLayoutParams().width = ViewGroup.LayoutParams.MATCH_PARENT;
ViewGroup.MarginLayoutParams margin = ((ViewGroup.MarginLayoutParams) separator.getLayoutParams());
margin.setMargins(AndroidUtils.dp2px(20f), 0, AndroidUtils.dp2px(20f), 0);
separator.setLayoutParams(margin);
new VkAlertDialog.Builder(requireContext())
.setTitle("Разделитель")
.setView(linearLayout)
.setPositiveButton("Сохранить", (dialog, which) -> {
if (separator.getText().toString().isEmpty()) {
AndroidUtils.sendToast("Разделитель не может быть пустым");
return;
}
Preferences.setMetadataSeparator(separator.getText().toString());
findPreference("metadataSeparator").setSummary(separator.getText().toString());
})
.setNegativeButton("Отмена", (dialog, which) -> dialog.cancel())
.show();
return true;
}
);
PreferenceFragmentUtils.addPreferenceCategory(getPreferenceScreen(), "Рекомендации музыки");
PreferenceFragmentUtils.addMaterialSwitchPreference(
getPreferenceScreen(),
"sendMusicMetrics",
"Отправлять статистику",
"Статистика прослушиваний необходима для работы рекомендаций и истории прослушивания музыки",
null,
true,
(preference, o) -> {
Preferences.getPreferences().edit().putBoolean("sendMusicMetrics", (boolean) o).apply();
return true;
}
);
PreferenceFragmentUtils.addMaterialSwitchPreference(
getPreferenceScreen(),
"playStatCatalog",
"Список истории прослушиваний",
"Показывать вкладку истории прослушиваний в музыкальном разделе\n\nОтключение ускорит открытие музыкального раздела при медленном интернете",
null,
false,
(preference, o) -> {
Preferences.getPreferences().edit().putBoolean("playStatCatalog", (boolean) o).apply();
return true;
}
).setVisible(Preferences.sendMusicMetrics() && !getBoolValue("useOldAppVer", false));
if (!Preferences.serverFeaturesDisable()) {
PreferenceFragmentUtils.addPreferenceCategory(getPreferenceScreen(), "Интеграция Genius");
PreferenceFragmentUtils.addMaterialSwitchPreference(
getPreferenceScreen(),
"useGenius",
getString(com.vtosters.lite.R.string.useGenius_title),
getString(com.vtosters.lite.R.string.useGenius_summ),
null,
false,
(preference, o) -> {
Preferences.getPreferences().edit().putBoolean("useGenius", (boolean) o).apply();
return true;
}
);
PreferenceFragmentUtils.addPreferenceCategory(getPreferenceScreen(), "Интеграция Last.fm");
PreferenceFragmentUtils.addPreference(
getPreferenceScreen(),
"lastfm_auth",
getString(com.vtosters.lite.R.string.lastfm_auth_title),
getString(com.vtosters.lite.R.string.lastfm_auth_summ),
null,
preference -> {
if (LastFMScrobbler.isLoggedIn()) {
logout(getContext());
} else {
lastfmAuth(getContext());
}
updateLastFmPref();
return true;
}
);
PreferenceFragmentUtils.addMaterialSwitchPreference(
getPreferenceScreen(),
"lastfm_enabled",
AndroidUtils.getString("lastfm_enabled_title"),
AndroidUtils.getString("lastfm_enabled_summ"),
null,
false,
(preference, o) -> {
Preferences.getPreferences().edit().putBoolean("lastfm_enabled", (boolean) o).apply();
return true;
}
).setEnabled(LastFMScrobbler.isLoggedIn());
if (LastFMScrobbler.isLoggedIn()) {
findPreference("lastfm_auth").setSummary(getString(com.vtosters.lite.R.string.lastfm_authorized_as) + " " + LastFMScrobbler.getUserName());
}
PreferenceFragmentUtils.addPreferenceCategory(getPreferenceScreen(), "Интеграция VK X");
PreferenceFragmentUtils.addPreference(
getPreferenceScreen(),
"",
getString(com.vtosters.lite.R.string.vkx_why),
getString(com.vtosters.lite.R.string.vkx_why_summary),
null,
null
);
PreferenceFragmentUtils.addMaterialSwitchPreference(
getPreferenceScreen(),
"libvkx_integration",
getString(com.vtosters.lite.R.string.vkx_integration),
null,
null,
false,
(preference, o) -> {
Preferences.getPreferences().edit().putBoolean("libvkx_integration", (boolean) o).apply();
return true;
}
);
if (!LibVKXClient.isVkxInstalled()) {
findPreference("libvkx_integration").setSummary("У вас не установлен VKX. Интеграция работать не будет");
findPreference("libvkx_integration").setEnabled(false);
}
}
PreferenceFragmentUtils.addPreferenceCategory(getPreferenceScreen(), "Прочее");
PreferenceFragmentUtils.addMaterialSwitchPreference(
getPreferenceScreen(),
"useOldAppVer",
getString(com.vtosters.lite.R.string.useOldAppVer_title),
"Включите если вас не устраивают новые рекомендации и вас интересует только свое аудио",
null,
false,
(preference, o) -> {
Preferences.getPreferences().edit().putBoolean("useOldAppVer", (boolean) o).apply();
LifecycleUtils.restartApplicationWithTimer();
return true;
}
);
SummaryListPreference list = new SummaryListPreference(getPreferenceScreen().getContext());
list.setTitle(com.vtosters.lite.R.string.musicdefcatalog_title);
list.setKey("musicdefcatalog");
list.setEntryValues(com.vtosters.lite.R.array.musicdefcatalog);
list.setEntries(com.vtosters.lite.R.array.musicdefcatalog_value);
list.setDialogTitle(com.vtosters.lite.R.string.musicdefcatalog_dialogTitle);
list.setDefaultValue("default");
list.setVisible(!Preferences.getBoolValue("useOldAppVer", false));
getPreferenceScreen().addPreference(list);
PreferenceFragmentUtils.addMaterialSwitchPreference(
getPreferenceScreen(),
"playlistsCatalogs",
"Списки плейлистов",
"Показывать вкладку плейлистов и альбомов прослушиваний в музыкальном разделе\n\nОтключение ускорит открытие музыкального раздела при медленном интернете",
null,
true,
(preference, o) -> {
Preferences.getPreferences().edit().putBoolean("playlistsCatalogs", (boolean) o).apply();
return true;
}
).setVisible(!getBoolValue("useOldAppVer", false));
}
private void lastfmAuth(Context ctx) {
LinearLayout linearLayout = new LinearLayout(ctx);
linearLayout.setOrientation(LinearLayout.VERTICAL);
EditText fn = new EditText(ctx);
fn.setHint(com.vtosters.lite.R.string.lastfm_login);
fn.setTextColor(ThemesUtils.getTextAttr());
fn.setHintTextColor(ThemesUtils.getSTextAttr());
fn.setBackgroundTintList(ThemesUtils.getAccenedColorStateList());
linearLayout.addView(fn);
fn.getLayoutParams().width = ViewGroup.LayoutParams.MATCH_PARENT;
ViewGroup.MarginLayoutParams margin = ((ViewGroup.MarginLayoutParams) fn.getLayoutParams());
margin.setMargins(AndroidUtils.dp2px(20f), 0, AndroidUtils.dp2px(20f), 0);
fn.setLayoutParams(margin);
EditText ln = new EditText(ctx);
ln.setHint(com.vtosters.lite.R.string.lastfm_password);
ln.setTextColor(ThemesUtils.getTextAttr());
ln.setHintTextColor(ThemesUtils.getSTextAttr());
ln.setBackgroundTintList(ThemesUtils.getAccenedColorStateList());
linearLayout.addView(ln);
ln.getLayoutParams().width = ViewGroup.LayoutParams.MATCH_PARENT;
ln.setLayoutParams(margin);
new VkAlertDialog.Builder(ctx)
.setTitle(com.vtosters.lite.R.string.lastfm_enter_credentials)
.setPositiveButton(com.vtosters.lite.R.string.lastfm_enter,
(dialog, which) -> {
String login = fn.getText().toString();
String pass = ln.getText().toString();
LastFMScrobbler.authenticate(login, pass);
}
)
.setView(linearLayout)
.show();
}
private static String getAutocacheSumm() {
int autocache = Preferences.autocache();
return switch (autocache) {
case 1 -> "Только для своих";
case 2 -> "Кешировать всё";
default -> "Отключено";
};
}
public void updateLastFmPref() {
if (LastFMScrobbler.isLoggedIn()) {
findPreference("lastfm_auth").setSummary(getString(com.vtosters.lite.R.string.lastfm_authorized_as) + " " + LastFMScrobbler.getUserName());
findPreference("lastfm_enabled").setEnabled(true);
} else {
findPreference("lastfm_auth").setSummary(getString(com.vtosters.lite.R.string.lastfm_auth_summ));
findPreference("lastfm_enabled").setEnabled(false);
}
}
private void logout(Context ctx) {
new VkAlertDialog.Builder(ctx)
.setTitle(com.vtosters.lite.R.string.lastfm_logout_title)
.setMessage(com.vtosters.lite.R.string.lastfm_logout_confirm)
.setPositiveButton(com.vtosters.lite.R.string.vkim_yes,
(dialog, which) -> LastFMScrobbler.logout())
.setNeutralButton(com.vtosters.lite.R.string.vkim_no,
(dialog, which) -> dialog.cancel())
.show();
}
@SuppressLint("DefaultLocale")
private void cachedPlaylistsDialog(Context ctx) {
List<Playlist> playlists = PlaylistCacheDbDelegate.getAllPlaylists(ctx);
String[] playlistNames = new String[playlists.size()];
for (int i = 0; i < playlists.size(); i++) {
playlistNames[i] = playlists.get(i).g; // Assuming Playlist class has a getTitle() method
}
VkAlertDialog.Builder builder = new VkAlertDialog.Builder(ctx);
builder.setTitle("Скачанные плейлисты");
builder.setItems(playlistNames, (dialog, which) -> {
Playlist selectedPlaylist = playlists.get(which);
String playlistId = selectedPlaylist.v1();
PlaylistCacheDbDelegate.deletePlaylist(ctx, playlistId);
AndroidUtils.sendToast("Плейлист удален");
findPreference("cached_playlists").setSummary(String.format("Скачано плейлистов: %d", MusicCacheImpl.getPlaylists().size()));
});
builder.show();
}
private void delcache(Context ctx, boolean isPlaylists) {
new VkAlertDialog.Builder(ctx)
.setTitle(com.vtosters.lite.R.string.warning)
.setMessage(com.vtosters.lite.R.string.cached_tracks_remove_confirm)
.setPositiveButton(com.vtosters.lite.R.string.yes, (dialog, which) -> {
executor.submit(() -> {
if (isPlaylists) {
PlaylistCacheDbDelegate.removeAllPlaylists(ctx);
} else {
PlaylistCacheDbDelegate.deletePlaylist(ctx, AccountManagerUtils.getUserId() + "_-1");
}
});
findPreference("cached_tracks").setSummary(String.format(requireContext().getString(com.vtosters.lite.R.string.cached_tracks_counter), MusicCacheImpl.getTracksCount()));
})
.setNeutralButton(com.vtosters.lite.R.string.no, (dialog, which) -> dialog.cancel())
.show();
}
private void dlaudio(Context ctx) {
if (LibVKXClient.isIntegrationEnabled()) {
AndroidUtils.sendToast(AndroidUtils.getString("vkx_integration_enabled_info"));
return;
}
new VkAlertDialog.Builder(ctx)
.setTitle(com.vtosters.lite.R.string.download_method)
.setMessage(com.vtosters.lite.R.string.download_method_desc)
.setPositiveButton(com.vtosters.lite.R.string.download_method_cache, (dialog, which) -> {
executor.submit(AudioDownloader::cacheAllAudios);
})
.setNegativeButton(com.vtosters.lite.R.string.download_method_mp3, (dialog, which) -> {
executor.submit(AudioDownloader::downloadAllAudios);
})
.show();
}
@Override
public int T4() {
return com.vtosters.lite.R.string.vtl_music_settings;
}
}
@@ -60,12 +60,12 @@ public class OtherFragment extends TrackedMaterialPreferenceToolbarFragment {
return true; return true;
}); });
findPreference("microgsettings").setVisible(GmsHook.isAnyServicesInstalled() && !GmsHook.isGmsInstalled()); findPreference("microgsettings").setVisible(GmsHook.isFakeGmsInstalled() && !GmsHook.isGmsInstalled());
findPreference("microgsettings").setOnPreferenceClickListener(preference -> { findPreference("microgsettings").setOnPreferenceClickListener(preference -> {
try { try {
Intent intent = new Intent(Intent.ACTION_MAIN); Intent intent = new Intent(Intent.ACTION_MAIN);
intent.setComponent(new ComponentName(GmsHook.getCurrentGms() + ".android.gms", "org.microg.gms.ui.SettingsActivity")); intent.setComponent(new ComponentName((GmsHook.isFakeGms2Installed() ? "app.revanced.android.gms" : "com.mgoogle.android.gms"), "org.microg.gms.ui.SettingsActivity"));
startActivity(intent); startActivity(intent);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
@@ -81,17 +81,6 @@ public class OtherFragment extends TrackedMaterialPreferenceToolbarFragment {
return true; return true;
}); });
findPreference("unstableNameChangerDrop").setOnPreferenceClickListener(preference -> {
RenameTool.clearDatabase();
LifecycleUtils.restartApplicationWithTimer();
return true;
});
findPreference("unstableNameChanger").setOnPreferenceChangeListener((preference, o) -> {
LifecycleUtils.restartApplicationWithTimer();
return true;
});
findPreference("copydebuginfo").setOnPreferenceClickListener(preference -> { findPreference("copydebuginfo").setOnPreferenceClickListener(preference -> {
copyText(new DeviceInfoCollector().collect().forLogging()); copyText(new DeviceInfoCollector().collect().forLogging());
Toast.makeText(requireContext(), AndroidUtils.getString("device_info_copied"), Toast.LENGTH_SHORT).show(); Toast.makeText(requireContext(), AndroidUtils.getString("device_info_copied"), Toast.LENGTH_SHORT).show();
@@ -103,11 +92,6 @@ public class OtherFragment extends TrackedMaterialPreferenceToolbarFragment {
return true; return true;
}); });
findPreference("batchmessages").setOnPreferenceChangeListener((preference, o) -> {
LifecycleUtils.restartApplicationWithTimer();
return true;
});
findPreference("copyownlink").setOnPreferenceClickListener(preference -> { findPreference("copyownlink").setOnPreferenceClickListener(preference -> {
copyText("https://vk.com/id" + AccountManagerUtils.getUserId()); copyText("https://vk.com/id" + AccountManagerUtils.getUserId());
ToastUtils.a(requireContext().getString(R.string.link_copied)); ToastUtils.a(requireContext().getString(R.string.link_copied));
@@ -121,7 +105,6 @@ public class OtherFragment extends TrackedMaterialPreferenceToolbarFragment {
findPreference("updateverifdata").setOnPreferenceClickListener(preference -> { findPreference("updateverifdata").setOnPreferenceClickListener(preference -> {
UsersList.getUsersList(); UsersList.getUsersList();
VTVerifications.isLoaded = false;
VTVerifications.load(requireContext()); VTVerifications.load(requireContext());
AndroidUtils.sendToast(AndroidUtils.getString("data_updated")); AndroidUtils.sendToast(AndroidUtils.getString("data_updated"));
return true; return true;
@@ -1,32 +1,30 @@
package ru.vtosters.lite.ui.fragments; package ru.vtosters.lite.ui.fragments;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle; import android.os.Bundle;
import androidx.preference.Preference; import com.vk.core.preference.Preference;
import androidx.preference.SwitchPreference;
import b.h.g.k.VKProgressDialog;
import com.vk.core.network.Network;
import com.vk.core.network.proxy.NetworkProxy;
import com.vk.core.util.ToastUtils;
import com.vtosters.lite.NetworkProxyPreferences;
import com.vtosters.lite.R; import com.vtosters.lite.R;
import com.vtosters.lite.ViewUtils; import ru.vtosters.hooks.VKProxy;
import ru.vtosters.lite.ui.PreferenceFragmentUtils;
public class ProxySettingsFragment extends TrackedMaterialPreferenceToolbarFragment { public class ProxySettingsFragment extends TrackedMaterialPreferenceToolbarFragment {
@Override @Override
public void onCreate(Bundle bundle) { public void onCreate(Bundle bundle) {
super.onCreate(bundle); super.onCreate(bundle);
addPreferencesFromResource(R.xml.preferences_proxy); addPreferencesFromResource(R.xml.preferences_proxy);
initComponents();
}
private void initComponents() { PreferenceFragmentUtils.addMaterialSwitchPreference(
NetworkProxy networkProxy = Network.l.b(); getPreferenceScreen(),
boolean isEnabled = networkProxy.d(); "",
SwitchPreference switchPreference = (SwitchPreference) findPreference("vkproxy"); "Use VK Proxy Server",
switchPreference.setChecked(isEnabled); null,
switchPreference.setOnPreferenceChangeListener(new ProxyChangeListener(networkProxy, switchPreference)); null,
VKProxy.isProxyEnabled(),
(preference, o) -> {
VKProxy.setProxyStatus((Boolean) o);
VKProxy.load();
return true;
}
);
} }
@Override @Override
@@ -34,89 +32,3 @@ public class ProxySettingsFragment extends TrackedMaterialPreferenceToolbarFragm
return R.string.vtlproxy; return R.string.vtlproxy;
} }
} }
class ProxyChangeListener implements Preference.OnPreferenceChangeListener {
private final NetworkProxy networkProxy;
private final SwitchPreference switchPreference;
ProxyChangeListener(NetworkProxy networkProxy, SwitchPreference switchPreference) {
this.networkProxy = networkProxy;
this.switchPreference = switchPreference;
}
@Override
public boolean onPreferenceChange(Preference preference, Object obj) {
handleProxyChange(preference, (Boolean) obj);
return false;
}
private void handleProxyChange(Preference preference, Boolean isEnabled) {
if (!networkProxy.d() && isEnabled) {
enableProxy(preference);
} else {
disableProxy();
}
}
private void enableProxy(Preference preference) {
networkProxy.a(true);
VKProgressDialog vKProgressDialog = createProgressDialog(preference.getContext());
networkProxy.a((NetworkProxy.c) new ProxyPreferences(vKProgressDialog));
}
private void disableProxy() {
Network.l.b().a(false);
switchPreference.setChecked(networkProxy.d());
}
private VKProgressDialog createProgressDialog(Context context) {
VKProgressDialog vKProgressDialog = new VKProgressDialog(context);
vKProgressDialog.setMessage(context.getString(R.string.loading));
vKProgressDialog.setOnDismissListener(new OnDismissListener());
ViewUtils.b(vKProgressDialog);
return vKProgressDialog;
}
private class OnDismissListener implements DialogInterface.OnDismissListener {
@Override
public void onDismiss(DialogInterface dialogInterface) {
switchPreference.setChecked(networkProxy.d());
}
}
private class ProxyPreferences extends NetworkProxyPreferences {
private final VKProgressDialog progressDialog;
ProxyPreferences(VKProgressDialog vKProgressDialog) {
this.progressDialog = vKProgressDialog;
}
@Override
public void b(NetworkProxy.Reason reason) {
super.b(reason);
dismissProgressDialog();
switchPreference.setChecked(networkProxy.d());
showToast(reason);
}
@Override
public void e() {
super.e();
dismissProgressDialog();
switchPreference.setChecked(networkProxy.d());
}
private void dismissProgressDialog() {
ViewUtils.a(progressDialog);
}
private void showToast(NetworkProxy.Reason reason) {
if (reason == NetworkProxy.Reason.PROXY_NOT_AVAILABLE) {
ToastUtils.a(R.string.sett_proxy_not_available);
} else {
ToastUtils.a(R.string.sett_no_proxy);
}
}
}
}
@@ -6,10 +6,8 @@ import android.os.Bundle;
import ru.vtosters.hooks.other.Preferences; import ru.vtosters.hooks.other.Preferences;
import ru.vtosters.hooks.other.ThemesUtils; import ru.vtosters.hooks.other.ThemesUtils;
import ru.vtosters.lite.ui.PreferenceFragmentUtils; import ru.vtosters.lite.ui.PreferenceFragmentUtils;
import ru.vtosters.lite.utils.AndroidUtils;
import ru.vtosters.lite.utils.NetworkUtils; import ru.vtosters.lite.utils.NetworkUtils;
import ru.vtosters.sponsorpost.data.Filter; import ru.vtosters.sponsorpost.data.Filter;
import ru.vtosters.sponsorpost.internal.VotesPreferences;
import ru.vtosters.sponsorpost.services.FilterService; import ru.vtosters.sponsorpost.services.FilterService;
import ru.vtosters.sponsorpost.utils.FiltersPreferences; import ru.vtosters.sponsorpost.utils.FiltersPreferences;
import ru.vtosters.sponsorpost.utils.PostsPreferences; import ru.vtosters.sponsorpost.utils.PostsPreferences;
@@ -37,40 +35,6 @@ public class SponsorPostFragment extends TrackedMaterialPreferenceToolbarFragmen
null null
); );
PreferenceFragmentUtils.addPreferenceCategory(getPreferenceScreen(), "Статистика блокировок");
PreferenceFragmentUtils.addPreference(
getPreferenceScreen(),
"",
"SponsorPost",
"Заблокировано постов: " + PostsPreferences.getNumBlockedPosts(),
null,
null
);
PreferenceFragmentUtils.addPreference(
getPreferenceScreen(),
"",
"SponsorPost фильтры",
"Заблокировано постов: " + FiltersPreferences.getNumBlockedPosts(),
null,
null
);
PreferenceFragmentUtils.addPreference(
getPreferenceScreen(),
"",
"Сброс статистики",
null,
null,
preference -> {
FiltersPreferences.dropNumBlockedPosts();
PostsPreferences.dropNumBlockedPosts();
AndroidUtils.sendToast("Статистика сброшена");
return false;
}
);
PreferenceFragmentUtils.addPreferenceCategory(getPreferenceScreen(), "Sponsor Post"); PreferenceFragmentUtils.addPreferenceCategory(getPreferenceScreen(), "Sponsor Post");
if (Preferences.serverFeaturesDisable()) { if (Preferences.serverFeaturesDisable()) {
@@ -85,9 +49,9 @@ public class SponsorPostFragment extends TrackedMaterialPreferenceToolbarFragmen
} else { } else {
PreferenceFragmentUtils.addMaterialSwitchPreference( PreferenceFragmentUtils.addMaterialSwitchPreference(
getPreferenceScreen(), getPreferenceScreen(),
"", "sponsorpost",
"Фильтр постов", "Фильтр постов",
"Получать списки рекламных постов в группах и ленте, которые не блокируются рекламными фильтрами\n\nРекомендуется использовать совместно со Стандартным фильтром", "Получать списки рекламных постов в группах и ленте, которые не блокируются рекламными фильтрами",
null, null,
PostsPreferences.isEnabled(), PostsPreferences.isEnabled(),
(preference, o) -> { (preference, o) -> {
@@ -95,61 +59,6 @@ public class SponsorPostFragment extends TrackedMaterialPreferenceToolbarFragmen
return true; return true;
} }
); );
PreferenceFragmentUtils.addMaterialSwitchPreference(
getPreferenceScreen(),
"",
"Не скрывать посты",
"Помечать посты рекламной пометкой, но не скрывать их полностью",
null,
PostsPreferences.isEnabledMarking(),
(preference, o) -> {
PostsPreferences.setEnabledMarking((boolean) o);
return true;
}
).setEnabled(PostsPreferences.isEnabled());
PreferenceFragmentUtils.addMaterialSwitchPreference(
getPreferenceScreen(),
"",
"Предварительные списки",
"Получать дополнительно более свежие списки которые ещё не попали в основной список. Нужно для активного голосования для блокировки постов. Возможно много ошибок!",
null,
VotesPreferences.isEnabled(),
(preference, o) -> {
VotesPreferences.setEnabled((boolean) o);
return true;
}
).setEnabled(PostsPreferences.isEnabled());
if (NetworkUtils.isNetworkConnected()) {
PreferenceFragmentUtils.addPreference(
getPreferenceScreen(),
"",
"Обновить списки постов",
null,
null,
preference -> {
this.requireActivity().runOnUiThread(() -> {
Updates.updatePosts();
AndroidUtils.sendToast("Обновлено");
});
return false;
}
);
}
PreferenceFragmentUtils.addPreference(
getPreferenceScreen(),
"",
"Телеграм чат",
"Помогите улучшить качество блокировки постов в нашем телеграм чате\n\nДля перехода в чат вам необходима последняя версия Telegram",
null,
preference -> {
requireContext().startActivity(new Intent(Intent.ACTION_VIEW).setData(Uri.parse("https://t.me/vtosterschat/3559729")));
return false;
}
);
} }
requireActivity().runOnUiThread(() -> { requireActivity().runOnUiThread(() -> {
@@ -214,48 +123,6 @@ public class SponsorPostFragment extends TrackedMaterialPreferenceToolbarFragmen
); );
} }
PreferenceFragmentUtils.addMaterialSwitchPreference(
getPreferenceScreen(),
"",
"Не блокировать посты",
"Помечать посты заблокированные фильтрами",
null,
FiltersPreferences.isEnabledMarking(),
(preference, o) -> {
FiltersPreferences.setEnabledMarking((boolean) o);
return true;
}
);
if (NetworkUtils.isNetworkConnected()) {
PreferenceFragmentUtils.addPreference(
getPreferenceScreen(),
"",
"Обновить списки фильтров",
null,
null,
preference -> {
this.requireActivity().runOnUiThread(() -> {
FiltersPreferences.forceUpdateFilters();
AndroidUtils.sendToast("Обновлено");
});
return false;
}
);
}
PreferenceFragmentUtils.addPreference(
getPreferenceScreen(),
"",
"Телеграм чат",
"Помогите улучшить качество фильтров обсудив работу в нашем телеграм чате\n\nДля перехода в чат вам необходима последняя версия Telegram",
null,
preference -> {
requireContext().startActivity(new Intent(Intent.ACTION_VIEW).setData(Uri.parse("https://t.me/vtosterschat/3559732")));
return false;
}
);
PreferenceFragmentUtils.addPreference( PreferenceFragmentUtils.addPreference(
getPreferenceScreen(), getPreferenceScreen(),
"", "",
@@ -272,20 +139,6 @@ public class SponsorPostFragment extends TrackedMaterialPreferenceToolbarFragmen
} }
} }
); );
if (Preferences.dev()) {
PreferenceFragmentUtils.addPreference(
getPreferenceScreen(),
"",
"Сбросить токен пользователя",
null,
null,
preference -> {
VotesPreferences.remUserToken();
return false;
}
);
}
} }
@Override @Override
@@ -13,8 +13,6 @@ import ru.vtosters.lite.ui.PreferenceFragmentUtils;
import ru.vtosters.lite.utils.AndroidUtils; import ru.vtosters.lite.utils.AndroidUtils;
import ru.vtosters.lite.utils.VersionReader; import ru.vtosters.lite.utils.VersionReader;
import static ru.vtosters.hooks.GmsHook.isSpoofPackageInstalled;
public class SystemInfo extends TrackedMaterialPreferenceToolbarFragment { public class SystemInfo extends TrackedMaterialPreferenceToolbarFragment {
@Override @Override
@@ -48,11 +46,11 @@ public class SystemInfo extends TrackedMaterialPreferenceToolbarFragment {
boolean isZenUI = OEMDetector.isZenUI(); boolean isZenUI = OEMDetector.isZenUI();
boolean isEMUI = OEMDetector.isEMUI(); boolean isEMUI = OEMDetector.isEMUI();
boolean hasMiuiIncrCode = OEMDetector.hasMiuiIncrCode(); boolean hasMiuiIncrCode = OEMDetector.hasMiuiIncrCode();
boolean isHyperOS = OEMDetector.isHyperOs();
boolean hasGMS = GmsHook.isGmsInstalled(); boolean hasGMS = GmsHook.isGmsInstalled();
boolean hasVancedMicroG = GmsHook.isAnyServicesInstalled(); boolean hasVancedMicroG = GmsHook.isFakeGmsInstalled();
String GMSPackageName = hasGMS ? "com.google.android.gms" : hasVancedMicroG ? GmsHook.getCurrentGms() + ".android.gms" : "no gms or microg"; boolean hasReVancedMicroG = GmsHook.isFakeGms2Installed();
String GMSPackageName = hasGMS ? "com.google.android.gms" : hasVancedMicroG ? "com.mgoogle.android.gms" : hasReVancedMicroG ? "app.revanced.android.gms" : "no gms or microg";
boolean isValidSignature = Preferences.isValidSignature(); boolean isValidSignature = Preferences.isValidSignature();
boolean isTablet = AndroidUtils.isTablet(); boolean isTablet = AndroidUtils.isTablet();
@@ -106,7 +104,9 @@ public class SystemInfo extends TrackedMaterialPreferenceToolbarFragment {
PreferenceFragmentUtils.addPreference(getPreferenceScreen(), "", "isInstalledGoogleGMS", "Value: " + hasGMS, 0, null); PreferenceFragmentUtils.addPreference(getPreferenceScreen(), "", "isInstalledGoogleGMS", "Value: " + hasGMS, 0, null);
PreferenceFragmentUtils.addPreference(getPreferenceScreen(), "", "isInstalledMicroG", "Value: " + hasVancedMicroG, 0, null); PreferenceFragmentUtils.addPreference(getPreferenceScreen(), "", "isInstalledVancedMicroG", "Value: " + hasVancedMicroG, 0, null);
PreferenceFragmentUtils.addPreference(getPreferenceScreen(), "", "isInstalledReVancedMicroG", "Value: " + hasReVancedMicroG, 0, null);
PreferenceFragmentUtils.addPreference(getPreferenceScreen(), "", "Product Name", productName, 0, preference -> { PreferenceFragmentUtils.addPreference(getPreferenceScreen(), "", "Product Name", productName, 0, preference -> {
((ClipboardManager) requireActivity().getSystemService(Context.CLIPBOARD_SERVICE)).setPrimaryClip(ClipData.newPlainText("MBH-ST", productName)); ((ClipboardManager) requireActivity().getSystemService(Context.CLIPBOARD_SERVICE)).setPrimaryClip(ClipData.newPlainText("MBH-ST", productName));
@@ -194,8 +194,6 @@ public class SystemInfo extends TrackedMaterialPreferenceToolbarFragment {
PreferenceFragmentUtils.addPreference(getPreferenceScreen(), "", "isMiui", "Value: " + isMiui, 0, null); PreferenceFragmentUtils.addPreference(getPreferenceScreen(), "", "isMiui", "Value: " + isMiui, 0, null);
PreferenceFragmentUtils.addPreference(getPreferenceScreen(), "", "isHyperOS", "Value: " + isHyperOS, 0, null);
PreferenceFragmentUtils.addPreference(getPreferenceScreen(), "", "isEMUI", "Value: " + isEMUI, 0, null); PreferenceFragmentUtils.addPreference(getPreferenceScreen(), "", "isEMUI", "Value: " + isEMUI, 0, null);
PreferenceFragmentUtils.addPreference(getPreferenceScreen(), "", "isFlyme", "Value: " + isFlyme, 0, null); PreferenceFragmentUtils.addPreference(getPreferenceScreen(), "", "isFlyme", "Value: " + isFlyme, 0, null);
@@ -116,6 +116,17 @@ public class ThemesFragment extends TrackedMaterialPreferenceToolbarFragment {
return true; return true;
}); });
var dockbarEditor = findPreference("dockbareditor");
dockbarEditor.setSummary(AndroidUtils.getString(R.string.vtldocksumm) + ": " + DockBarEditorManager.getInstance().getSelectedTabs().size());
dockbarEditor.setOnPreferenceClickListener(preference -> {
NavigatorUtils.switchFragment(requireContext(), DockBarEditorFragment.class);
return true;
});
if (Preferences.vkme() || AndroidUtils.isTablet()) {
dockbarEditor.setVisible(false);
}
if (AndroidUtils.isTablet()) { if (AndroidUtils.isTablet()) {
PreferenceCategory dockbarSettingsPreferenceCategory = (PreferenceCategory) findPreference("dockbarsett"); PreferenceCategory dockbarSettingsPreferenceCategory = (PreferenceCategory) findPreference("dockbarsett");
dockbarSettingsPreferenceCategory.setVisible(false); dockbarSettingsPreferenceCategory.setVisible(false);
@@ -42,11 +42,9 @@ import ru.vtosters.hooks.ui.SystemThemeChangerHook;
import ru.vtosters.lite.BuildConfig; import ru.vtosters.lite.BuildConfig;
import ru.vtosters.lite.concurrent.VTExecutors; import ru.vtosters.lite.concurrent.VTExecutors;
import ru.vtosters.lite.deviceinfo.OEMDetector; import ru.vtosters.lite.deviceinfo.OEMDetector;
import ru.vtosters.lite.proxy.ProxyUtils;
import ru.vtosters.lite.ssfs.Utils; import ru.vtosters.lite.ssfs.Utils;
import ru.vtosters.lite.themes.utils.RecolorUtils; import ru.vtosters.lite.themes.utils.RecolorUtils;
import ru.vtosters.lite.ui.PreferenceFragmentUtils; import ru.vtosters.lite.ui.PreferenceFragmentUtils;
import ru.vtosters.lite.ui.components.DockBarEditorManager;
import ru.vtosters.lite.ui.dialogs.OTADialog; import ru.vtosters.lite.ui.dialogs.OTADialog;
import ru.vtosters.lite.ui.fragments.tgstickers.StickersFragment; import ru.vtosters.lite.ui.fragments.tgstickers.StickersFragment;
import ru.vtosters.lite.utils.*; import ru.vtosters.lite.utils.*;
@@ -79,13 +77,9 @@ public class VTSettings extends TrackedMaterialPreferenceToolbarFragment {
public static String getProxysumm() { public static String getProxysumm() {
String type = Preferences.getString("proxy"); String type = Preferences.getString("proxy");
boolean isVKProxy = ProxyUtils.isVKProxyEnabled();
if (isVKProxy) { if (type.equals("noproxy") || type.isEmpty())
type = "Встроенный";
} else if (type.equals("noproxy") || type.isEmpty()) {
type = AndroidUtils.getString(R.string.vtlsettdisabled); type = AndroidUtils.getString(R.string.vtlsettdisabled);
}
return AndroidUtils.getString(R.string.vtlproxysumm) + ": " + type; return AndroidUtils.getString(R.string.vtlproxysumm) + ": " + type;
} }
@@ -108,7 +102,7 @@ public class VTSettings extends TrackedMaterialPreferenceToolbarFragment {
boolean isLinksUnverified = AndroidUtils.isLinksUnverified(requireActivity()); boolean isLinksUnverified = AndroidUtils.isLinksUnverified(requireActivity());
boolean isDozingAvailable = Build.VERSION.SDK_INT >= 23 && !manager.isIgnoringBatteryOptimizations(AndroidUtils.getPackageName()); boolean isDozingAvailable = Build.VERSION.SDK_INT >= 23 && !manager.isIgnoringBatteryOptimizations(AndroidUtils.getPackageName());
boolean areNotificationsDisabled = !AndroidUtils.areNotificationsEnabled() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O; boolean areNotificationsDisabled = !AndroidUtils.areNotificationsEnabled() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
boolean isGMSNotInstalled = !GmsHook.isAnyServicesInstalled() && !GmsHook.isGmsInstalled(); boolean isGMSNotInstalled = !GmsHook.isAnyServicesInstalled();
if (AccountManagerUtils.isLogin()) { if (AccountManagerUtils.isLogin()) {
Preference accountSwitcher = PreferenceFragmentUtils.addPreference( Preference accountSwitcher = PreferenceFragmentUtils.addPreference(
@@ -472,18 +466,6 @@ public class VTSettings extends TrackedMaterialPreferenceToolbarFragment {
} }
); );
PreferenceFragmentUtils.addPreference(
getPreferenceScreen(),
"",
requireContext().getString(R.string.music),
getValAsString(R.string.useGenius_title, Preferences.getBoolValue("useGenius", false)),
R.drawable.ic_music_outline_28,
preference -> {
NavigatorUtils.switchFragment(requireContext(), MusicFragment.class);
return false;
}
);
PreferenceFragmentUtils.addPreference( PreferenceFragmentUtils.addPreference(
getPreferenceScreen(), getPreferenceScreen(),
"", "",
@@ -534,20 +516,6 @@ public class VTSettings extends TrackedMaterialPreferenceToolbarFragment {
} }
); );
if (!Preferences.vkme() && !AndroidUtils.isTablet()) {
PreferenceFragmentUtils.addPreference(
getPreferenceScreen(),
"",
requireContext().getString(R.string.dockbar_editor),
requireContext().getString(R.string.vtldocksumm) + ": " + DockBarEditorManager.getInstance().getSelectedTabs().size(),
R.drawable.ic_pin_outline_28,
preference -> {
NavigatorUtils.switchFragment(requireContext(), DockBarEditorFragment.class);
return false;
}
);
}
PreferenceFragmentUtils.addPreference( PreferenceFragmentUtils.addPreference(
getPreferenceScreen(), getPreferenceScreen(),
"currcache", "currcache",
@@ -584,7 +552,7 @@ public class VTSettings extends TrackedMaterialPreferenceToolbarFragment {
} }
); );
if (Build.VERSION.SDK_INT >= 33 && (!OEMDetector.isMIUI() || OEMDetector.isHyperOs())) { if (Build.VERSION.SDK_INT >= 33 && !OEMDetector.isMIUI()) {
PreferenceFragmentUtils.addPreference( PreferenceFragmentUtils.addPreference(
getPreferenceScreen(), getPreferenceScreen(),
"", "",
@@ -14,12 +14,10 @@ import com.vtosters.lite.api.ExtendedCommunityProfile;
import com.vtosters.lite.api.ExtendedUserProfile; import com.vtosters.lite.api.ExtendedUserProfile;
import kotlin.Unit; import kotlin.Unit;
import kotlin.jvm.b.Functions; import kotlin.jvm.b.Functions;
import ru.vtosters.hooks.RenameHook;
import ru.vtosters.hooks.other.Preferences; import ru.vtosters.hooks.other.Preferences;
import ru.vtosters.lite.foaf.FoafBase; import ru.vtosters.lite.foaf.FoafBase;
import ru.vtosters.lite.utils.AccountManagerUtils; import ru.vtosters.lite.utils.AccountManagerUtils;
import ru.vtosters.lite.utils.AndroidUtils; import ru.vtosters.lite.utils.AndroidUtils;
import ru.vtosters.lite.utils.RenameTool;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
@@ -68,12 +66,10 @@ public class MenuBuilder {
return Unit.a; return Unit.a;
}); });
if (RenameHook.isEnabled()) { // addItem(builder, AndroidUtils.getString(R.string.menu_change_name), () -> {
addItem(builder, AndroidUtils.getString(R.string.menu_change_name), () -> { // RenameTool.createDialog(eup, ctx);
RenameTool.createDialog(eup, ctx); // return Unit.a;
return Unit.a; // });
});
}
addItem(builder, AndroidUtils.getString(isPageWhitelistedFilter ? R.string.remove_from_filter_whitelist : R.string.add_to_filter_whitelist), () -> { addItem(builder, AndroidUtils.getString(isPageWhitelistedFilter ? R.string.remove_from_filter_whitelist : R.string.add_to_filter_whitelist), () -> {
setWhitelistedFilter(eup, !isPageWhitelistedFilter); setWhitelistedFilter(eup, !isPageWhitelistedFilter);
@@ -87,7 +83,7 @@ public class MenuBuilder {
return Unit.a; return Unit.a;
}); });
} catch (IllegalAccessException | InvocationTargetException e) { } catch (IllegalAccessException | InvocationTargetException e) {
e.fillInStackTrace(); e.printStackTrace();
} }
} }
@@ -104,12 +100,10 @@ public class MenuBuilder {
return Unit.a; return Unit.a;
}); });
if (RenameHook.isEnabled()) { // addItem(builder, AndroidUtils.getString(R.string.menu_change_name), () -> {
addItem(builder, AndroidUtils.getString(R.string.menu_change_name), () -> { // RenameTool.createDialogGroup(ecp, ctx);
RenameTool.createDialogGroup(ecp, ctx); // return Unit.a;
return Unit.a; // });
});
}
addItem(builder, AndroidUtils.getString(isPageWhitelistedFilter ? R.string.remove_from_filter_whitelist : R.string.add_to_filter_whitelist), () -> { addItem(builder, AndroidUtils.getString(isPageWhitelistedFilter ? R.string.remove_from_filter_whitelist : R.string.add_to_filter_whitelist), () -> {
setWhitelistedFilter(ecp, !isPageWhitelistedFilter); setWhitelistedFilter(ecp, !isPageWhitelistedFilter);
@@ -129,7 +123,7 @@ public class MenuBuilder {
return Unit.a; return Unit.a;
}); });
} catch (IllegalAccessException | InvocationTargetException e) { } catch (IllegalAccessException | InvocationTargetException e) {
e.fillInStackTrace(); e.printStackTrace();
} }
} }
@@ -17,8 +17,6 @@ import android.util.Log;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.StringRes; import androidx.annotation.StringRes;
import androidx.core.app.NotificationManagerCompat; import androidx.core.app.NotificationManagerCompat;
import com.vk.core.util.AppContextHolder;
import com.vk.core.util.ToastUtils; import com.vk.core.util.ToastUtils;
import com.vtosters.lite.general.fragments.WebViewFragment; import com.vtosters.lite.general.fragments.WebViewFragment;
@@ -52,8 +50,14 @@ public class AndroidUtils {
@NonNull @NonNull
public static Context getGlobalContext() { public static Context getGlobalContext() {
return AppContextHolder.a; try {
} // Getting the global context to use context on application initialization 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");
}
return LifecycleUtils.getCurrentActivity();
} // Getting the global context through reflection to use context on application initialization
public static Resources getResources() { public static Resources getResources() {
return getGlobalContext().getResources(); return getGlobalContext().getResources();

Some files were not shown because too many files have changed in this diff Show More