2022-06-24 00:16:32 +02:00
|
|
|
package app.revanced.integrations.utils;
|
|
|
|
|
2022-11-25 00:10:58 +01:00
|
|
|
import android.annotation.SuppressLint;
|
2022-06-24 00:16:32 +02:00
|
|
|
import android.content.Context;
|
2022-06-27 22:08:50 +02:00
|
|
|
import android.content.res.Resources;
|
2022-06-24 00:16:32 +02:00
|
|
|
import android.os.Handler;
|
|
|
|
import android.os.Looper;
|
|
|
|
|
2022-12-31 07:38:47 +01:00
|
|
|
import java.text.Bidi;
|
|
|
|
import java.util.Locale;
|
2022-11-30 19:58:11 +01:00
|
|
|
import java.util.concurrent.Callable;
|
|
|
|
import java.util.concurrent.Future;
|
|
|
|
import java.util.concurrent.LinkedBlockingQueue;
|
2022-12-21 22:19:34 +01:00
|
|
|
import java.util.concurrent.ThreadFactory;
|
2022-11-30 19:58:11 +01:00
|
|
|
import java.util.concurrent.ThreadPoolExecutor;
|
|
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
|
2022-06-27 22:08:50 +02:00
|
|
|
import app.revanced.integrations.sponsorblock.player.PlayerType;
|
2022-06-24 00:16:32 +02:00
|
|
|
|
|
|
|
public class ReVancedUtils {
|
|
|
|
|
2022-06-27 22:08:50 +02:00
|
|
|
private static PlayerType env;
|
2022-11-25 00:10:58 +01:00
|
|
|
private static boolean newVideo = false;
|
2022-06-27 22:08:50 +02:00
|
|
|
|
2022-11-25 00:10:58 +01:00
|
|
|
@SuppressLint("StaticFieldLeak")
|
2022-06-27 22:08:50 +02:00
|
|
|
public static Context context;
|
2022-10-29 01:54:03 +02:00
|
|
|
|
2022-11-25 00:10:58 +01:00
|
|
|
private ReVancedUtils() {
|
|
|
|
} // utility class
|
|
|
|
|
2022-11-30 19:58:11 +01:00
|
|
|
/**
|
|
|
|
* Maximum number of background threads run concurrently
|
|
|
|
*/
|
|
|
|
private static final int SHARED_THREAD_POOL_MAXIMUM_BACKGROUND_THREADS = 20;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* General purpose pool for network calls and other background tasks.
|
2022-12-21 22:19:34 +01:00
|
|
|
* All tasks run at max thread priority.
|
2022-11-30 19:58:11 +01:00
|
|
|
*/
|
|
|
|
private static final ThreadPoolExecutor backgroundThreadPool = new ThreadPoolExecutor(
|
2022-12-21 22:19:34 +01:00
|
|
|
1, // minimum 1 thread always ready to be used
|
2022-11-30 19:58:11 +01:00
|
|
|
10, // For any threads over the minimum, keep them alive 10 seconds after they go idle
|
|
|
|
SHARED_THREAD_POOL_MAXIMUM_BACKGROUND_THREADS,
|
|
|
|
TimeUnit.SECONDS,
|
2022-12-21 22:19:34 +01:00
|
|
|
new LinkedBlockingQueue<Runnable>(),
|
|
|
|
new ThreadFactory() {
|
|
|
|
@Override
|
|
|
|
public Thread newThread(Runnable r) {
|
|
|
|
Thread t = new Thread(r);
|
|
|
|
t.setPriority(Thread.MAX_PRIORITY); // run at max priority
|
|
|
|
return t;
|
|
|
|
}
|
|
|
|
});
|
2022-11-30 19:58:11 +01:00
|
|
|
|
|
|
|
private static void checkIfPoolHasReachedLimit() {
|
|
|
|
if (backgroundThreadPool.getActiveCount() >= SHARED_THREAD_POOL_MAXIMUM_BACKGROUND_THREADS) {
|
|
|
|
// Something is wrong. Background threads are piling up and not completing as expected,
|
|
|
|
// or some ReVanced code is submitting an unexpected number of background tasks.
|
|
|
|
LogHelper.printException(() -> "Reached maximum background thread count of "
|
|
|
|
+ SHARED_THREAD_POOL_MAXIMUM_BACKGROUND_THREADS + " threads");
|
|
|
|
|
|
|
|
// Because this condition will manifest as a slow running app or a memory leak,
|
|
|
|
// it might be best to show the user a toast or some other suggestion to restart the app.
|
|
|
|
// TODO? if debug is enabled, show a toast?
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public static void runOnBackgroundThread(Runnable task) {
|
|
|
|
backgroundThreadPool.execute(task);
|
2022-12-21 22:19:34 +01:00
|
|
|
checkIfPoolHasReachedLimit();
|
2022-11-30 19:58:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public static <T> Future<T> submitOnBackgroundThread(Callable<T> call) {
|
2022-12-21 22:19:34 +01:00
|
|
|
Future<T> future = backgroundThreadPool.submit(call);
|
2022-11-30 19:58:11 +01:00
|
|
|
checkIfPoolHasReachedLimit();
|
2022-12-21 22:19:34 +01:00
|
|
|
return future;
|
2022-11-30 19:58:11 +01:00
|
|
|
}
|
|
|
|
|
2022-10-29 01:54:03 +02:00
|
|
|
public static boolean containsAny(final String value, final String... targets) {
|
|
|
|
for (String string : targets)
|
2022-11-19 23:22:34 +01:00
|
|
|
if (!string.isEmpty() && value.contains(string)) return true;
|
2022-10-29 01:54:03 +02:00
|
|
|
return false;
|
|
|
|
}
|
2022-06-27 22:08:50 +02:00
|
|
|
|
2022-07-05 22:31:13 +02:00
|
|
|
public static void setNewVideo(boolean started) {
|
2022-11-30 00:49:26 +01:00
|
|
|
LogHelper.printDebug(() -> "New video started: " + started);
|
2022-07-05 22:31:13 +02:00
|
|
|
newVideo = started;
|
|
|
|
}
|
2022-10-29 01:54:03 +02:00
|
|
|
|
2022-07-05 22:31:13 +02:00
|
|
|
public static boolean isNewVideoStarted() {
|
|
|
|
return newVideo;
|
|
|
|
}
|
|
|
|
|
2022-07-11 14:29:39 +02:00
|
|
|
public static Integer getResourceIdByName(Context context, String type, String name) {
|
|
|
|
try {
|
|
|
|
Resources res = context.getResources();
|
|
|
|
return res.getIdentifier(name, type, context.getPackageName());
|
|
|
|
} catch (Throwable exception) {
|
2022-11-30 00:49:26 +01:00
|
|
|
LogHelper.printException(() -> ("Resource not found."), exception);
|
2022-07-11 14:29:39 +02:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-27 22:08:50 +02:00
|
|
|
public static void setPlayerType(PlayerType type) {
|
|
|
|
env = type;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static PlayerType getPlayerType() {
|
|
|
|
return env;
|
|
|
|
}
|
|
|
|
|
2022-06-24 00:16:32 +02:00
|
|
|
public static int getIdentifier(String name, String defType) {
|
2022-06-27 22:08:50 +02:00
|
|
|
Context context = getContext();
|
2022-06-24 00:16:32 +02:00
|
|
|
return context.getResources().getIdentifier(name, defType, context.getPackageName());
|
|
|
|
}
|
|
|
|
|
2022-06-27 22:08:50 +02:00
|
|
|
public static Context getContext() {
|
|
|
|
if (context != null) {
|
|
|
|
return context;
|
|
|
|
} else {
|
2022-11-30 00:49:26 +01:00
|
|
|
LogHelper.printException(() -> ("Context is null, returning null!"));
|
2022-06-27 22:08:50 +02:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
2022-07-14 18:42:43 +02:00
|
|
|
|
|
|
|
public static boolean isTablet(Context context) {
|
|
|
|
return context.getResources().getConfiguration().smallestScreenWidthDp >= 600;
|
|
|
|
}
|
2022-11-25 00:10:58 +01:00
|
|
|
|
2022-12-31 07:38:47 +01:00
|
|
|
private static final boolean isRightToLeftTextLayout =
|
|
|
|
new Bidi(Locale.getDefault().getDisplayLanguage(), Bidi.DIRECTION_DEFAULT_RIGHT_TO_LEFT).isRightToLeft();
|
|
|
|
/**
|
|
|
|
* If the device language uses right to left text layout (hebrew, arabic, etc)
|
|
|
|
*/
|
|
|
|
public static boolean isRightToLeftTextLayout() {
|
|
|
|
return isRightToLeftTextLayout;
|
|
|
|
}
|
|
|
|
|
2022-12-21 22:19:34 +01:00
|
|
|
/**
|
|
|
|
* Automatically logs any exceptions the runnable throws
|
|
|
|
*/
|
2022-11-25 00:10:58 +01:00
|
|
|
public static void runOnMainThread(Runnable runnable) {
|
2022-12-21 22:19:34 +01:00
|
|
|
Runnable exceptLoggingRunnable = () -> {
|
|
|
|
try {
|
|
|
|
runnable.run();
|
|
|
|
} catch (Exception ex) {
|
|
|
|
LogHelper.printException(() -> "Exception on main thread from runnable: " + runnable.toString(), ex);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
new Handler(Looper.getMainLooper()).post(exceptLoggingRunnable);
|
2022-11-25 00:10:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return if the calling thread is on the main thread
|
|
|
|
*/
|
|
|
|
public static boolean currentIsOnMainThread() {
|
|
|
|
return Looper.getMainLooper().isCurrentThread();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @throws IllegalStateException if the calling thread is _not_ on the main thread
|
|
|
|
*/
|
|
|
|
public static void verifyOnMainThread() throws IllegalStateException {
|
|
|
|
if (!currentIsOnMainThread()) {
|
|
|
|
throw new IllegalStateException("Must call _on_ the main thread");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @throws IllegalStateException if the calling thread _is_ on the main thread
|
|
|
|
*/
|
|
|
|
public static void verifyOffMainThread() throws IllegalStateException {
|
|
|
|
if (currentIsOnMainThread()) {
|
|
|
|
throw new IllegalStateException("Must call _off_ the main thread");
|
|
|
|
}
|
|
|
|
}
|
2022-06-24 00:16:32 +02:00
|
|
|
}
|