Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8d41d423f8 | ||
|
|
37a4ba4462 | ||
|
|
7fba308f99 | ||
|
|
5ef2f2f6cd |
@@ -28,7 +28,7 @@ jobs:
|
|||||||
uses: nttld/setup-ndk@v1
|
uses: nttld/setup-ndk@v1
|
||||||
with:
|
with:
|
||||||
ndk-version: r25c
|
ndk-version: r25c
|
||||||
add-to-path: true
|
add-to-path: false
|
||||||
|
|
||||||
- name: Set up JDK
|
- name: Set up JDK
|
||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v3
|
||||||
|
|||||||
+2
-4
@@ -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'
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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.
@@ -4,8 +4,10 @@
|
|||||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
|
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
|
||||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
|
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||||
|
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" android:maxSdkVersion="22"/>
|
||||||
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
|
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.GET_ACCOUNTS" android:maxSdkVersion="22"/>
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
|||||||
+124
@@ -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 "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+74
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,200 @@
|
|||||||
|
package com.vk.contacts;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.ContextThemeWrapper;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.CheckBox;
|
||||||
|
import android.widget.CompoundButton;
|
||||||
|
import android.widget.RadioButton;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import com.vk.core.dialogs.alert.VkAlertDialog;
|
||||||
|
import com.vk.core.ui.themes.VKThemeHelper;
|
||||||
|
import com.vk.core.util.ContextExtKt;
|
||||||
|
import com.vk.navigation.NavigatorKeys;
|
||||||
|
import com.vk.permission.PermissionHelper;
|
||||||
|
import com.vtosters.lite.R;
|
||||||
|
import com.vtosters.lite.VKActivity;
|
||||||
|
import com.vtosters.lite.activities.LogoutReceiver;
|
||||||
|
import java.util.List;
|
||||||
|
import kotlin.Unit;
|
||||||
|
import kotlin.jvm.b.Functions;
|
||||||
|
import kotlin.jvm.b.Functions2;
|
||||||
|
|
||||||
|
public class ContactsSyncAcitvity extends VKActivity implements View.OnClickListener {
|
||||||
|
private boolean I;
|
||||||
|
private View J;
|
||||||
|
private RadioButton K;
|
||||||
|
private RadioButton L;
|
||||||
|
private RadioButton M;
|
||||||
|
@Nullable
|
||||||
|
private String G = null;
|
||||||
|
private int H = 2;
|
||||||
|
private LogoutReceiver N = null;
|
||||||
|
|
||||||
|
public static void a(Context context) {
|
||||||
|
b(context, ContactsSyncUtils.b());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void b(Context context, int i) {
|
||||||
|
a(context, i, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void f(int i) {
|
||||||
|
this.H = i;
|
||||||
|
this.K.setChecked(i == 0);
|
||||||
|
this.L.setChecked(i == 1);
|
||||||
|
this.M.setChecked(i == 2);
|
||||||
|
CheckBox checkBox = this.J.findViewById(R.id.sync_big_pictures);
|
||||||
|
checkBox.setEnabled(i != 2);
|
||||||
|
if (i == 2) {
|
||||||
|
checkBox.setChecked(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onDismiss() {
|
||||||
|
ContactsSyncReporter.a.a(ContactsSyncUtils.b(), this.G);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void x1() {
|
||||||
|
this.H = getIntent().getIntExtra("force_select_sync_contacts", ContactsSyncUtils.b());
|
||||||
|
this.I = ContactsSyncUtils.d();
|
||||||
|
|
||||||
|
this.K = this.J.findViewById(R.id.welcome_sync_opt1);
|
||||||
|
this.L = this.J.findViewById(R.id.welcome_sync_opt2);
|
||||||
|
this.M = this.J.findViewById(R.id.welcome_sync_opt3);
|
||||||
|
|
||||||
|
this.K.setChecked(this.H == 0);
|
||||||
|
this.L.setChecked(this.H == 1);
|
||||||
|
this.M.setChecked(this.H == 2);
|
||||||
|
|
||||||
|
this.J.findViewById(R.id.welcome_sync1).setOnClickListener(this);
|
||||||
|
this.J.findViewById(R.id.welcome_sync2).setOnClickListener(this);
|
||||||
|
this.J.findViewById(R.id.welcome_sync3).setOnClickListener(this);
|
||||||
|
|
||||||
|
CheckBox checkBox = this.J.findViewById(R.id.sync_big_pictures);
|
||||||
|
checkBox.setChecked(this.I);
|
||||||
|
checkBox.setEnabled(this.H != 2);
|
||||||
|
checkBox.setOnCheckedChangeListener(ContactsSyncAcitvity.this::a);
|
||||||
|
|
||||||
|
VkAlertDialog.Builder builder = new VkAlertDialog.Builder(this);
|
||||||
|
builder.setTitle(R.string.sett_sync);
|
||||||
|
builder.setView(this.J);
|
||||||
|
builder.setCancelable(false);
|
||||||
|
builder.setPositiveButton(R.string.save, ContactsSyncAcitvity.this::a);
|
||||||
|
builder.setNegativeButton(R.string.cancel, ContactsSyncAcitvity.this::b);
|
||||||
|
builder.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Unit c(List list) {
|
||||||
|
ContactsSyncReporter.a.a(this.G);
|
||||||
|
finish();
|
||||||
|
return Unit.a;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("NonConstantResourceId")
|
||||||
|
@Override // android.view.View.OnClickListener
|
||||||
|
public void onClick(View view) {
|
||||||
|
switch (view.getId()) {
|
||||||
|
case R.id.welcome_sync1 /* 2131365835 */:
|
||||||
|
f(0);
|
||||||
|
return;
|
||||||
|
case R.id.welcome_sync2 /* 2131365836 */:
|
||||||
|
f(1);
|
||||||
|
return;
|
||||||
|
case R.id.welcome_sync3 /* 2131365837 */:
|
||||||
|
f(2);
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // com.vtosters.lite.VKActivity, com.vk.core.ui.themes.ThemableActivity, androidx.appcompat.app.AppCompatActivity, androidx.fragment.app.FragmentActivity, androidx.core.app.ComponentActivity, android.app.Activity
|
||||||
|
public void onCreate(Bundle bundle) {
|
||||||
|
setTheme(VKThemeHelper.n());
|
||||||
|
super.onCreate(bundle);
|
||||||
|
this.G = getIntent().getStringExtra(NavigatorKeys.l0);
|
||||||
|
this.N = LogoutReceiver.a(this);
|
||||||
|
overridePendingTransition(0, 0);
|
||||||
|
getWindow().setBackgroundDrawableResource(R.drawable.transparent);
|
||||||
|
setContentView(new View(this));
|
||||||
|
this.J = View.inflate(new ContextThemeWrapper(this, VKThemeHelper.n()), R.layout.welcome, null);
|
||||||
|
VKThemeHelper.d(this);
|
||||||
|
PermissionHelper permissionHelper = PermissionHelper.r;
|
||||||
|
permissionHelper.a(this, permissionHelper.e(), R.string.permissions_contacts_sync, R.string.permissions_contacts_sync_settings, new Functions() { // from class: com.vk.contacts.c
|
||||||
|
@Override // kotlin.jvm.b.Functions
|
||||||
|
public Object invoke() {
|
||||||
|
return ContactsSyncAcitvity.this.w1();
|
||||||
|
}
|
||||||
|
}, new Functions2() { // from class: com.vk.contacts.a
|
||||||
|
@Override // kotlin.jvm.b.Functions2
|
||||||
|
public Object invoke(Object obj) {
|
||||||
|
return ContactsSyncAcitvity.this.c((List) obj);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // com.vtosters.lite.VKActivity, androidx.appcompat.app.AppCompatActivity, androidx.fragment.app.FragmentActivity, android.app.Activity
|
||||||
|
public void onDestroy() {
|
||||||
|
this.N.a();
|
||||||
|
super.onDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // android.app.Activity
|
||||||
|
protected void onRestoreInstanceState(Bundle bundle) {
|
||||||
|
super.onRestoreInstanceState(bundle);
|
||||||
|
this.H = bundle.getInt("state_option", 2);
|
||||||
|
this.I = bundle.getBoolean("state_sync_hq_photo", false);
|
||||||
|
f(this.H);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // androidx.appcompat.app.AppCompatActivity, androidx.fragment.app.FragmentActivity, androidx.core.app.ComponentActivity, android.app.Activity
|
||||||
|
public void onSaveInstanceState(Bundle bundle) {
|
||||||
|
super.onSaveInstanceState(bundle);
|
||||||
|
bundle.putInt("state_option", this.H);
|
||||||
|
bundle.putBoolean("state_sync_hq_photo", this.I);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Unit w1() {
|
||||||
|
x1();
|
||||||
|
return Unit.a;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void a(Context context, int i, @Nullable String str) {
|
||||||
|
Intent intent = new Intent(context, ContactsSyncAcitvity.class);
|
||||||
|
intent.setAction("syncsettings");
|
||||||
|
intent.putExtra("force_select_sync_contacts", i);
|
||||||
|
intent.putExtra(NavigatorKeys.l0, str);
|
||||||
|
if (ContextExtKt.e(context) == null) {
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
}
|
||||||
|
context.startActivity(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void b(DialogInterface dialogInterface, int i) {
|
||||||
|
onDismiss();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void a(CompoundButton compoundButton, boolean z) {
|
||||||
|
this.I = z;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void a(DialogInterface dialogInterface, int i) {
|
||||||
|
ContactsSyncUtils.b(this.H);
|
||||||
|
ContactsSyncUtils.a(this.I);
|
||||||
|
Intent intent = new Intent();
|
||||||
|
intent.putExtra("option", this.H);
|
||||||
|
setResult(-1, intent);
|
||||||
|
onDismiss();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void a(DialogInterface dialogInterface) {
|
||||||
|
onDismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package com.vk.contacts;
|
||||||
|
|
||||||
|
import com.vk.metrics.eventtracking.Event;
|
||||||
|
import com.vk.metrics.eventtracking.VkTracker;
|
||||||
|
import com.vk.navigation.NavigatorKeys;
|
||||||
|
|
||||||
|
|
||||||
|
public final class ContactsSyncReporter {
|
||||||
|
public static final ContactsSyncReporter a = new ContactsSyncReporter();
|
||||||
|
private static final String EVENT_NAME = "LoggingTracker";
|
||||||
|
private static final String ENABLE_CONTACTS_SYNC = "enable_contacts_sync";
|
||||||
|
private static final String HAS_CONTACTS_PERMISSION = "has_contacts_permission";
|
||||||
|
private static final String SELECTED_SYNC_OPTION = "selected_sync_option";
|
||||||
|
private static final String NAVIGATOR_KEYS_L0 = NavigatorKeys.l0;
|
||||||
|
|
||||||
|
private ContactsSyncReporter() {
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Event createEvent(boolean z, int i, String str) {
|
||||||
|
Event.a a2 = Event.b.a();
|
||||||
|
a2.a(EVENT_NAME);
|
||||||
|
a2.a(ENABLE_CONTACTS_SYNC);
|
||||||
|
a2.a(HAS_CONTACTS_PERMISSION, z);
|
||||||
|
a2.a(SELECTED_SYNC_OPTION, i != 0 ? (i != 1 ? "disabled" : "contacts") : "all");
|
||||||
|
if (str == null) {
|
||||||
|
str = "";
|
||||||
|
}
|
||||||
|
a2.a(NAVIGATOR_KEYS_L0, str);
|
||||||
|
return a2.a();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void a(String str) {
|
||||||
|
a(false, 2, str);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void a(int i, String str) {
|
||||||
|
a(true, i, str);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void a(boolean z, int i, String str) {
|
||||||
|
VkTracker vkTracker = VkTracker.k;
|
||||||
|
Event event = createEvent(z, i, str);
|
||||||
|
vkTracker.a(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
package com.vk.contacts;
|
||||||
|
|
||||||
|
import android.accounts.Account;
|
||||||
|
import android.accounts.AccountManager;
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
|
import com.vk.auth.VKAuthUtils;
|
||||||
|
import com.vk.core.concurrent.VkExecutors;
|
||||||
|
import com.vk.core.preference.Preference;
|
||||||
|
import com.vk.core.util.AppContextHolder;
|
||||||
|
import com.vk.metrics.eventtracking.VkTracker;
|
||||||
|
import com.vtosters.lite.auth.VKAccountManager;
|
||||||
|
|
||||||
|
public final class ContactsSyncUtils {
|
||||||
|
private static final SharedPreferences prefs;
|
||||||
|
|
||||||
|
static {
|
||||||
|
prefs = Preference.b();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void a(boolean z) { // setSyncHqPhotos
|
||||||
|
try {
|
||||||
|
prefs.edit().putBoolean("sync_hq_photos", z).apply();
|
||||||
|
Log.d("Contacts", "setSyncHqPhotos");
|
||||||
|
} catch (Exception e) {
|
||||||
|
VkTracker.k.b(e);
|
||||||
|
e.fillInStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void b(int i) { // setSyncOption
|
||||||
|
try {
|
||||||
|
prefs.edit().putInt("key_sync_option", i).apply();
|
||||||
|
syncAccount(i);
|
||||||
|
Log.d("Contacts", "setSyncOption");
|
||||||
|
} catch (Exception e) {
|
||||||
|
VkTracker.k.b(e);
|
||||||
|
e.fillInStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean d() { // getSyncHqPhotos
|
||||||
|
return prefs.getBoolean("sync_hq_photos", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void e() { // syncAccount
|
||||||
|
syncAccount(b());
|
||||||
|
Log.d("Contacts", "syncAccount");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int b() { // getSyncOption
|
||||||
|
Account[] accounts;
|
||||||
|
try {
|
||||||
|
AccountManager accountManager = AccountManager.get(AppContextHolder.a);
|
||||||
|
accounts = accountManager.getAccountsByType("com.vkontakte.account");
|
||||||
|
if (accounts.length == 0) {
|
||||||
|
Account account = new Account(VKAccountManager.d().Z(), "com.vkontakte.account");
|
||||||
|
try {
|
||||||
|
accountManager.addAccountExplicitly(account, null, null);
|
||||||
|
} catch (Exception e) {
|
||||||
|
VkTracker.k.a(e);
|
||||||
|
e.fillInStackTrace();
|
||||||
|
}
|
||||||
|
accounts = new Account[]{account};
|
||||||
|
}
|
||||||
|
if (ContentResolver.getSyncAutomatically(accounts[0], "com.android.contacts")) {
|
||||||
|
if (prefs.contains("key_sync_option")) {
|
||||||
|
return prefs.getInt("key_sync_option", 2);
|
||||||
|
}
|
||||||
|
if (prefs.contains("sync_all")) {
|
||||||
|
return prefs.getBoolean("sync_all", false) ? 0 : 1;
|
||||||
|
}
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
return 2;
|
||||||
|
} catch (Exception e2) {
|
||||||
|
VkTracker.k.b(e2);
|
||||||
|
e2.fillInStackTrace();
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void syncAccount(int i) {
|
||||||
|
Log.d("Contacts", String.valueOf(i));
|
||||||
|
if (!VKAccountManager.d().b1()) {
|
||||||
|
syncAllAccounts();
|
||||||
|
Log.d("Contacts", "VKAccountManager.d().b1() is not false");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Account account = VKAuthUtils.a.a(VKAccountManager.d().Z());
|
||||||
|
if (i != 2 && account != null) {
|
||||||
|
syncSingleAccount(account);
|
||||||
|
Log.d("Contacts", "sync single account, account is " + account);
|
||||||
|
} else {
|
||||||
|
syncAllAccounts();
|
||||||
|
Log.d("Contacts", "sync all accounts, account is " + account);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void syncSingleAccount(Account account) {
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.putBoolean("com.vtosters.lite.EXTRA_FORCE_KEY", true);
|
||||||
|
ContentResolver.requestSync(account, "com.android.contacts", bundle);
|
||||||
|
VkExecutors.x.l().submit(() -> ContactsSyncAdapterService.a(AppContextHolder.a, account, new Bundle()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void syncAllAccounts() {
|
||||||
|
VkExecutors.x.l().submit(() -> ContactsSyncAdapterService.a(AppContextHolder.a.getContentResolver()));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import android.util.Log;
|
|||||||
import androidx.annotation.Keep;
|
import androidx.annotation.Keep;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import com.facebook.soloader.SoLoader;
|
import com.facebook.soloader.SoLoader;
|
||||||
|
import ru.vtosters.sponsorpost.internal.Native;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
@@ -248,6 +249,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");
|
||||||
}
|
}
|
||||||
@@ -273,6 +275,7 @@ public final class MediaNative {
|
|||||||
SoLoader.init(context2, false);
|
SoLoader.init(context2, false);
|
||||||
NativeLibLoader.loadLibrary("gnustl_shared");
|
NativeLibLoader.loadLibrary("gnustl_shared");
|
||||||
NativeLibLoader.loadLibrary("vkchronicle");
|
NativeLibLoader.loadLibrary("vkchronicle");
|
||||||
|
Native.canVote = NativeLibLoader.loadLibrary("sponsorpost");
|
||||||
loadVKMediaLibs();
|
loadVKMediaLibs();
|
||||||
f17443d.notifyAll();
|
f17443d.notifyAll();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
||||||
|
|
||||||
@@ -157,4 +157,4 @@ public class DeletedMessagesHook {
|
|||||||
return msgIdsList;
|
return msgIdsList;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,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)
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
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.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.Call;
|
||||||
import okhttp3.Callback;
|
import okhttp3.Callback;
|
||||||
@@ -13,6 +10,8 @@ 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.proxy.ProxyUtils;
|
||||||
|
import ru.vtosters.lite.utils.AccountManagerUtils;
|
||||||
import ru.vtosters.lite.utils.AndroidUtils;
|
import ru.vtosters.lite.utils.AndroidUtils;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -147,36 +146,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 +188,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;
|
||||||
|
|||||||
@@ -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,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;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,24 +134,20 @@ 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.add_to_cache) {
|
||||||
if (actionId == R.id.remove_from_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;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (actionId == R.id.remove_from_cache_vkx) {
|
if (actionId == R.id.remove_from_cache_vkx) {
|
||||||
getInstance().runOnService(service -> {
|
getInstance().runOnService(service -> {
|
||||||
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 {
|
||||||
@@ -278,4 +258,4 @@ public class MusicBottomSheetHook {
|
|||||||
false
|
false
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,20 +4,19 @@ 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.Request;
|
import okhttp3.Request;
|
||||||
import okhttp3.Response;
|
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.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 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 com.vk.core.network.Network.ClientType.CLIENT_API;
|
||||||
import static ru.vtosters.hooks.DateHook.getLocale;
|
import static ru.vtosters.hooks.DateHook.getLocale;
|
||||||
@@ -41,15 +40,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 +51,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 +89,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 +97,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 +130,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 +217,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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -285,9 +263,7 @@ public class CatalogJsonInjector {
|
|||||||
+ "&access_token="
|
+ "&access_token="
|
||||||
+ AccountManagerUtils.getUserToken())
|
+ AccountManagerUtils.getUserToken())
|
||||||
.a("Accept-Encoding", "gzip")
|
.a("Accept-Encoding", "gzip")
|
||||||
.a("User-Agent", Network.l.c().a())
|
.a(Headers.a("User-Agent", Network.l.c().a(), "Content-Type", "application/x-www-form-urlencoded; charset=utf-8")).a();
|
||||||
.a("Content-Type", "application/x-www-form-urlencoded; charset=utf-8")
|
|
||||||
.a();
|
|
||||||
try (Response resp = Network.b(CLIENT_API).a(request).execute()) {
|
try (Response resp = Network.b(CLIENT_API).a(request).execute()) {
|
||||||
var json = new JSONObject(GzipDecompressor.decompressResponse(resp)).getJSONObject("response");
|
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);
|
||||||
|
|||||||
@@ -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,8 +6,6 @@ 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;
|
||||||
@@ -24,8 +22,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);
|
||||||
@@ -100,8 +97,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 +289,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);
|
||||||
}
|
}
|
||||||
@@ -363,14 +356,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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,10 @@ import android.os.Environment;
|
|||||||
import com.vk.core.network.Network;
|
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 okhttp3.OkHttpClient;
|
||||||
|
import okhttp3.Request;
|
||||||
|
import okhttp3.Response;
|
||||||
|
import okhttp3.ResponseBody;
|
||||||
import ru.vtosters.lite.utils.AndroidUtils;
|
import ru.vtosters.lite.utils.AndroidUtils;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -38,43 +41,45 @@ public class StoryDownloader {
|
|||||||
.b(url)
|
.b(url)
|
||||||
.a();
|
.a();
|
||||||
|
|
||||||
client.a(request).a(new Callback() {
|
try (Response response = client.a(request).execute()) {
|
||||||
@Override
|
if (!response.h()) {
|
||||||
public void a(Call call, IOException e) {
|
throw new IOException("Unexpected code " + response);
|
||||||
|
}
|
||||||
|
|
||||||
|
ResponseBody responseBody = response.a();
|
||||||
|
|
||||||
|
if (responseBody == null) {
|
||||||
|
throw new IOException("No response body");
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
e.fillInStackTrace();
|
||||||
}
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
@Override
|
e.fillInStackTrace();
|
||||||
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,8 @@
|
|||||||
package ru.vtosters.lite.downloaders;
|
package ru.vtosters.lite.downloaders;
|
||||||
|
|
||||||
import android.app.NotificationChannel;
|
|
||||||
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.media.MediaScannerConnection;
|
||||||
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,7 +10,6 @@ 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.network.Network;
|
||||||
@@ -44,9 +40,6 @@ import static ru.vtosters.lite.utils.AccountManagerUtils.getUserToken;
|
|||||||
public class VideoDownloader {
|
public class VideoDownloader {
|
||||||
public static int DOWNLOAD_ID = 0;
|
public static int DOWNLOAD_ID = 0;
|
||||||
public static int OPEN_EXTERNAL_LINK_ID = 1;
|
public static 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]));
|
||||||
@@ -81,86 +74,46 @@ public class VideoDownloader {
|
|||||||
.setAdapter(adapter, (dialog, which) -> {
|
.setAdapter(adapter, (dialog, which) -> {
|
||||||
String url = urls.get(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);;
|
OkHttpClient client = Network.b(Network.ClientType.CLIENT_IMAGE_LOADER);;
|
||||||
Request request = new Request.a()
|
Request request = new Request.a()
|
||||||
.b(url)
|
.b(url)
|
||||||
.a();
|
.a();
|
||||||
|
|
||||||
client.a(request).a(new Callback() {
|
try (Response response = client.a(request).execute()) {
|
||||||
@Override
|
if (!response.h()) {
|
||||||
public void a(Call call, IOException e) {
|
throw new IOException("Unexpected code " + 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;
|
||||||
|
|
||||||
|
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
||||||
|
outputStream.write(buffer, 0, bytesRead);
|
||||||
|
}
|
||||||
|
outputStream.flush();
|
||||||
|
|
||||||
|
MediaScannerConnection.scanFile(finalContext, new String[]{outputFile.toString()}, null, null);
|
||||||
|
|
||||||
|
AndroidUtils.sendToast("Файл скачан в Movies/VKVideo");
|
||||||
|
} catch (IOException e) {
|
||||||
e.fillInStackTrace();
|
e.fillInStackTrace();
|
||||||
}
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
@Override
|
throw new RuntimeException(e);
|
||||||
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();
|
.show();
|
||||||
}
|
}
|
||||||
@@ -288,4 +241,4 @@ public class VideoDownloader {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,25 +2,22 @@ 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.music.downloader.M3UDownloader;
|
||||||
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 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";
|
||||||
private static final String URL = "https://api.genius.com";
|
private static final String URL = "https://api.genius.com";
|
||||||
@@ -28,7 +25,7 @@ public class Genius {
|
|||||||
|
|
||||||
public static String getTextMusic(MusicTrack musictrack) {
|
public static String getTextMusic(MusicTrack musictrack) {
|
||||||
String uid = musictrack.y1();
|
String uid = musictrack.y1();
|
||||||
String artist = removeExtras(MusicTrackUtils.getArtists(musictrack));
|
String artist = removeExtras(M3UDownloader.getArtists(musictrack));
|
||||||
String title = removeExtras(musictrack.f);
|
String title = removeExtras(musictrack.f);
|
||||||
int duration = musictrack.h;
|
int duration = musictrack.h;
|
||||||
|
|
||||||
|
|||||||
@@ -3,14 +3,19 @@ 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.music.downloader.M3UDownloader;
|
||||||
|
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 +24,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 +33,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 = M3UDownloader.getArtists(musictrack);
|
||||||
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 +41,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 +64,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,
|
||||||
|
|||||||
+26
-14
@@ -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,15 @@ 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.downloader.M3UDownloader;
|
||||||
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 +20,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),
|
M3UDownloader.getArtists(track),
|
||||||
track.I != null ? track.I.getTitle() : "",
|
track.I != null ? track.I.getTitle() : "",
|
||||||
track.K,
|
track.K,
|
||||||
track.h,
|
track.h,
|
||||||
@@ -38,33 +32,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 +58,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 +75,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,14 @@ 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.music.downloader.M3UDownloader;
|
||||||
|
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 +24,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 +117,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, M3UDownloader.getArtists(track));
|
||||||
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 +166,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 +216,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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-149
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+67
-66
@@ -1,79 +1,93 @@
|
|||||||
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.music.downloader.M3UDownloader;
|
||||||
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(),
|
||||||
|
M3UDownloader.getArtists(track),
|
||||||
|
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)
|
M3UDownloader.getArtists(track),
|
||||||
.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 +115,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 +122,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 {
|
||||||
@@ -142,4 +143,4 @@ public class PlaylistHelper {
|
|||||||
.put("layout", new JSONObject()
|
.put("layout", new JSONObject()
|
||||||
.put("name", "separator"));
|
.put("name", "separator"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-6
@@ -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());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,9 +53,7 @@ public class PlaylistConverter {
|
|||||||
var request = new Request.a()
|
var request = new Request.a()
|
||||||
.b(requestUrl)
|
.b(requestUrl)
|
||||||
.a("Accept-Encoding", "gzip")
|
.a("Accept-Encoding", "gzip")
|
||||||
.a("User-Agent", Network.l.c().a())
|
.a(Headers.a("User-Agent", Network.l.c().a(), "Content-Type", "application/x-www-form-urlencoded; charset=utf-8")).a();
|
||||||
.a("Content-Type", "application/x-www-form-urlencoded; charset=utf-8")
|
|
||||||
.a();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var response = CompletableFuture.supplyAsync(() -> {
|
var response = CompletableFuture.supplyAsync(() -> {
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -47,9 +47,8 @@ public class AudioGet {
|
|||||||
var request = new Request.a()
|
var request = new Request.a()
|
||||||
.b(requestUrl)
|
.b(requestUrl)
|
||||||
.a("Accept-Encoding", "gzip")
|
.a("Accept-Encoding", "gzip")
|
||||||
.a("User-Agent", Network.l.c().a())
|
.a(Headers.a("User-Agent", Network.l.c().a(), "Content-Type", "application/x-www-form-urlencoded; charset=utf-8")).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 (Response resp = Network.b(CLIENT_API).a(request).execute()) {
|
||||||
|
|||||||
@@ -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,193 @@
|
|||||||
|
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.Artist;
|
||||||
|
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;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
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" : getArtists(track) + " - " + 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getArtists(MusicTrack track) {
|
||||||
|
return (track.L == null || track.I == null
|
||||||
|
? normalizeMetadata(track.C)
|
||||||
|
: normalizeMetadata(track.L.stream().map(Artist::w1).collect(Collectors.joining(", "))));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String normalizeMetadata(String in) {
|
||||||
|
return in.replaceAll("[\\\\/:*?\"<>|]", "");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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©right=";
|
||||||
|
//
|
||||||
|
// 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);
|
||||||
}
|
}
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,41 +1,29 @@
|
|||||||
package ru.vtosters.lite.ui.fragments;
|
package ru.vtosters.lite.ui.fragments;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.ArrayAdapter;
|
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
import android.widget.TextView;
|
import androidx.preference.Preference;
|
||||||
import bruhcollective.itaysonlab.libvkx.client.LibVKXClient;
|
import bruhcollective.itaysonlab.libvkx.client.LibVKXClient;
|
||||||
import com.vk.core.dialogs.alert.VkAlertDialog;
|
import com.vk.core.dialogs.alert.VkAlertDialog;
|
||||||
import com.vk.dto.music.Playlist;
|
|
||||||
import com.vtosters.lite.ui.SummaryListPreference;
|
import com.vtosters.lite.ui.SummaryListPreference;
|
||||||
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.downloaders.AudioDownloader;
|
import ru.vtosters.lite.downloaders.AudioDownloader;
|
||||||
import ru.vtosters.lite.music.LastFMScrobbler;
|
import ru.vtosters.lite.music.LastFMScrobbler;
|
||||||
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.ui.PreferenceFragmentUtils;
|
import ru.vtosters.lite.ui.PreferenceFragmentUtils;
|
||||||
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.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Executors;
|
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 {
|
public class MusicFragment extends TrackedMaterialPreferenceToolbarFragment {
|
||||||
private static final ExecutorService executor = Executors.newCachedThreadPool();
|
private static final ExecutorService executor = Executors.newCachedThreadPool();
|
||||||
|
|
||||||
@SuppressLint("DefaultLocale")
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle bundle) {
|
public void onCreate(Bundle bundle) {
|
||||||
super.onCreate(bundle);
|
super.onCreate(bundle);
|
||||||
@@ -56,38 +44,12 @@ public class MusicFragment extends TrackedMaterialPreferenceToolbarFragment {
|
|||||||
if (MusicCacheImpl.isEmpty()) {
|
if (MusicCacheImpl.isEmpty()) {
|
||||||
AndroidUtils.sendToast(requireContext().getString(com.vtosters.lite.R.string.no_cache_error));
|
AndroidUtils.sendToast(requireContext().getString(com.vtosters.lite.R.string.no_cache_error));
|
||||||
} else {
|
} else {
|
||||||
delcache(requireContext(), false);
|
delcache(requireContext());
|
||||||
}
|
}
|
||||||
return true;
|
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(
|
PreferenceFragmentUtils.addPreference(
|
||||||
getPreferenceScreen(),
|
getPreferenceScreen(),
|
||||||
"audio_download",
|
"audio_download",
|
||||||
@@ -113,38 +75,15 @@ public class MusicFragment extends TrackedMaterialPreferenceToolbarFragment {
|
|||||||
}
|
}
|
||||||
).setEnabled(!MusicCacheImpl.isEmpty());
|
).setEnabled(!MusicCacheImpl.isEmpty());
|
||||||
|
|
||||||
PreferenceFragmentUtils.addPreference(
|
PreferenceFragmentUtils.addMaterialSwitchPreference(
|
||||||
getPreferenceScreen(),
|
getPreferenceScreen(),
|
||||||
"autocache_params",
|
"autocache",
|
||||||
getString(com.vtosters.lite.R.string.autocache_title),
|
getString(com.vtosters.lite.R.string.autocache_title),
|
||||||
getAutocacheSumm(),
|
getString(com.vtosters.lite.R.string.autocache_summ),
|
||||||
null,
|
null,
|
||||||
preference -> {
|
false,
|
||||||
List<String> items = Arrays.asList("Не кешировать", "Только свои", "Все");
|
(preference, o) -> {
|
||||||
|
Preferences.getPreferences().edit().putBoolean("autocache", (boolean) o).apply();
|
||||||
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;
|
return true;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -160,7 +99,6 @@ public class MusicFragment extends TrackedMaterialPreferenceToolbarFragment {
|
|||||||
false,
|
false,
|
||||||
(preference, o) -> {
|
(preference, o) -> {
|
||||||
Preferences.getPreferences().edit().putBoolean("dldir", (boolean) o).apply();
|
Preferences.getPreferences().edit().putBoolean("dldir", (boolean) o).apply();
|
||||||
LifecycleUtils.restartApplicationWithTimer();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -178,73 +116,6 @@ public class MusicFragment extends TrackedMaterialPreferenceToolbarFragment {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
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()) {
|
if (!Preferences.serverFeaturesDisable()) {
|
||||||
PreferenceFragmentUtils.addPreferenceCategory(getPreferenceScreen(), "Интеграция Genius");
|
PreferenceFragmentUtils.addPreferenceCategory(getPreferenceScreen(), "Интеграция Genius");
|
||||||
PreferenceFragmentUtils.addMaterialSwitchPreference(
|
PreferenceFragmentUtils.addMaterialSwitchPreference(
|
||||||
@@ -352,19 +223,6 @@ public class MusicFragment extends TrackedMaterialPreferenceToolbarFragment {
|
|||||||
list.setVisible(!Preferences.getBoolValue("useOldAppVer", false));
|
list.setVisible(!Preferences.getBoolValue("useOldAppVer", false));
|
||||||
|
|
||||||
getPreferenceScreen().addPreference(list);
|
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) {
|
private void lastfmAuth(Context ctx) {
|
||||||
@@ -404,15 +262,6 @@ public class MusicFragment extends TrackedMaterialPreferenceToolbarFragment {
|
|||||||
.show();
|
.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String getAutocacheSumm() {
|
|
||||||
int autocache = Preferences.autocache();
|
|
||||||
return switch (autocache) {
|
|
||||||
case 1 -> "Только для своих";
|
|
||||||
case 2 -> "Кешировать всё";
|
|
||||||
default -> "Отключено";
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateLastFmPref() {
|
public void updateLastFmPref() {
|
||||||
if (LastFMScrobbler.isLoggedIn()) {
|
if (LastFMScrobbler.isLoggedIn()) {
|
||||||
findPreference("lastfm_auth").setSummary(getString(com.vtosters.lite.R.string.lastfm_authorized_as) + " " + LastFMScrobbler.getUserName());
|
findPreference("lastfm_auth").setSummary(getString(com.vtosters.lite.R.string.lastfm_authorized_as) + " " + LastFMScrobbler.getUserName());
|
||||||
@@ -434,43 +283,14 @@ public class MusicFragment extends TrackedMaterialPreferenceToolbarFragment {
|
|||||||
.show();
|
.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("DefaultLocale")
|
private void delcache(Context ctx) {
|
||||||
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)
|
new VkAlertDialog.Builder(ctx)
|
||||||
.setTitle(com.vtosters.lite.R.string.warning)
|
.setTitle(com.vtosters.lite.R.string.warning)
|
||||||
.setMessage(com.vtosters.lite.R.string.cached_tracks_remove_confirm)
|
.setMessage(com.vtosters.lite.R.string.cached_tracks_remove_confirm)
|
||||||
.setPositiveButton(com.vtosters.lite.R.string.yes, (dialog, which) -> {
|
.setPositiveButton(com.vtosters.lite.R.string.yes,
|
||||||
executor.submit(() -> {
|
(dialog, which) -> executor.submit(MusicCacheImpl::clear))
|
||||||
if (isPlaylists) {
|
.setNeutralButton(com.vtosters.lite.R.string.no,
|
||||||
PlaylistCacheDbDelegate.removeAllPlaylists(ctx);
|
(dialog, which) -> dialog.cancel())
|
||||||
} 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();
|
.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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));
|
||||||
|
|||||||
@@ -227,23 +227,6 @@ public class SponsorPostFragment extends TrackedMaterialPreferenceToolbarFragmen
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (NetworkUtils.isNetworkConnected()) {
|
|
||||||
PreferenceFragmentUtils.addPreference(
|
|
||||||
getPreferenceScreen(),
|
|
||||||
"",
|
|
||||||
"Обновить списки фильтров",
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
preference -> {
|
|
||||||
this.requireActivity().runOnUiThread(() -> {
|
|
||||||
FiltersPreferences.forceUpdateFilters();
|
|
||||||
AndroidUtils.sendToast("Обновлено");
|
|
||||||
});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
PreferenceFragmentUtils.addPreference(
|
PreferenceFragmentUtils.addPreference(
|
||||||
getPreferenceScreen(),
|
getPreferenceScreen(),
|
||||||
"",
|
"",
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ 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.*;
|
||||||
@@ -108,7 +107,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(
|
||||||
@@ -534,20 +533,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 +569,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();
|
||||||
|
|||||||
@@ -1,107 +0,0 @@
|
|||||||
package ru.vtosters.lite.utils;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.net.ConnectivityManager;
|
|
||||||
import android.net.NetworkCapabilities;
|
|
||||||
import android.net.NetworkInfo;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.util.Log;
|
|
||||||
import com.vk.core.network.Network;
|
|
||||||
import okhttp3.Call;
|
|
||||||
import okhttp3.Callback;
|
|
||||||
import okhttp3.Request;
|
|
||||||
import okhttp3.Response;
|
|
||||||
import org.json.JSONArray;
|
|
||||||
import org.json.JSONObject;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import static com.vk.core.network.Network.ClientType.CLIENT_API;
|
|
||||||
import static ru.vtosters.hooks.other.Preferences.getBoolValue;
|
|
||||||
import static ru.vtosters.lite.proxy.ProxyUtils.getApi;
|
|
||||||
import static ru.vtosters.lite.utils.AccountManagerUtils.getUserToken;
|
|
||||||
|
|
||||||
public class Metrics {
|
|
||||||
private static JSONArray events = new JSONArray();
|
|
||||||
|
|
||||||
public static void trackEvents(JSONObject object, boolean isMusic) {
|
|
||||||
if (NetworkUtils.isNetworkConnected()) {
|
|
||||||
handleNetworkConnected(object, isMusic);
|
|
||||||
} else {
|
|
||||||
handleNetworkDisconnected(object, isMusic);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void handleNetworkConnected(JSONObject object, boolean isMusic) {
|
|
||||||
if (isMusic || shouldSaveUserTraffic() || events.length() > 0) {
|
|
||||||
trackEventsImmediately(object);
|
|
||||||
} else {
|
|
||||||
trackEventList(object);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void handleNetworkDisconnected(JSONObject object, boolean isMusic) {
|
|
||||||
if (!isMusic) {
|
|
||||||
events.put(object);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean shouldSaveUserTraffic() {
|
|
||||||
return getBoolValue("isRoamingState", false) || isMobileNetwork();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void trackEventList(JSONObject object) {
|
|
||||||
events.put(object);
|
|
||||||
|
|
||||||
if (events.length() > 2) {
|
|
||||||
trackEventsRequest(events);
|
|
||||||
resetEvents();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void trackEventsImmediately(JSONObject object) {
|
|
||||||
JSONArray eventsToTrack = new JSONArray();
|
|
||||||
eventsToTrack.put(object);
|
|
||||||
trackEventsRequest(eventsToTrack);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void resetEvents() {
|
|
||||||
events = new JSONArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
private static boolean isMobileNetwork() {
|
|
||||||
ConnectivityManager connectivityManager = (ConnectivityManager) AndroidUtils.getGlobalContext().getSystemService(Context.CONNECTIVITY_SERVICE);
|
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
||||||
android.net.Network network = connectivityManager.getActiveNetwork();
|
|
||||||
if (network != null) {
|
|
||||||
NetworkCapabilities networkCapabilities = connectivityManager.getNetworkCapabilities(network);
|
|
||||||
return networkCapabilities != null && networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
NetworkInfo networkInfo = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
|
|
||||||
return networkInfo != null && networkInfo.isConnected();
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void trackEventsRequest(JSONArray object) {
|
|
||||||
Request req = new Request.a()
|
|
||||||
.b("https://" + getApi() + "/method/stats.trackEvents?events=" + object.toString() + "&v=5.119&access_token=" + getUserToken())
|
|
||||||
.a();
|
|
||||||
|
|
||||||
Network.b(CLIENT_API).a(req).a(new Callback() {
|
|
||||||
@Override
|
|
||||||
public void a(Call call, IOException e) {
|
|
||||||
e.fillInStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a(Call call, Response response) throws IOException {
|
|
||||||
Log.d("Metrics", response.a().g());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,19 +2,17 @@ package ru.vtosters.lite.utils;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
|
||||||
import com.vk.core.fragments.FragmentImpl;
|
import com.vk.core.fragments.FragmentImpl;
|
||||||
import com.vk.extensions.NavigatorExt;
|
|
||||||
import com.vk.navigation.Navigator;
|
import com.vk.navigation.Navigator;
|
||||||
import com.vtosters.lite.TabletDialogActivity;
|
|
||||||
import ru.vtosters.lite.ui.fragments.VTSettings;
|
import ru.vtosters.lite.ui.fragments.VTSettings;
|
||||||
|
|
||||||
public class NavigatorUtils {
|
public class NavigatorUtils {
|
||||||
|
|
||||||
public static void switchFragment(Context context, Class<? extends FragmentImpl> fragmentClz) {
|
public static void switchFragment(Context context, Class<? extends FragmentImpl> fragmentClz) {
|
||||||
Navigator navigator = new Navigator(fragmentClz, new Bundle());
|
var intent = new Navigator(fragmentClz)
|
||||||
NavigatorExt.a(navigator, new TabletDialogActivity.b());
|
.b(context)
|
||||||
navigator.a(context);
|
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
context.startActivity(intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void switchFragmentNavigator(Context context, Navigator fragmentClz) {
|
public static void switchFragmentNavigator(Context context, Navigator fragmentClz) {
|
||||||
|
|||||||
@@ -9,24 +9,24 @@ import static ru.vtosters.lite.foaf.FoafBase.getBypassedOnlineInfo;
|
|||||||
|
|
||||||
public class OnlineBypass {
|
public class OnlineBypass {
|
||||||
public static JSONObject setOnlineInfo(JSONObject json) throws JSONException {
|
public static JSONObject setOnlineInfo(JSONObject json) throws JSONException {
|
||||||
int id = json.optInt("id");
|
var id = json.optInt("id");
|
||||||
if (id == AccountManagerUtils.getUserId()) {
|
if (id == AccountManagerUtils.getUserId()) {
|
||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
JSONObject onlineinfo = json.optJSONObject("online_info");
|
var onlineinfo = json.optJSONObject("online_info");
|
||||||
if (onlineinfo != null && !onlineinfo.optBoolean("visible") && !Preferences.serverFeaturesDisable()) {
|
if (onlineinfo != null && !onlineinfo.optBoolean("visible") && !Preferences.serverFeaturesDisable()) {
|
||||||
var bypassed = getBypassedOnlineInfo(id);
|
var bypassed = getBypassedOnlineInfo(id);
|
||||||
if (bypassed.optInt("last_seen", 0) != 0) {
|
if (bypassed.optInt("last_seen", 0) != 0) {
|
||||||
json.remove("online_info");
|
json.remove("online_info");
|
||||||
|
|
||||||
JSONObject online_info = new JSONObject()
|
var online_info = new JSONObject()
|
||||||
.put("visible", true)
|
.put("visible", true)
|
||||||
.put("last_seen", bypassed.optInt("last_seen"))
|
.put("last_seen", bypassed.optInt("last_seen"))
|
||||||
.put("is_online", bypassed.optBoolean("is_online"))
|
.put("is_online", bypassed.optBoolean("is_online"))
|
||||||
.put("app_id", bypassed.optInt("app_id"))
|
.put("app_id", bypassed.optInt("app_id"))
|
||||||
.put("is_mobile", bypassed.optBoolean("is_mobile"));
|
.put("is_mobile", bypassed.optBoolean("is_mobile"));
|
||||||
|
|
||||||
JSONObject last_seen = new JSONObject()
|
var last_seen = new JSONObject()
|
||||||
.put("platform", bypassed.optInt("platform"))
|
.put("platform", bypassed.optInt("platform"))
|
||||||
.put("time", bypassed.optInt("last_seen"));
|
.put("time", bypassed.optInt("last_seen"));
|
||||||
|
|
||||||
@@ -37,95 +37,51 @@ public class OnlineBypass {
|
|||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static JSONArray setOnlineInfoUsers(JSONArray profiles) {
|
public static JSONArray setOnlineInfoUsers(JSONArray profiles) throws JSONException {
|
||||||
if (shouldReturnProfiles(profiles)) return profiles;
|
if (profiles == null || profiles.length() == 0 || Preferences.serverFeaturesDisable()) return profiles;
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
int curVkId = AccountManagerUtils.getUserId();
|
var curVkId = AccountManagerUtils.getUserId();
|
||||||
|
|
||||||
for (int i = 0; i < profiles.length(); i++) {
|
for (int i = 0; i < profiles.length(); i++) {
|
||||||
JSONObject profile = getProfile(profiles, i);
|
JSONObject profile = profiles.getJSONObject(i);
|
||||||
int id = profile.optInt("id", -1);
|
int id = profile.optInt("id", -1);
|
||||||
JSONObject onlinfo = profile.optJSONObject("online_info");
|
JSONObject onlinfo = profile.optJSONObject("online_info");
|
||||||
|
if (id == curVkId || id < 0 || onlinfo == null || onlinfo.optBoolean("visible")) {
|
||||||
if (shouldSkipProfile(id, curVkId, onlinfo)) continue;
|
continue;
|
||||||
|
}
|
||||||
sb.append(id).append(",");
|
sb.append(id);
|
||||||
|
sb.append(",");
|
||||||
|
}
|
||||||
|
var ids = sb.toString();
|
||||||
|
if (!ids.isEmpty()) {
|
||||||
|
ids = ids.substring(0, ids.length() - 1);
|
||||||
|
} else {
|
||||||
|
return profiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
String ids = prepareIds(sb);
|
|
||||||
if (ids.isEmpty()) return profiles;
|
|
||||||
|
|
||||||
JSONObject bypassedObj = getBypassedOnlineInfo(ids);
|
JSONObject bypassedObj = getBypassedOnlineInfo(ids);
|
||||||
for (int i = 0; i < profiles.length(); i++) {
|
for (int i = 0; i < profiles.length(); i++) {
|
||||||
JSONObject profile = getProfile(profiles, i);
|
JSONObject profile = profiles.getJSONObject(i);
|
||||||
int id = profile.optInt("id");
|
int id = profile.optInt("id");
|
||||||
JSONObject bypassed = bypassedObj.optJSONObject(Integer.toString(id));
|
JSONObject bypassed = bypassedObj.optJSONObject(Integer.toString(id));
|
||||||
|
if (bypassed == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
profile.remove("online_info");
|
||||||
|
|
||||||
if (bypassed == null) continue;
|
var online_info = new JSONObject()
|
||||||
|
|
||||||
JSONObject online_info = createOnlineInfo(bypassed);
|
|
||||||
JSONObject last_seen = createLastSeen(bypassed);
|
|
||||||
|
|
||||||
updateProfile(profile, online_info, last_seen);
|
|
||||||
}
|
|
||||||
|
|
||||||
return profiles;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean shouldReturnProfiles(JSONArray profiles) {
|
|
||||||
return profiles == null || profiles.length() == 0 || Preferences.serverFeaturesDisable();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static JSONObject getProfile(JSONArray profiles, int i) {
|
|
||||||
try {
|
|
||||||
return profiles.getJSONObject(i);
|
|
||||||
} catch (JSONException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean shouldSkipProfile(int id, int curVkId, JSONObject onlinfo) {
|
|
||||||
return id == curVkId || id < 0 || onlinfo == null || onlinfo.optBoolean("visible");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String prepareIds(StringBuilder sb) {
|
|
||||||
String ids = sb.toString();
|
|
||||||
if (!ids.isEmpty()) {
|
|
||||||
ids = ids.substring(0, ids.length() - 1);
|
|
||||||
}
|
|
||||||
return ids;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static JSONObject createOnlineInfo(JSONObject bypassed) {
|
|
||||||
try {
|
|
||||||
return new JSONObject()
|
|
||||||
.put("visible", true)
|
.put("visible", true)
|
||||||
.put("last_seen", bypassed.optInt("last_seen"))
|
.put("last_seen", bypassed.optInt("last_seen"))
|
||||||
.put("is_online", bypassed.optBoolean("is_online"))
|
.put("is_online", bypassed.optBoolean("is_online"))
|
||||||
.put("app_id", bypassed.optInt("app_id"))
|
.put("app_id", bypassed.optInt("app_id"))
|
||||||
.put("is_mobile", bypassed.optBoolean("is_mobile"));
|
.put("is_mobile", bypassed.optBoolean("is_mobile"));
|
||||||
} catch (JSONException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static JSONObject createLastSeen(JSONObject bypassed) {
|
var last_seen = new JSONObject()
|
||||||
try {
|
|
||||||
return new JSONObject()
|
|
||||||
.put("platform", bypassed.optInt("platform"))
|
.put("platform", bypassed.optInt("platform"))
|
||||||
.put("time", bypassed.optInt("last_seen"));
|
.put("time", bypassed.optInt("last_seen"));
|
||||||
} catch (JSONException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void updateProfile(JSONObject profile, JSONObject online_info, JSONObject last_seen) {
|
|
||||||
try {
|
|
||||||
profile.remove("online_info");
|
|
||||||
profile.put("last_seen", last_seen).put("online_info", online_info);
|
profile.put("last_seen", last_seen).put("online_info", online_info);
|
||||||
} catch (JSONException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return profiles;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package ru.vtosters.lite.utils;
|
package ru.vtosters.lite.utils;
|
||||||
|
|
||||||
import android.content.ContentValues;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
@@ -13,15 +12,15 @@ import android.widget.LinearLayout;
|
|||||||
import androidx.core.util.Pair;
|
import androidx.core.util.Pair;
|
||||||
import com.vk.core.dialogs.alert.VkAlertDialog;
|
import com.vk.core.dialogs.alert.VkAlertDialog;
|
||||||
import com.vk.dto.user.UserProfile;
|
import com.vk.dto.user.UserProfile;
|
||||||
|
import com.vk.im.engine.models.users.User;
|
||||||
import com.vtosters.lite.R;
|
import com.vtosters.lite.R;
|
||||||
import com.vtosters.lite.api.ExtendedCommunityProfile;
|
import com.vtosters.lite.api.ExtendedCommunityProfile;
|
||||||
import com.vtosters.lite.api.ExtendedUserProfile;
|
import com.vtosters.lite.api.ExtendedUserProfile;
|
||||||
import ru.vtosters.hooks.other.ThemesUtils;
|
import ru.vtosters.hooks.other.ThemesUtils;
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.lang.reflect.Field;
|
||||||
import java.net.URLDecoder;
|
import java.net.URLDecoder;
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
import java.util.function.BiConsumer;
|
|
||||||
|
|
||||||
import static ru.vtosters.hooks.other.ThemesUtils.getSTextAttr;
|
import static ru.vtosters.hooks.other.ThemesUtils.getSTextAttr;
|
||||||
import static ru.vtosters.hooks.other.ThemesUtils.getTextAttr;
|
import static ru.vtosters.hooks.other.ThemesUtils.getTextAttr;
|
||||||
@@ -48,40 +47,69 @@ public class RenameTool {
|
|||||||
return helperInstance;
|
return helperInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getCurrentModifiedUser() {
|
||||||
|
Cursor rawQuery = getHelper().getReadableDatabase().rawQuery(String.format("SELECT * FROM %s WHERE %s=%s", TABLE_NAME, COLUMN_VKID, AccountManagerUtils.getUserId()), new String[0]);
|
||||||
|
if (rawQuery.moveToFirst()) {
|
||||||
|
try {
|
||||||
|
String decode = URLDecoder.decode(rawQuery.getString(rawQuery.getColumnIndexOrThrow(COLUMN_FIRSTNAME)), "UTF-8");
|
||||||
|
return decode + " " + URLDecoder.decode(rawQuery.getString(rawQuery.getColumnIndexOrThrow(COLUMN_LASTNAME)), "UTF-8");
|
||||||
|
} catch (java.io.UnsupportedEncodingException | java.lang.IllegalArgumentException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} finally {
|
||||||
|
rawQuery.close();
|
||||||
|
}
|
||||||
|
} else rawQuery.close();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reload all values from DB
|
||||||
public static void reloadDB() {
|
public static void reloadDB() {
|
||||||
renamedGroups.clear();
|
renamedGroups.clear();
|
||||||
renamedUsers.clear();
|
renamedUsers.clear();
|
||||||
updateRequested = false;
|
updateRequested = false;
|
||||||
|
|
||||||
loadData(TABLE_NAME, (cursor, vkID) -> {
|
try (Cursor cursor = getHelper().getReadableDatabase().rawQuery(String.format("SELECT * FROM %s", TABLE_NAME), new String[0])) {
|
||||||
String firstName = decode(cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_FIRSTNAME)));
|
|
||||||
String lastName = decode(cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_LASTNAME)));
|
|
||||||
renamedUsers.put(vkID, new Pair<>(firstName, lastName));
|
|
||||||
});
|
|
||||||
|
|
||||||
loadData(TABLE_NAME_GROUP, (cursor, vkID) -> {
|
|
||||||
String name = decode(cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_NAME)));
|
|
||||||
renamedGroups.put(vkID, name);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generic method to load data from the database
|
|
||||||
private static void loadData(String tableName, BiConsumer<Cursor, Integer> consumer) {
|
|
||||||
try (Cursor cursor = getHelper().getReadableDatabase().rawQuery("SELECT * FROM " + tableName, null)) {
|
|
||||||
while (cursor.moveToNext()) {
|
while (cursor.moveToNext()) {
|
||||||
|
String firstName = URLDecoder.decode(cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_FIRSTNAME)), "UTF-8");
|
||||||
|
String lastName = URLDecoder.decode(cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_LASTNAME)), "UTF-8");
|
||||||
int vkID = cursor.getInt(cursor.getColumnIndexOrThrow(COLUMN_VKID));
|
int vkID = cursor.getInt(cursor.getColumnIndexOrThrow(COLUMN_VKID));
|
||||||
consumer.accept(cursor, vkID);
|
renamedUsers.put(vkID, new Pair<>(firstName, lastName));
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException("Failed to load data from " + tableName, e);
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
try (Cursor cursor = getHelper().getReadableDatabase().rawQuery(String.format("SELECT * FROM %s", TABLE_NAME_GROUP), new String[0])) {
|
||||||
|
while (cursor.moveToNext()) {
|
||||||
|
String name = URLDecoder.decode(cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_NAME)), "UTF-8");
|
||||||
|
int vkID = cursor.getInt(cursor.getColumnIndexOrThrow(COLUMN_VKID));
|
||||||
|
renamedGroups.put(vkID, name);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String decode(String value) {
|
public static void injectIntoChat(User u) {
|
||||||
|
int id = u.getId();
|
||||||
|
if (updateRequested) {
|
||||||
|
reloadDB();
|
||||||
|
}
|
||||||
|
|
||||||
|
Pair<String, String> user = renamedUsers.get(id);
|
||||||
|
if (user == null) return;
|
||||||
|
|
||||||
|
setObject(u, "I", user.first);
|
||||||
|
setObject(u, "J", user.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setObject(User instance, String name, Object obj) {
|
||||||
try {
|
try {
|
||||||
return URLDecoder.decode(value, "UTF-8");
|
Field f = User.class.getDeclaredField(name);
|
||||||
} catch (UnsupportedEncodingException e) {
|
f.setAccessible(true);
|
||||||
throw new RuntimeException("UTF-8 encoding not supported", e);
|
f.set(instance, obj);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,7 +117,7 @@ public class RenameTool {
|
|||||||
LinearLayout linearLayout = new LinearLayout(ctx);
|
LinearLayout linearLayout = new LinearLayout(ctx);
|
||||||
linearLayout.setOrientation(LinearLayout.VERTICAL);
|
linearLayout.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
|
||||||
EditText fn = new EditText(ctx);
|
final EditText fn = new EditText(ctx);
|
||||||
fn.setHint(R.string.vk_auth_sign_up_first_name);
|
fn.setHint(R.string.vk_auth_sign_up_first_name);
|
||||||
fn.setTextColor(getTextAttr());
|
fn.setTextColor(getTextAttr());
|
||||||
fn.setHintTextColor(getSTextAttr());
|
fn.setHintTextColor(getSTextAttr());
|
||||||
@@ -100,7 +128,7 @@ public class RenameTool {
|
|||||||
margin.setMargins(dp2px(20f), 0, dp2px(20f), 0);
|
margin.setMargins(dp2px(20f), 0, dp2px(20f), 0);
|
||||||
fn.setLayoutParams(margin);
|
fn.setLayoutParams(margin);
|
||||||
|
|
||||||
EditText ln = new EditText(ctx);
|
final EditText ln = new EditText(ctx);
|
||||||
ln.setHint(R.string.vk_auth_sign_up_last_name);
|
ln.setHint(R.string.vk_auth_sign_up_last_name);
|
||||||
ln.setTextColor(getTextAttr());
|
ln.setTextColor(getTextAttr());
|
||||||
ln.setHintTextColor(getSTextAttr());
|
ln.setHintTextColor(getSTextAttr());
|
||||||
@@ -109,93 +137,69 @@ public class RenameTool {
|
|||||||
ln.getLayoutParams().width = ViewGroup.LayoutParams.MATCH_PARENT;
|
ln.getLayoutParams().width = ViewGroup.LayoutParams.MATCH_PARENT;
|
||||||
ln.setLayoutParams(margin);
|
ln.setLayoutParams(margin);
|
||||||
|
|
||||||
UserProfile profile = AccountManagerUtils.fromEup(eup);
|
final UserProfile profile = AccountManagerUtils.fromEup(eup);
|
||||||
fn.setText(AccountManagerUtils.getUserFirstName(profile));
|
fn.setText(AccountManagerUtils.getUserFirstName(profile));
|
||||||
ln.setText(AccountManagerUtils.getUserLastName(profile));
|
ln.setText(AccountManagerUtils.getUserLastName(profile));
|
||||||
|
|
||||||
int id = AccountManagerUtils.getUserID(profile);
|
int id = AccountManagerUtils.getUserID(profile);
|
||||||
|
|
||||||
VkAlertDialog.Builder builder = new VkAlertDialog.Builder(ctx)
|
var builder = new VkAlertDialog.Builder(ctx)
|
||||||
.setTitle(R.string.rename_title)
|
.setTitle(R.string.rename_title)
|
||||||
.setMessage(R.string.rename_message)
|
.setMessage(R.string.rename_message)
|
||||||
.setView(linearLayout)
|
.setView(linearLayout)
|
||||||
.setPositiveButton("OK", (dialog, which) -> updateName(ctx, fn.getText().toString(), ln.getText().toString(), id));
|
.setPositiveButton("OK", (dialog, which) -> {
|
||||||
|
String firstName = fn.getText().toString();
|
||||||
if (isChangedName(id)) {
|
String lastName = ln.getText().toString();
|
||||||
builder.setNeutralButton(R.string.rename_reset, (dialog, which) -> deleteUser(ctx, id));
|
SQLiteDatabase writableDatabase = getHelper().getWritableDatabase();
|
||||||
}
|
try {
|
||||||
|
if (isChangedName(id)) {
|
||||||
|
writableDatabase.execSQL(
|
||||||
|
String.format("UPDATE %s SET %s='%s', %s='%s' WHERE %s='%s'",
|
||||||
|
TABLE_NAME,
|
||||||
|
COLUMN_FIRSTNAME, URLEncoder.encode(firstName, "UTF-8"),
|
||||||
|
COLUMN_LASTNAME, URLEncoder.encode(lastName, "UTF-8"),
|
||||||
|
COLUMN_VKID, id));
|
||||||
|
} else {
|
||||||
|
writableDatabase.execSQL(
|
||||||
|
String.format("INSERT INTO %s (%s, %s, %s) VALUES (%s, '%s', '%s')",
|
||||||
|
TABLE_NAME,
|
||||||
|
COLUMN_VKID, COLUMN_FIRSTNAME, COLUMN_LASTNAME,
|
||||||
|
id, URLEncoder.encode(firstName, "UTF-8"), URLEncoder.encode(lastName, "UTF-8")));
|
||||||
|
}
|
||||||
|
updateRequested = true;
|
||||||
|
if (id == AccountManagerUtils.getUserId()) {
|
||||||
|
ctx.sendBroadcast(new Intent("com.vkontakte.android.USER_NAME_CHANGED"));
|
||||||
|
}
|
||||||
|
ctx.sendBroadcast(new Intent("com.vkontakte.android.ACTION_PROFILE_UPDATED").putExtra("uid", id));
|
||||||
|
|
||||||
|
sendToast(ctx.getString(R.string.rename_success));
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
sendToast(ctx.getString(R.string.rename_error));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (isChangedName(id))
|
||||||
|
builder.setNeutralButton(R.string.rename_reset, (dialog, which) -> {
|
||||||
|
getHelper().getWritableDatabase().execSQL(
|
||||||
|
String.format(
|
||||||
|
"DELETE FROM %s WHERE %s='%s'",
|
||||||
|
TABLE_NAME,
|
||||||
|
COLUMN_VKID, id));
|
||||||
|
if (id == AccountManagerUtils.getUserId()) {
|
||||||
|
ctx.sendBroadcast(new Intent("com.vkontakte.android.USER_NAME_CHANGED"));
|
||||||
|
}
|
||||||
|
updateRequested = true;
|
||||||
|
ctx.sendBroadcast(new Intent("com.vkontakte.android.ACTION_PROFILE_UPDATED").putExtra("uid", id));
|
||||||
|
sendToast(ctx.getString(R.string.rename_remove_from_bd_success));
|
||||||
|
});
|
||||||
builder.show();
|
builder.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void deleteUser(Context ctx, int id) {
|
|
||||||
SQLiteDatabase writableDatabase = getHelper().getWritableDatabase();
|
|
||||||
|
|
||||||
writableDatabase.delete(TABLE_NAME, COLUMN_VKID + "=?", new String[]{String.valueOf(id)});
|
|
||||||
|
|
||||||
updateRequested = true;
|
|
||||||
notifyUser(ctx, id);
|
|
||||||
|
|
||||||
sendToast(ctx.getString(R.string.rename_remove_from_bd_success));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void clearDatabase() {
|
|
||||||
SQLiteDatabase writableDatabase = getHelper().getWritableDatabase();
|
|
||||||
|
|
||||||
writableDatabase.delete(TABLE_NAME, null, null);
|
|
||||||
writableDatabase.delete(TABLE_NAME_GROUP, null, null);
|
|
||||||
|
|
||||||
renamedGroups.clear();
|
|
||||||
renamedUsers.clear();
|
|
||||||
updateRequested = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void updateName(Context ctx, String firstName, String lastName, int id) {
|
|
||||||
SQLiteDatabase writableDatabase = getHelper().getWritableDatabase();
|
|
||||||
|
|
||||||
try {
|
|
||||||
ContentValues values = new ContentValues();
|
|
||||||
values.put(COLUMN_FIRSTNAME, encode(firstName));
|
|
||||||
values.put(COLUMN_LASTNAME, encode(lastName));
|
|
||||||
|
|
||||||
if (isChangedName(id)) {
|
|
||||||
// Update existing record
|
|
||||||
writableDatabase.update(TABLE_NAME, values, COLUMN_VKID + "=?", new String[]{String.valueOf(id)});
|
|
||||||
} else {
|
|
||||||
// Insert new record
|
|
||||||
values.put(COLUMN_VKID, id);
|
|
||||||
writableDatabase.insert(TABLE_NAME, null, values);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateRequested = true;
|
|
||||||
notifyUser(ctx, id);
|
|
||||||
|
|
||||||
sendToast(ctx.getString(R.string.rename_success));
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException("Database operation failed", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String encode(String value) {
|
|
||||||
try {
|
|
||||||
return URLEncoder.encode(value, "UTF-8");
|
|
||||||
} catch (UnsupportedEncodingException e) {
|
|
||||||
throw new RuntimeException("UTF-8 encoding not supported", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void notifyUser(Context ctx, int id) {
|
|
||||||
if (id == AccountManagerUtils.getUserId()) {
|
|
||||||
ctx.sendBroadcast(new Intent("com.vkontakte.android.USER_NAME_CHANGED"));
|
|
||||||
}
|
|
||||||
ctx.sendBroadcast(new Intent("com.vkontakte.android.ACTION_PROFILE_UPDATED").putExtra("uid", id));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void createDialogGroup(ExtendedCommunityProfile eup, final Context ctx) {
|
public static void createDialogGroup(ExtendedCommunityProfile eup, final Context ctx) {
|
||||||
LinearLayout linearLayout = new LinearLayout(ctx);
|
LinearLayout linearLayout = new LinearLayout(ctx);
|
||||||
linearLayout.setOrientation(LinearLayout.VERTICAL);
|
linearLayout.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
|
||||||
EditText fn = new EditText(ctx);
|
final EditText fn = new EditText(ctx);
|
||||||
fn.setHint(R.string.vk_auth_sign_up_first_name);
|
fn.setHint(R.string.vk_auth_sign_up_first_name);
|
||||||
fn.setHintTextColor(getSTextAttr());
|
fn.setHintTextColor(getSTextAttr());
|
||||||
fn.setTextColor(getTextAttr());
|
fn.setTextColor(getTextAttr());
|
||||||
@@ -206,61 +210,52 @@ public class RenameTool {
|
|||||||
margin.setMargins(dp2px(20f), 0, dp2px(20f), 0);
|
margin.setMargins(dp2px(20f), 0, dp2px(20f), 0);
|
||||||
fn.setLayoutParams(margin);
|
fn.setLayoutParams(margin);
|
||||||
|
|
||||||
UserProfile profile = AccountManagerUtils.fromEup(eup);
|
final UserProfile profile = AccountManagerUtils.fromEup(eup);
|
||||||
fn.setText(AccountManagerUtils.getGroupName(profile));
|
fn.setText(AccountManagerUtils.getGroupName(profile));
|
||||||
|
|
||||||
int fid = AccountManagerUtils.getUserID(profile);
|
int fid = AccountManagerUtils.getUserID(profile);
|
||||||
|
|
||||||
if (fid < 0) fid = -fid;
|
if (fid < 0) fid = -fid;
|
||||||
|
|
||||||
int id = fid;
|
final int id = fid;
|
||||||
|
|
||||||
VkAlertDialog.Builder builder = new VkAlertDialog.Builder(ctx)
|
var builder = new VkAlertDialog.Builder(ctx)
|
||||||
.setTitle(R.string.rename_title)
|
.setTitle(R.string.rename_title)
|
||||||
.setMessage(R.string.rename_message)
|
.setMessage(R.string.rename_message)
|
||||||
.setView(linearLayout)
|
.setView(linearLayout)
|
||||||
.setPositiveButton("OK", (dialog, which) -> updateGroupName(ctx, fn.getText().toString(), id));
|
.setPositiveButton("OK", (dialog, which) -> {
|
||||||
|
String firstName = fn.getText().toString();
|
||||||
if (isChangedNameGroup(id)) {
|
SQLiteDatabase writableDatabase = getHelper().getWritableDatabase();
|
||||||
builder.setNeutralButton(R.string.rename_reset, (dialog, which) -> deleteGroupName(ctx, id));
|
try {
|
||||||
}
|
if (isChangedNameGroup(id)) {
|
||||||
|
writableDatabase.execSQL(
|
||||||
|
String.format(
|
||||||
|
"UPDATE %s SET %s='%s' WHERE %s='%s'",
|
||||||
|
TABLE_NAME_GROUP,
|
||||||
|
COLUMN_NAME, URLEncoder.encode(firstName, "UTF-8"),
|
||||||
|
COLUMN_VKID, id));
|
||||||
|
} else {
|
||||||
|
writableDatabase.execSQL(
|
||||||
|
String.format(
|
||||||
|
"INSERT INTO %s (%s, %s) VALUES (%s, '%s')",
|
||||||
|
TABLE_NAME_GROUP,
|
||||||
|
COLUMN_VKID, COLUMN_NAME,
|
||||||
|
id, URLEncoder.encode(firstName, "UTF-8")));
|
||||||
|
}
|
||||||
|
updateRequested = true;
|
||||||
|
sendToast(AndroidUtils.getString(R.string.rename_group_success));
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (isChangedNameGroup(id))
|
||||||
|
builder.setNeutralButton(R.string.rename_reset, (dialog, which) -> {
|
||||||
|
getHelper().getWritableDatabase().execSQL(String.format("DELETE FROM %s WHERE %s='%s'", TABLE_NAME_GROUP, COLUMN_VKID, id));
|
||||||
|
updateRequested = true;
|
||||||
|
sendToast(ctx.getString(R.string.rename_remove_group_from_bd_success));
|
||||||
|
});
|
||||||
builder.show();
|
builder.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void deleteGroupName(Context ctx, int id) {
|
|
||||||
SQLiteDatabase writableDatabase = getHelper().getWritableDatabase();
|
|
||||||
|
|
||||||
writableDatabase.delete(TABLE_NAME_GROUP, COLUMN_VKID + "=?", new String[]{String.valueOf(id)});
|
|
||||||
|
|
||||||
updateRequested = true;
|
|
||||||
|
|
||||||
sendToast(ctx.getString(R.string.rename_remove_group_from_bd_success));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void updateGroupName(Context ctx, String groupName, int id) {
|
|
||||||
SQLiteDatabase writableDatabase = getHelper().getWritableDatabase();
|
|
||||||
|
|
||||||
try {
|
|
||||||
ContentValues values = new ContentValues();
|
|
||||||
values.put(COLUMN_NAME, encode(groupName));
|
|
||||||
|
|
||||||
if (isChangedNameGroup(id)) {
|
|
||||||
// Update existing record
|
|
||||||
writableDatabase.update(TABLE_NAME_GROUP, values, COLUMN_VKID + "=?", new String[]{String.valueOf(id)});
|
|
||||||
} else {
|
|
||||||
// Insert new record
|
|
||||||
values.put(COLUMN_VKID, id);
|
|
||||||
writableDatabase.insert(TABLE_NAME_GROUP, null, values);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateRequested = true;
|
|
||||||
sendToast(AndroidUtils.getString(R.string.rename_group_success));
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException("Database operation failed", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isChangedName(int uid) {
|
public static boolean isChangedName(int uid) {
|
||||||
return renamedUsers.get(uid) != null;
|
return renamedUsers.get(uid) != null;
|
||||||
}
|
}
|
||||||
@@ -269,6 +264,10 @@ public class RenameTool {
|
|||||||
return renamedGroups.get(uid) != null;
|
return renamedGroups.get(uid) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isIdInList(UserProfile profile) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private static class DbHelper extends SQLiteOpenHelper {
|
private static class DbHelper extends SQLiteOpenHelper {
|
||||||
public DbHelper(Context context) {
|
public DbHelper(Context context) {
|
||||||
super(context, DB_NAME, null, DB_VERSION);
|
super(context, DB_NAME, null, DB_VERSION);
|
||||||
|
|||||||
@@ -96,15 +96,14 @@ public class VTVerifications {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void processIds(JSONArray jsonIds, List<Integer> member) {
|
private static void processIds(JSONArray jsonIds, List<Integer> member) {
|
||||||
if (jsonIds == null || jsonIds.length() == 0) {
|
if (jsonIds == null || jsonIds.length() == 0)
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < jsonIds.length(); i++) {
|
for (int i = 0; i < jsonIds.length(); i++) {
|
||||||
int id = jsonIds.optInt(i);
|
try {
|
||||||
|
member.add(jsonIds.getInt(i));
|
||||||
if (id != 0) {
|
} catch (JSONException e) {
|
||||||
member.add(id);
|
e.fillInStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import static ru.vtosters.hooks.other.ThemesUtils.*;
|
|||||||
import static ru.vtosters.lite.utils.AndroidUtils.getGlobalContext;
|
import static ru.vtosters.lite.utils.AndroidUtils.getGlobalContext;
|
||||||
|
|
||||||
public class WebViewColoringUtils {
|
public class WebViewColoringUtils {
|
||||||
|
public static boolean isLoaded = false;
|
||||||
public static String loadedCSS = "";
|
public static String loadedCSS = "";
|
||||||
public static String loadedCSSAmoled = "";
|
public static String loadedCSSAmoled = "";
|
||||||
|
|
||||||
@@ -52,6 +53,7 @@ public class WebViewColoringUtils {
|
|||||||
public static void load() {
|
public static void load() {
|
||||||
loadedCSS = readCSS("vt_ui_accent.css");
|
loadedCSS = readCSS("vt_ui_accent.css");
|
||||||
loadedCSSAmoled = readCSS("vt_ui_oled.css");
|
loadedCSSAmoled = readCSS("vt_ui_oled.css");
|
||||||
|
isLoaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void inject(WebView webView, String str) {
|
public static void inject(WebView webView, String str) {
|
||||||
|
|||||||
@@ -11,40 +11,32 @@ public class MusicCacheStorageUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||||
public static File getTrackDirById(String trackId) {
|
public static File getTrackDirById(final String trackId) {
|
||||||
File res = new File(getCacheStorageDir(), trackId);
|
final var res = new File(getCacheStorageDir(), trackId);
|
||||||
res.mkdirs();
|
res.mkdirs();
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static File getTrackFile(String trackId) {
|
public static File getTrackFile(final String trackId) {
|
||||||
return new File(getTrackDirById(trackId), "track.mp3");
|
return new File(getTrackDirById(trackId), "track.mp3");
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||||
public static File getThumbDirById(String trackId) {
|
public static File getThumbDirById(final String trackId) {
|
||||||
File res = new File(getTrackDirById(trackId), "thumbs");
|
final var res = new File(getTrackDirById(trackId), "thumbs");
|
||||||
res.mkdirs();
|
res.mkdirs();
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static File getTrackThumb(String trackId, int factor) {
|
public static File getTrackThumb(final String trackId, final int factor) {
|
||||||
return new File(getThumbDirById(trackId), "thumb_" + factor + ".png");
|
return new File(getThumbDirById(trackId), "thumb_" + factor + ".png");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static File getPlaylistThumb(String playlistId, int factor) {
|
public static void removeTrackDirById(final String trackId) {
|
||||||
return new File(getThumbDirById(playlistId), "thumb_playlist_" + factor + ".png");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void removePlaylistThumb(String playlistId) {
|
|
||||||
IOUtils.deleteRecursive(getThumbDirById(playlistId));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void removeTrackDirById(String trackId) {
|
|
||||||
IOUtils.deleteRecursive(getTrackDirById(trackId));
|
IOUtils.deleteRecursive(getTrackDirById(trackId));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void clear() {
|
public static void clear() {
|
||||||
IOUtils.deleteRecursive(getCacheStorageDir());
|
IOUtils.deleteRecursive(getCacheStorageDir());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
package ru.vtosters.lite.utils.music;
|
|
||||||
|
|
||||||
import com.vk.dto.music.MusicTrack;
|
|
||||||
import com.vk.dto.music.Artist;
|
|
||||||
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
public class MusicTrackUtils {
|
|
||||||
public static String getArtists(MusicTrack musicTrack) {
|
|
||||||
return (musicTrack.L == null || musicTrack.I == null
|
|
||||||
? normalizeMetadata(musicTrack.C)
|
|
||||||
: normalizeMetadata(musicTrack.L.stream().map(Artist::w1).collect(Collectors.joining(", "))));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String normalizeMetadata(String in) {
|
|
||||||
return in.replaceAll("[\\\\/:*?\"<>|]", "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
package ru.vtosters.lite.utils.music;
|
|
||||||
|
|
||||||
import android.util.Log;
|
|
||||||
import com.vk.dto.music.Playlist;
|
|
||||||
import org.json.JSONObject;
|
|
||||||
|
|
||||||
public class PlaylistUtils {
|
|
||||||
public static JSONObject getThumb(Playlist playlist) {
|
|
||||||
try {
|
|
||||||
Log.d("Playlist", "thumb is not null");
|
|
||||||
return playlist.F.J();
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.d("Playlist", "thumb is null");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,7 +3,6 @@ package ru.vtosters.sponsorpost.internal;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import ru.vtosters.hooks.other.Preferences;
|
|
||||||
import ru.vtosters.lite.utils.AndroidUtils;
|
import ru.vtosters.lite.utils.AndroidUtils;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@@ -23,7 +22,7 @@ public class VotesPreferences {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isEnabled() {
|
public static boolean isEnabled() {
|
||||||
return preferences.getBoolean("sponsortesting", false) && !Preferences.serverFeaturesDisable();
|
return preferences.getBoolean("sponsortesting", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setEnabled(boolean status) {
|
public static void setEnabled(boolean status) {
|
||||||
|
|||||||
@@ -61,18 +61,6 @@ public class FiltersPreferences {
|
|||||||
editor.apply();
|
editor.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void forceUpdateFilters() {
|
|
||||||
SharedPreferences.Editor editor = preferences.edit();
|
|
||||||
|
|
||||||
preferences.getAll().keySet().stream()
|
|
||||||
.filter(key -> key.endsWith(":" + PREF_KEY_VERSION))
|
|
||||||
.forEach(key -> editor.putString(key, "0"));
|
|
||||||
|
|
||||||
editor.apply();
|
|
||||||
|
|
||||||
Updates.updateFilters();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean getSavedKeyValue(int id) {
|
public static boolean getSavedKeyValue(int id) {
|
||||||
return getFilter(id) != null;
|
return getFilter(id) != null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package ru.vtosters.sponsorpost.utils;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import ru.vtosters.hooks.other.Preferences;
|
|
||||||
import ru.vtosters.lite.utils.AndroidUtils;
|
import ru.vtosters.lite.utils.AndroidUtils;
|
||||||
import ru.vtosters.sponsorpost.data.Post;
|
import ru.vtosters.sponsorpost.data.Post;
|
||||||
|
|
||||||
@@ -24,7 +23,7 @@ public class PostsPreferences {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isEnabled() {
|
public static boolean isEnabled() {
|
||||||
return preferences.getBoolean("sponsorpost", true) && !Preferences.serverFeaturesDisable();
|
return preferences.getBoolean("sponsorpost", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setEnabled(boolean status) {
|
public static void setEnabled(boolean status) {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package ru.vtosters.sponsorpost.utils;
|
package ru.vtosters.sponsorpost.utils;
|
||||||
|
|
||||||
import android.util.Log;
|
|
||||||
import ru.vtosters.lite.utils.NetworkUtils;
|
import ru.vtosters.lite.utils.NetworkUtils;
|
||||||
import ru.vtosters.lite.utils.newsfeed.NewsFeedFiltersUtils;
|
import ru.vtosters.lite.utils.newsfeed.NewsFeedFiltersUtils;
|
||||||
import ru.vtosters.sponsorpost.data.Filter;
|
import ru.vtosters.sponsorpost.data.Filter;
|
||||||
@@ -32,17 +31,14 @@ public class Updates {
|
|||||||
for (Filter filter : filters) {
|
for (Filter filter : filters) {
|
||||||
Filter localFilter = findLocalFilterById(localFilters, filter.getId());
|
Filter localFilter = findLocalFilterById(localFilters, filter.getId());
|
||||||
if (localFilter == null) {
|
if (localFilter == null) {
|
||||||
Log.d("Updates", "local filter is not exist, adding new: " + filter.getId());
|
|
||||||
FiltersPreferences.saveFilter(filter);
|
FiltersPreferences.saveFilter(filter);
|
||||||
} else {
|
} else {
|
||||||
String remoteVersion = filter.getVersion();
|
String remoteVersion = filter.getVersion();
|
||||||
String localVersion = localFilter.getVersion();
|
String localVersion = localFilter.getVersion();
|
||||||
if (Objects.equals(remoteVersion, localVersion)) {
|
if (Objects.equals(remoteVersion, localVersion)) {
|
||||||
Log.d("Updates", "saved local filter: " + localFilter.getId());
|
|
||||||
FiltersPreferences.saveFilter(localFilter);
|
|
||||||
} else {
|
|
||||||
Log.d("Updates", "saved remote filter: " + filter.getId());
|
|
||||||
FiltersPreferences.saveFilter(filter);
|
FiltersPreferences.saveFilter(filter);
|
||||||
|
} else {
|
||||||
|
FiltersPreferences.saveFilter(localFilter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -140,7 +140,6 @@
|
|||||||
<uses-permission android:name="com.mgoogle.android.c2dm.permission.RECEIVE" />
|
<uses-permission android:name="com.mgoogle.android.c2dm.permission.RECEIVE" />
|
||||||
<uses-permission android:name="app.revanced.android.c2dm.permission.RECEIVE" />
|
<uses-permission android:name="app.revanced.android.c2dm.permission.RECEIVE" />
|
||||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
|
||||||
<uses-permission android:name="com.google.android.finsky.permission.BIND_GET_INSTALL_REFERRER_SERVICE" />
|
<uses-permission android:name="com.google.android.finsky.permission.BIND_GET_INSTALL_REFERRER_SERVICE" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
|
|||||||
@@ -75,7 +75,6 @@
|
|||||||
--header_tint: %header_text% !important;
|
--header_tint: %header_text% !important;
|
||||||
--header_tint_alternate: %header_text% !important;
|
--header_tint_alternate: %header_text% !important;
|
||||||
--vkui--color_icon_accent_themed: %header_text% !important;
|
--vkui--color_icon_accent_themed: %header_text% !important;
|
||||||
--vkui--color_text_accent_themed: %header_text% !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -140,7 +139,6 @@
|
|||||||
--header_tint: %header_text% !important;
|
--header_tint: %header_text% !important;
|
||||||
--header_tint_alternate: %header_text% !important;
|
--header_tint_alternate: %header_text% !important;
|
||||||
--vkui--color_icon_accent_themed: %header_text% !important;
|
--vkui--color_icon_accent_themed: %header_text% !important;
|
||||||
--vkui--color_text_accent_themed: %header_text% !important;
|
|
||||||
--color_field_border: %field_border% !important;
|
--color_field_border: %field_border% !important;
|
||||||
--vkui--color_field_border: %field_border% !important;
|
--vkui--color_field_border: %field_border% !important;
|
||||||
--color_field_background: %field_background% !important;
|
--color_field_background: %field_background% !important;
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Regular → Executable
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user