Compare commits

...
Author SHA1 Message Date
Denis 3068ff76c1 Fix crash (#1542) 2023-12-31 13:04:55 +03:00
gdlbo a6b56e0e87 Profiles/Groups resp fixes (#1532) 2023-12-13 23:08:32 +03:00
gdlbo 5360ca6c5e updated translations 2023-12-13 23:07:40 +03:00
gdlbo f2ac845797 Update FFMpeg.java 2023-10-14 16:05:51 +03:00
gdlbo a85e55a594 meta data pref 2023-10-12 22:45:43 +03:00
gdlbo e2775db4e5 fixed english translation 2023-10-12 20:19:17 +03:00
gdlbo 589e8928a0 Bitrate upgrade (#1498)
libvkmedia.so armv7/arm64
2023-10-12 19:57:00 +03:00
gdlbo cd189b1dda fixed id3v2 meta 2023-10-10 23:15:11 +03:00
gdlbo 14c111a3e6 some music fixes 2023-10-10 22:53:16 +03:00
gdlbo b506c56346 fixed compiling issue 2023-10-07 03:13:24 +03:00
gdlbo bbbc94e510 added facebook graph disabler 2023-10-07 03:07:31 +03:00
gdlbo 8cb2c380e7 fixed adding "null" to track name 2023-10-06 12:44:55 +03:00
gdlbo b9879f9ed7 updated appcenter 2023-10-04 19:44:24 +03:00
gdlbo cf05e051e8 added summary for advanced online info 2023-10-04 13:47:47 +03:00
gdlbo 963c4d81ba upped ver 2023-10-04 13:37:20 +03:00
gdlbo 099c699702 added vkn online fix disabler 2023-10-04 13:37:07 +03:00
gdlbo 1117fc19d4 added force tablet mode 2023-10-02 16:28:08 +03:00
gdlbo 78dca26063 fix #1489 2023-10-02 14:52:52 +03:00
gdlbo f5180f61d5 fixed empty text from ssfs 2023-09-30 14:37:49 +03:00
gdlbo 540fffdcc9 fixed download issue 2023-09-30 03:15:14 +03:00
gdlbo 5357c60eaa Update TelegramStickersGrabber.java 2023-09-30 02:33:52 +03:00
gdlbo c59f29121f Refactoted Telegram Stickers downloader and settings (#1485) 2023-09-29 19:58:33 +03:00
gdlbo bd38cab377 autoupdated disabler 2023-09-28 17:40:49 +03:00
gdlbo 0058431590 deleted useless summ and updated translation 2023-09-28 17:01:38 +03:00
gdlbo bf4cf2be53 adfree + free from server connection (#1481) 2023-09-28 16:10:30 +03:00
gdlbo aac86a9952 Update InstallGMSFragment.java
added inotia00 microg information
2023-09-28 11:09:40 +03:00
gdlbo ae00e72bac added more microg instructions 2023-09-27 23:55:56 +03:00
gdlbo fea984f6fd fixed null foaf crash app 2023-09-27 23:34:37 +03:00
gdlbo d870c2f984 Update VTSettings.java 2023-09-27 22:12:12 +03:00
gdlbo f7f43928cf cleanup and refactor code
also fixed ts merging
2023-09-25 14:44:27 +03:00
Vologhat 1accb45f12 Merge remote-tracking branch 'origin/main' 2023-09-24 18:43:40 +03:00
Vologhat d9f53920e5 optimize music cache logic 2023-09-24 18:39:12 +03:00
gdlbo 76d91332d7 added recommendation icons 2023-09-23 16:04:45 +03:00
gdlbo 728cbde683 added xiaomi battery dozing information 2023-09-23 14:58:13 +03:00
gdlbo 60c98cd998 some settings improves 2023-09-23 14:50:18 +03:00
gdlbo f1d106e0f0 added gms info in device info
installed microg/gms and push service
2023-09-23 11:17:43 +03:00
gdlbo c92194e90b app.revanced.android.gms support (#1474) 2023-09-23 10:53:05 +03:00
gdlbo 91683af807 deleted useless file 2023-09-23 01:50:43 +03:00
gdlbo c5c898aa4e added recommendations for app fixes + added more gmsless instructions 2023-09-23 01:22:13 +03:00
gdlbo 25634b2469 cache info/clean improvements 2023-09-22 23:27:08 +03:00
gdlbo b8e764d09d Update AboutAppFragment.smali 2023-09-15 19:31:09 +03:00
gdlbo 17ed32857a fixed information about cache settings 2023-09-15 19:05:34 +03:00
gdlbo d631d07e0f removed useless update toast 2023-09-15 19:03:14 +03:00
gdlbo 2ec4c69832 added "Data and storage" item to settings 2023-09-15 18:40:38 +03:00
abslike 6426c965c6 Fix button black text in bots 2023-09-15 18:38:27 +03:00
gdlbo 4f6d410185 stories disabled by default 2023-09-15 18:09:29 +03:00
gdlbo 18a899f439 updated and refactored vt settings 2023-09-15 18:08:28 +03:00
gdlbo 7e5b3fe233 superapp audio item fix 2023-09-14 12:08:16 +03:00
gdlbo 4abd0eec6c fix compile 2023-09-14 11:28:08 +03:00
135 changed files with 3011 additions and 4855 deletions
+8 -2
View File
@@ -84,9 +84,15 @@ android {
}
}
task testClasses {
doLast {
println 'This is a dummy testClasses task'
}
}
dependencies {
implementation 'com.microsoft.appcenter:appcenter-analytics:5.0.1'
implementation 'com.microsoft.appcenter:appcenter-crashes:5.0.1'
implementation 'com.microsoft.appcenter:appcenter-analytics:5.0.2'
implementation 'com.microsoft.appcenter:appcenter-crashes:5.0.2'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.3'
implementation 'net.sourceforge.streamsupport:streamsupport-minifuture:1.7.4'
implementation 'com.google.guava:guava:23.3-android'
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -10,9 +10,9 @@ import com.vk.music.common.MusicPlaybackLaunchContext;
import java.util.ArrayList;
import java.util.List;
import static ru.vtosters.lite.utils.AndroidUtils.getGlobalContext;
import static ru.vtosters.hooks.other.Preferences.getBoolValue;
import static ru.vtosters.hooks.other.Preferences.isValidSignature;
import static ru.vtosters.lite.utils.AndroidUtils.getGlobalContext;
public class LibVKXClient {
@SuppressLint("StaticFieldLeak")
@@ -80,11 +80,6 @@ public class LibVKXClient {
lambdaplay(list, musicTrack, iLibVkxService, null);
}
public static String asId(MusicTrack musicTrack) {
String id = musicTrack.e + "_" + musicTrack.d;
if (musicTrack.O != null && !musicTrack.O.equals("")) {
id += "_" + musicTrack.O;
}
return id;
}
public static String asId(MusicTrack musicTrack)
{ return musicTrack.y1(); }
}
@@ -36,8 +36,8 @@ public class TelegramStickersPack extends CustomStickersPack {
public HashMap<String, List<Integer>> emojis = new HashMap<>();
public TelegramStickersPack(String id) {
this.id=this.title=id;
this.hash=id.hashCode();
this.id = this.title = id;
this.hash = id.hashCode();
}
public JSONObject encodeEmojis() throws JSONException {
@@ -26,19 +26,13 @@ import static ru.vtosters.lite.utils.AndroidUtils.getGlobalContext;
public class TelegramStickersService {
private static final String TAG = "TGStickersService";
private static TelegramStickersService instance;
private final Context c;
private final ArrayList<TelegramStickersPack> packs;
private final ArrayList<TelegramStickersPack> activePacks;
private final ArrayList<TelegramStickersPack> inactivePacks;
private final ArrayList<StickersEventsListener> listeners;
private final HashSet<String> currentlyDownloading;
private final Handler uiThreadHandler;
private final TelegramStickersGrabber grabber;
private final TelegramStickersDbHelper dbHelper;
private final ArrayList<Runnable> queuedTasks;
private final ThreadPoolExecutor executor;
@@ -48,16 +42,14 @@ public class TelegramStickersService {
private TelegramStickersService(Context context) {
instance = this;
this.c = context.getApplicationContext();
listeners = new ArrayList<>();
currentlyDownloading = new HashSet<>();
uiThreadHandler = new Handler(Looper.getMainLooper());
grabber = new TelegramStickersGrabber(null);
dbHelper = new TelegramStickersDbHelper(c);
dbHelper = new TelegramStickersDbHelper(context);
queuedTasks = new ArrayList<>();
executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(2);
notificationsHelper = new NotificationsHelper(c);
notificationsHelper = new NotificationsHelper(context);
packs = new ArrayList<>();
activePacks = new ArrayList<>();
@@ -70,11 +62,11 @@ public class TelegramStickersService {
return instance == null ? new TelegramStickersService(c) : instance;
}
private void updatePacks(final boolean notify) {
private void updatePacks(boolean notify) {
packs.clear();
dbHelper.getAllPacks(new TelegramStickersDbHelper.PacksLoadingListener() {
@Override
public void onPackLoaded(final TelegramStickersPack pack) {
public void onPackLoaded(TelegramStickersPack pack) {
runOnUiThread(() -> {
packs.add(pack);
@@ -103,14 +95,6 @@ public class TelegramStickersService {
});
}
public void addStickersEventsListener(StickersEventsListener listener) {
listeners.add(listener);
}
public void removeStickersEventsListener(StickersEventsListener listener) {
listeners.remove(listener);
}
private void notifyPackAdded(TelegramStickersPack pack, int atIndex) {
getGlobalContext().sendBroadcast(new Intent(StickersFragment.ACTION_RELOAD));
if (listeners.isEmpty())
@@ -188,20 +172,16 @@ public class TelegramStickersService {
return inactivePacks;
}
public boolean isDoneLoading() {
return ready;
}
public boolean requestPackDownload(final String id, File packFolder) {
public void requestPackDownload(String id, File packFolder) {
if (currentlyDownloading.contains(id)) {
Log.e(TAG, String.format("Got request to download pack %s which is already downloading", id));
return false;
return;
}
if (!ready) {
final File folder = packFolder;
File folder = packFolder;
queuedTasks.add(() -> requestPackDownload(id, folder));
return true;
return;
}
TelegramStickersPack pack = new TelegramStickersPack(id);
@@ -221,85 +201,86 @@ public class TelegramStickersService {
notifyPackAdded(pack, packs.size() - 1);
}
final TelegramStickersPack newPack = pack;
final boolean isUpdate = update;
TelegramStickersPack newPack = pack;
boolean isUpdate = update;
currentlyDownloading.add(id);
notificationsHelper.packStartedDownloading(newPack);
grabber.enableProxy();
grabber.grabPack(id, packFolder, pack.version, new TelegramStickersGrabber.PackDownloadListener() {
@Override
public void onPackDownloaded(TelegramStickersPackInfo packInfo, boolean newVersionFound) {
runOnUiThread(() -> {
newPack.state = TelegramStickersPack.DOWNLOADED;
notifyPackChanged(newPack, packs.indexOf(newPack));
currentlyDownloading.remove(newPack.id);
syncPack(newPack);
if (isUpdate) {
if (newPack.enabled)
notifyActivePacksListChanged();
} else {
activePacks.add(newPack);
notifyActivePacksListChanged();
}
});
notificationsHelper.packDoneDownloading(newPack, true, isUpdate, null);
}
@Override
public void onPackDownloadError(final Exception e) {
runOnUiThread(() -> {
Log.e(TAG, "Error while downloading pack " + newPack.id);
e.printStackTrace();
if (!isUpdate) {
int index = packs.indexOf(newPack);
packs.remove(index);
notifyPackRemoved(newPack, index);
} else {
// try-catch block to handle possible timeout exception
try {
grabber.grabPack(id, packFolder, pack.version, new TelegramStickersGrabber.PackDownloadListener() {
@Override
public void onPackDownloaded(TelegramStickersPackInfo packInfo, boolean newVersionFound) {
runOnUiThread(() -> {
newPack.state = TelegramStickersPack.DOWNLOADED;
notifyPackChanged(newPack, packs.indexOf(newPack));
}
currentlyDownloading.remove(newPack.id);
syncPack(newPack);
notifyPackDownloadError(newPack, e);
currentlyDownloading.remove(newPack.id);
});
notificationsHelper.packDoneDownloading(newPack, false, isUpdate, e);
}
if (isUpdate) {
if (newPack.enabled)
notifyActivePacksListChanged();
} else {
activePacks.add(newPack);
notifyActivePacksListChanged();
}
});
notificationsHelper.packDoneDownloading(newPack, true, isUpdate, null);
}
@Override
public void onGotPackInfo(final TelegramStickersPackInfo packInfo) {
runOnUiThread(() -> {
newPack.title = packInfo.title;
newPack.stickersCount = packInfo.stickersCount;
newPack.version = packInfo.version;
notifyPackChanged(newPack, packs.indexOf(newPack));
});
@Override
public void onPackDownloadError(Exception e) {
runOnUiThread(() -> {
Log.e(TAG, "Error while downloading pack " + newPack.id);
e.printStackTrace();
notificationsHelper.packDownloadUpdated(newPack, 0);
}
if (!isUpdate) {
int index = packs.indexOf(newPack);
packs.remove(index);
notifyPackRemoved(newPack, index);
} else {
newPack.state = TelegramStickersPack.DOWNLOADED;
notifyPackChanged(newPack, packs.indexOf(newPack));
}
@Override
public void onStickerDownloaded(String pack, File sticker, String boundEmoji, int stickerIndex, int downloadedStickersCount, int totalStickersCount) {
stickerIndex--;
notifyPackDownloadError(newPack, e);
currentlyDownloading.remove(newPack.id);
});
notificationsHelper.packDoneDownloading(newPack, false, isUpdate, e);
}
List<Integer> list = newPack.emojis.get(boundEmoji);
if (list == null) list = new ArrayList<>();
list.add(stickerIndex);
newPack.emojis.put(boundEmoji, list);
@Override
public void onGotPackInfo(TelegramStickersPackInfo packInfo) {
runOnUiThread(() -> {
newPack.title = packInfo.title;
newPack.stickersCount = packInfo.stickersCount;
newPack.version = packInfo.version;
notifyPackChanged(newPack, packs.indexOf(newPack));
});
notificationsHelper.packDownloadUpdated(newPack, downloadedStickersCount);
}
});
notificationsHelper.packDownloadUpdated(newPack, 0);
}
return true;
@Override
public void onStickerDownloaded(String pack, File sticker, String boundEmoji, int stickerIndex, int downloadedStickersCount, int totalStickersCount) {
stickerIndex--;
List<Integer> list = newPack.emojis.get(boundEmoji);
if (list == null) list = new ArrayList<>();
list.add(stickerIndex);
newPack.emojis.put(boundEmoji, list);
notificationsHelper.packDownloadUpdated(newPack, downloadedStickersCount);
}
});
} catch (Exception e) {
// handle other possible exceptions
e.printStackTrace();
}
}
public void setPackEnabled(final TelegramStickersPack pack, final boolean enabled, final boolean notify) {
public void setPackEnabled(TelegramStickersPack pack, boolean enabled, boolean notify) {
if (!ready) {
queuedTasks.add(() -> setPackEnabled(pack, enabled, notify));
return;
@@ -325,7 +306,7 @@ public class TelegramStickersService {
}
}
public void deletePack(final TelegramStickersPack pack) {
public void deletePack(TelegramStickersPack pack) {
if (!ready) {
queuedTasks.add(() -> deletePack(pack));
return;
@@ -334,7 +315,6 @@ public class TelegramStickersService {
boolean d = dbHelper.deletePack(pack);
Log.d(TAG, "Delete from DB=" + d);
int index = packs.indexOf(pack);
packs.remove(index);
notifyPackRemoved(pack, index);
@@ -355,7 +335,7 @@ public class TelegramStickersService {
uiThreadHandler.post(r);
}
private void syncPack(final TelegramStickersPack pack) {
private void syncPack(TelegramStickersPack pack) {
if (!ready) {
queuedTasks.add(() -> syncPack(pack));
return;
@@ -397,6 +377,4 @@ public class TelegramStickersService {
void onInactivePacksListChanged();
}
}
@@ -7,23 +7,20 @@ import android.os.Looper;
import android.util.Log;
import com.aefyr.tsg.g2.stickersgrabber.util.Flag;
import com.aefyr.tsg.g2.stickersgrabber.util.GoalCounter;
import okhttp3.*;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import ru.vtosters.lite.net.*;
import ru.vtosters.lite.di.singleton.VtOkHttpClient;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.PasswordAuthentication;
import java.net.Proxy;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
/**
@@ -31,14 +28,19 @@ import java.util.concurrent.atomic.AtomicInteger;
*/
public class TelegramStickersGrabber {
private static final String TAG = "TSG";
public static String REAL_TG_IP = null;
public static String PROXY_IP = null;
public static int PROXY_PORT = -1;
public static String PROXY_USER = null;
public static String PROXY_PASS = null;
public static boolean PROXY_SOCKS = true;
public static boolean HAS_PASS = false;
public static boolean USE_PROXY = false;
private static final OkHttpClient sClient = VtOkHttpClient.getInstance();
private static final int MAX_RETRIES = 2;
private static final AtomicInteger retryCount = new AtomicInteger();
private static final int STICKER_QUALITY = 100;
private static final String STICKER_FILE_NAME_FORMAT = "%03d.png";
private static final String STICKERS_KEY = "stickers";
private static final String FILE_ID_KEY = "file_id";
private static final String EMOJI_KEY = "emoji";
private static final String IS_ANIMATED_KEY = "is_animated";
private static final String IS_VIDEO_KEY = "is_video";
private static final String THUMB_KEY = "thumb";
private static final String NAME_KEY = "name";
private static final String TITLE_KEY = "title";
private static String BOT_API_BASE_URL;
private static String GET_STICKER_SET_URL;
private static String GET_FILE_URL;
@@ -50,12 +52,10 @@ public class TelegramStickersGrabber {
private final MessageDigest sha256;
private final Handler uiThreadHandler;
private String botApiKey = "";
private NetClient httpClient;
private String botApiKey;
public TelegramStickersGrabber(String botApiKey) {
this.botApiKey = botApiKey;
httpClient = new NetClient.Builder().connectTimeout(5, TimeUnit.SECONDS).build();
uiThreadHandler = new Handler(Looper.getMainLooper());
try {
sha256 = MessageDigest.getInstance("SHA-256");
@@ -69,81 +69,12 @@ public class TelegramStickersGrabber {
public static void updateURLs() {
BOT_API_BASE_FILE_URL = "https://api.telegram.org/file/bot%s/%s";
BOT_API_BASE_URL = REAL_TG_IP != null ? "https://" + REAL_TG_IP + "/bot%s/" : "https://api.telegram.org/bot%s/";
BOT_API_BASE_URL = "https://api.telegram.org/bot%s/";
GET_STICKER_SET_URL = BOT_API_BASE_URL + "getStickerSet?name=%s";
GET_FILE_URL = BOT_API_BASE_URL + "getFile?file_id=%s";
}
public static boolean isTelegramBlocked() {
NetClient client = new NetClient.Builder().connectTimeout(5, TimeUnit.SECONDS).build();
try {
if (PROXY_IP != null) {
Socket socket = new Socket(PROXY_IP, PROXY_PORT);
Proxy proxy = new Proxy(Proxy.Type.SOCKS, socket.getRemoteSocketAddress());
NetClient.Builder b = new NetClient.Builder().proxy(proxy);
if (PROXY_USER != null) {
b = b.proxyAuthenticator(new PasswordAuthentication(PROXY_USER, PROXY_PASS.toCharArray()));
}
client = b.build();
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
NetRequest req = new NetRequest.Builder().url(REAL_TG_IP != null ? "https://" + REAL_TG_IP + "/test/" : "https://api.telegram.org/test/").build();
try {
NetResponse res = client.newCall(req).execute();
String s = res.getDataString();
if (!s.equals("{\"ok\":false,\"error_code\":404,\"description\":\"Not Found\"}"))
return true;
} catch (Exception e) {
return true;
}
return false;
}
public void resetProxy() {
httpClient = new NetClient.Builder().connectTimeout(5, TimeUnit.SECONDS).build();
}
public void enableProxy() {
Thread kostil = new Thread() {
@Override
public void run() {
try {
if (PROXY_IP != null) {
Socket socket = new Socket(PROXY_IP, PROXY_PORT);
Proxy proxy = new Proxy(Proxy.Type.SOCKS, socket.getRemoteSocketAddress());
NetClient.Builder b = httpClient.newBuilder().proxy(proxy);
if (PROXY_USER != null) {
httpClient = b.proxyAuthenticator(new PasswordAuthentication(PROXY_USER, PROXY_PASS.toCharArray())).build();
}
httpClient = b.build();
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
};
kostil.start();
try {
kostil.join();
Log.d(TAG, "Proxy set");
} catch (InterruptedException e) {
Log.e(TAG, "Unable to set proxy:");
e.printStackTrace();
}
}
public void grabPack(String id, File packFolder, String installedVersion, final PackDownloadListener listener) {
public void grabPack(String id, File packFolder, String installedVersion, PackDownloadListener listener) {
getPackInfo(id, packFolder, installedVersion, listener);
}
@@ -151,264 +82,247 @@ public class TelegramStickersGrabber {
botApiKey = key;
}
public void checkKey(final KeyCheckListener listener) {
public void checkKey(KeyCheckListener listener) {
Log.d(TAG, "Checking key: " + botApiKey);
NetRequest request = new NetRequest.Builder().get().url(String.format(BOT_API_BASE_URL + "getMe", botApiKey)).build();
httpClient.newCall(request).enqueue(new NetCallback() {
// Create a request object with the URL and bot API key
Request request = new Request.a()
.b(String.format(BOT_API_BASE_URL + "getMe", botApiKey))
.a();
// Create a new call object and enqueue it to run asynchronously
sClient.a(request).a(new Callback() {
@Override
public void onFailure(NetCall call, IOException e) {
e.printStackTrace();
public void a(Call call, IOException e) {
// Handle the failure case
Log.d(TAG, e.getMessage());
runOnUiThread(listener::onNetError);
}
@Override
public void onResponse(NetCall call, final NetResponse response) throws IOException {
runOnUiThread(() -> listener.onKeyChecked(response.code() == 200));
public void a(Call call, Response response) {
Log.d(TAG, response.toString());
runOnUiThread(() -> listener.onKeyChecked(response.h()));
}
});
}
private void getPackInfo(final String packName, final File packFolder, final String installedVersion, final PackDownloadListener listener) {
NetRequest packInfoRequest = new NetRequest.Builder().get().url(String.format(GET_STICKER_SET_URL, botApiKey, packName)).build();
httpClient.newCall(packInfoRequest).enqueue(new NetCallback() {
private void getPack(StickerSet set, File packFolder, TelegramStickersPackInfo packInfo, PackDownloadListener listener) {
GoalCounter downloadedStickers = new GoalCounter(set.stickers.size(), () -> {
Log.d(TAG, String.format("Pack %s has been downloaded to %s", set.id, packFolder.getAbsolutePath()));
listener.onPackDownloaded(packInfo, true);
});
Flag deathFlag = new Flag();
for (int i = 0; i < set.stickers.size(); i++) {
Sticker sticker = set.stickers.get(i);
String getFileUrl = String.format(GET_FILE_URL, botApiKey, sticker.fileId);
Request fileInfoRequest = new Request.a()
.b(getFileUrl)
.a();
int stickerIndex = i + 1;
sClient.a(fileInfoRequest).a(new Callback() {
@Override
public void a(Call call, IOException e) {
handleFailure(deathFlag, listener, e, downloadedStickers, call, fileInfoRequest, this);
}
@Override
public void a(Call call, Response response) {
if (deathFlag.up())
return;
if (!response.h()) {
retryRequest(call, fileInfoRequest, this);
return;
}
try (ResponseBody responseBody = response.a()) {
JSONObject fileInfoResponse = new JSONObject(responseBody.g()).getJSONObject("result");
String filePath = fileInfoResponse.getString("file_path");
if (botApiKey == null) botApiKey = "";
String fileDownloadUrl = String.format(BOT_API_BASE_FILE_URL, botApiKey, filePath);
Request fileDownloadRequest = new Request.a()
.b(fileDownloadUrl)
.a();
sClient.a(fileDownloadRequest).a(new Callback() {
@Override
public void a(Call call, IOException e) {
handleFailure(deathFlag, listener, e, downloadedStickers, call, fileDownloadRequest, this);
}
@Override
public void a(Call call, Response response) {
if (deathFlag.up())
return;
if (!response.h()) {
retryRequest(call, fileDownloadRequest, this);
return;
}
try (ResponseBody responseBody = response.a()) {
if (responseBody == null) {
handleFailure(deathFlag, listener, new TSGException("Response body for a sticker is null :/"), downloadedStickers, call, fileDownloadRequest, this);
return;
}
InputStream stickerInputStream = responseBody.a();
Bitmap stickerImage = BitmapFactory.decodeStream(stickerInputStream);
if (stickerImage == null) {
handleFailure(deathFlag, listener, new TSGException("Unable to decode sticker image"), downloadedStickers, call, fileDownloadRequest, this);
return;
}
File stickerFile = new File(packFolder, String.format(STICKER_FILE_NAME_FORMAT, stickerIndex));
try {
saveStickerImage(stickerImage, stickerFile);
} catch (TSGException e) {
throw new RuntimeException(e);
}
listener.onStickerDownloaded(set.name, stickerFile, sticker.emoji, stickerIndex,
downloadedStickers.value() + 1, set.stickers.size());
downloadedStickers.increase();
}
}
});
} catch (Exception e) {
handleFailure(deathFlag, listener, e, downloadedStickers, call, fileInfoRequest, this);
}
}
});
}
}
private void handleFailure(Flag deathFlag, PackDownloadListener listener, Exception e, GoalCounter downloadedStickers, Call call, Request request, Callback callback) {
if ((e instanceof IOException || e instanceof TSGException) && retryCount.get() > MAX_RETRIES) {
Log.e(TAG, "Unable to download sticker: " + e.getMessage());
downloadedStickers.increase();
return;
} else if (retryCount.get() < MAX_RETRIES) {
retryRequest(call, request, callback);
return;
}
if (deathFlag.up()) {
return;
}
deathFlag.raise();
listener.onPackDownloadError(e);
}
private void retryRequest(Call call, Request request, Callback callback) {
retryCount.incrementAndGet();
if (retryCount.get() > MAX_RETRIES) {
Log.e(TAG, "retryRequest: max retries");
callback.a(call, new IOException());
} else {
Log.e(TAG, "retryRequest");
sClient.a(request).a(callback);
}
}
private void saveStickerImage(Bitmap image, File file) throws TSGException {
try (FileOutputStream fos = new FileOutputStream(file)) {
image.compress(Bitmap.CompressFormat.PNG, 100, fos);
} catch (IOException e) {
throw new TSGException("Unable to save sticker image");
}
}
private void getPackInfo(String packName, File packFolder, String installedVersion, PackDownloadListener listener) {
String getStickerSetUrl = String.format(GET_STICKER_SET_URL, botApiKey, packName);
Request packInfoRequest = new Request.a()
.b(getStickerSetUrl)
.a();
sClient.a(packInfoRequest).a(new Callback() {
@Override
public void onFailure(NetCall call, IOException e) {
public void a(Call call, IOException e) {
Log.d(TAG, e.getMessage());
listener.onPackDownloadError(e);
}
@Override
public void onResponse(NetCall call, NetResponse response) {
if (!response.isSuccessful()) {
if (response.getData() != null)
listener.onPackDownloadError(new TSGException(response.getDataString() + "\nURL: " + call.request().url()));
else
listener.onPackDownloadError(new TSGException("Unknown Exception, no response body"));
public void a(Call call, Response response) throws IOException {
Log.d(TAG, response.toString());
if (!response.h()) {
String errorMessage = response.a() != null ? response.a().g() + "\nURL: " + call.m0().g() : "Unknown Exception, no response body";
listener.onPackDownloadError(new TSGException(errorMessage));
return;
}
StickerSet set;
try {
JSONObject jPackInfo = new JSONObject(response.getDataString()).getJSONObject("result");
String packVersion = new String(sha256.digest(jPackInfo.toString().getBytes(StandardCharsets.UTF_8)));
try (ResponseBody responseBody = response.a()) {
JSONObject packInfoResponse = new JSONObject(responseBody.g()).getJSONObject("result");
String packVersion = new String(sha256.digest(packInfoResponse.toString().getBytes(StandardCharsets.UTF_8)));
if (packVersion.equals(installedVersion)) {
listener.onPackDownloaded(null, false);
return;
}
JSONArray jStickers = jPackInfo.getJSONArray("stickers");
JSONArray stickersArray = packInfoResponse.getJSONArray(STICKERS_KEY);
if (jStickers.length() == 0) {
if (stickersArray.length() == 0) {
listener.onPackDownloadError(new TSGException("No stickers in pack"));
return;
}
ArrayList<Sticker> stickers = new ArrayList<>(jStickers.length());
ArrayList<Sticker> stickers = parseStickers(stickersArray);
Log.d(TAG, String.format("Parsing stickers in pack %s", packName));
StickerSet set = new StickerSet(packInfoResponse.getString(NAME_KEY), packInfoResponse.getString(TITLE_KEY), packVersion, stickers);
for (int i = 0; i < jStickers.length(); i++) {
JSONObject jSticker = jStickers.getJSONObject(i);
if (jSticker.optBoolean("is_animated") || jSticker.optBoolean("is_video")) {
if (jSticker.has("thumb")) {
stickers.add(new Sticker(jSticker.getJSONObject("thumb").getString("file_id"), jSticker.getString("emoji")));
} else {
listener.onPackDownloadError(new TSGException("Animated and video stickerpacks without thumbs are not supported!"));
return;
}
} else {
stickers.add(new Sticker(jSticker.getString("file_id"), jSticker.getString("emoji")));
}
if (!packFolder.exists() && !packFolder.mkdirs()) {
// Handle the folder creation failure case
listener.onPackDownloadError(new IOException("Can't create folder for the pack!"));
return;
}
set = new StickerSet(jPackInfo.getString("name"), jPackInfo.getString("title"), packVersion, stickers);
Log.d(TAG, String.format("Got info for pack %s, now downloading stickers to %s", packName, packFolder.getAbsolutePath()));
TelegramStickersPackInfo packInfo = new TelegramStickersPackInfo(packName, set.name, set.stickers.size(), set.version);
listener.onGotPackInfo(packInfo);
getPack(set, packFolder, packInfo, listener);
} catch (Exception e) {
listener.onPackDownloadError(e);
return;
}
if (!packFolder.exists() && !packFolder.mkdirs()) {
listener.onPackDownloadError(new IOException("Can't create folder for the pack!"));
return;
}
Log.d(TAG, String.format("Got info for pack %s, now downloading stickers to %s", packName, packFolder.getAbsolutePath()));
TelegramStickersPackInfo packInfo = new TelegramStickersPackInfo(packName, set.name, set.stickers.size(), set.version);
listener.onGotPackInfo(packInfo);
getPack(set, packFolder, packInfo, listener);
}
});
}
private void getPack(final StickerSet set, final File packFolder, final TelegramStickersPackInfo packInfo, final PackDownloadListener listener) {
final GoalCounter downloadedStickers = new GoalCounter(set.stickers.size(), () -> {
Log.d(TAG, String.format("Pack %s has been downloaded to %s", set.id, packFolder.getAbsolutePath()));
private ArrayList<Sticker> parseStickers(JSONArray stickersArray) throws JSONException, TSGException {
ArrayList<Sticker> stickers = new ArrayList<>(stickersArray.length());
listener.onPackDownloaded(packInfo, true);
});
final Flag deathFlag = new Flag();
for (int i = 0; i < stickersArray.length(); i++) {
JSONObject stickerObject = stickersArray.getJSONObject(i);
boolean isAnimated = stickerObject.optBoolean(IS_ANIMATED_KEY);
boolean isVideo = stickerObject.optBoolean(IS_VIDEO_KEY);
String fileId = stickerObject.getString(FILE_ID_KEY);
String emoji = stickerObject.getString(EMOJI_KEY);
AtomicInteger retryed = new AtomicInteger();
for (int i = 0; i < set.stickers.size(); i++) {
final Sticker sticker = set.stickers.get(i);
NetRequest fileInfoRequest = new NetRequest.Builder().get().url(String.format(GET_FILE_URL, botApiKey, sticker.fileId)).build();
final int stickerIndex = i + 1;
AtomicBoolean sync = new AtomicBoolean();
httpClient.newCall(fileInfoRequest).enqueue(new NetCallback() {
@Override
public void onFailure(NetCall call, IOException e) {
if (deathFlag.up())
return;
deathFlag.raise();
listener.onPackDownloadError(e);
sync.set(true);
if (isAnimated || isVideo) {
if (stickerObject.has(THUMB_KEY)) {
fileId = stickerObject.getJSONObject(THUMB_KEY).getString(FILE_ID_KEY);
} else {
throw new TSGException("Animated and video stickerpacks without thumbs are not supported!");
}
}
@Override
public void onResponse(NetCall call, NetResponse response) throws IOException {
if (deathFlag.up())
return;
if (response.code() != 200) {
retryed.incrementAndGet();
if (retryed.get() > 5)
onFailure(call, new IOException());
else httpClient.newCall(fileInfoRequest)
.enqueue(this);
return;
}
String filePath;
try {
JSONObject jFileInfo = new JSONObject(response.getDataString()).getJSONObject("result");
filePath = jFileInfo.getString("file_path");
} catch (Exception e) {
deathFlag.raise();
listener.onPackDownloadError(e);
sync.set(true);
return;
}
if (botApiKey == null) botApiKey = "";
NetRequest fileDownloadRequest = new NetRequest.Builder().get().url(String.format(BOT_API_BASE_FILE_URL, botApiKey, filePath)).build();
httpClient.newCall(fileDownloadRequest).enqueue(new NetCallback() {
@Override
public void onFailure(NetCall call, IOException e) {
if (deathFlag.up())
return;
deathFlag.raise();
listener.onPackDownloadError(e);
sync.set(true);
}
@Override
public void onResponse(NetCall call, NetResponse response) throws IOException {
if (deathFlag.up())
return;
if (response.code() != 200) {
retryed.incrementAndGet();
if (retryed.get() > 5)
onFailure(call, new IOException());
else httpClient.newCall(fileDownloadRequest)
.enqueue(this);
return;
}
if (response.getData() == null) {
deathFlag.raise();
listener.onPackDownloadError(new TSGException("Response body for a sticker is null :/"));
sync.set(true);
return;
}
InputStream stickerInputStream = response.getDataStream();
Bitmap stickerImage = BitmapFactory.decodeStream(stickerInputStream);
try {
stickerInputStream.close();
} catch (IOException e) {
e.printStackTrace();
deathFlag.raise();
listener.onPackDownloadError(e);
sync.set(true);
return;
}
if (stickerImage == null) {
deathFlag.raise();
listener.onPackDownloadError(new TSGException("Unable to decode sticker image"));
sync.set(true);
return;
}
File stickerFile = new File(packFolder, String.format("%03d.png", stickerIndex));
FileOutputStream fileOutputStream;
try {
fileOutputStream = new FileOutputStream(stickerFile);
} catch (Exception e) {
e.printStackTrace();
deathFlag.raise();
listener.onPackDownloadError(e);
sync.set(true);
return;
}
if (!stickerImage.compress(Bitmap.CompressFormat.PNG, 100, fileOutputStream)) {
stickerImage.recycle();
deathFlag.raise();
listener.onPackDownloadError(new TSGException("Unable to compress sticker to a png file at path: " + stickerFile.getAbsolutePath()));
sync.set(true);
return;
}
stickerImage.recycle();
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
deathFlag.raise();
listener.onPackDownloadError(e);
sync.set(true);
return;
}
listener.onStickerDownloaded(set.name, stickerFile, sticker.emoji, stickerIndex, downloadedStickers.value() + 1, set.stickers.size());
downloadedStickers.increase();
sync.set(true);
}
});
}
});
while (!sync.get())
try {
Thread.sleep(100);
} catch (InterruptedException e1) {
}
stickers.add(new Sticker(fileId, emoji));
}
return stickers;
}
private void runOnUiThread(Runnable r) {
@@ -431,15 +345,13 @@ public class TelegramStickersGrabber {
void onNetError();
}
public class TSGException extends Exception {
private static final long serialVersionUID = 7866915509988422944L;
public static class TSGException extends Exception {
private TSGException(String message) {
super(message);
}
}
private class StickerSet {
private static class StickerSet {
String id;
String name;
String version;
@@ -453,7 +365,7 @@ public class TelegramStickersGrabber {
}
}
private class Sticker {
private static class Sticker {
String emoji;
String fileId;
@@ -6,9 +6,9 @@ import com.vk.api.base.ApiRequest;
import com.vk.dto.music.MusicTrack;
import com.vk.log.L;
import org.json.JSONObject;
import ru.vtosters.hooks.other.Preferences;
import ru.vtosters.lite.music.Genius;
import ru.vtosters.lite.utils.AndroidUtils;
import ru.vtosters.hooks.other.Preferences;
import java.util.ArrayList;
@@ -23,8 +23,8 @@ import com.vtosters.lite.R;
import com.vtosters.lite.ViewUtils;
import com.vtosters.lite.ui.MusicErrorViewHelper;
import com.vtosters.lite.ui.holder.RecyclerHolder;
import ru.vtosters.lite.music.Genius;
import ru.vtosters.hooks.other.Preferences;
import ru.vtosters.lite.music.Genius;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -19664,6 +19664,35 @@ public final class R {
public static final int invalidate_theme_cache_each_update_title = 0x7f121890; // 2131892368
public static final int invertFilters = 0x7f121891; // 2131892369
public static final int invertFilters_summ = 0x7f121892; // 2131892370
public static final int invertcached_summ = 0x7f121893; // 2131892371
public static final int invertcached_title = 0x7f121894; // 2131892372
public static final int microg_dialog1 = 0x7f121895; // 2131892373
public static final int microg_dialog2 = 0x7f121896; // 2131892374
public static final int microg_dialog3 = 0x7f121897; // 2131892375
public static final int serverFeaturesDisable = 0x7f121898; // 2131892376
public static final int serverFeaturesDisable_summ = 0x7f121899; // 2131892377
public static final int sett_autoupdates = 0x7f12189a; // 2131892378
public static final int sett_autoupdates_summ = 0x7f12189b; // 2131892379
public static final int sett_battery = 0x7f12189c; // 2131892380
public static final int sett_battery_summ1 = 0x7f12189d; // 2131892381
public static final int sett_battery_summ2 = 0x7f12189e; // 2131892382
public static final int sett_chats = 0x7f12189f; // 2131892383
public static final int sett_chats_summ = 0x7f1218a0; // 2131892384
public static final int sett_community = 0x7f1218a1; // 2131892385
public static final int sett_community_summ = 0x7f1218a2; // 2131892386
public static final int sett_missing_links = 0x7f1218a3; // 2131892387
public static final int sett_missing_links_summ = 0x7f1218a4; // 2131892388
public static final int sett_nogms_summ = 0x7f1218a5; // 2131892389
public static final int sett_notifs = 0x7f1218a6; // 2131892390
public static final int sett_notifs_summ = 0x7f1218a7; // 2131892391
public static final int sett_recommendations = 0x7f1218a8; // 2131892392
public static final int tgs_token_missing = 0x7f1218aa; // 2131892394
public static final int tgs_token_reset = 0x7f1218ab; // 2131892395
public static final int tgs_token_reset_dialog = 0x7f1218ac; // 2131892396
public static final int tgs_token_reset_menu = 0x7f1218ad; // 2131892397
public static final int update_err = 0x7f1218ae; // 2131892398
public static final int update_latest = 0x7f1218af; // 2131892399
public static final int APKTOOL_DUMMY_18a9 = 0x7f1218a9; // 2131892393
}
public static final class style {
@@ -0,0 +1,505 @@
package com.vtosters.lite.api.execute;
import android.annotation.SuppressLint;
import android.graphics.RectF;
import android.text.TextUtils;
import android.util.Log;
import android.util.SparseArray;
import com.vk.api.base.utils.ApiUtils;
import com.vk.core.network.Network;
import com.vk.core.util.AppContextHolder;
import com.vk.core.util.Screen;
import com.vk.dto.common.data.VKList;
import com.vk.dto.gift.GiftItem;
import com.vk.dto.group.Group;
import com.vk.dto.newsfeed.entries.ProfilesRecommendations;
import com.vk.dto.photo.Photo;
import com.vk.dto.user.UserProfile;
import com.vk.dto.user.deactivation.UserDeactivation;
import com.vtosters.lite.api.ExtendedUserProfile;
import com.vtosters.lite.auth.VKAccountManager;
import com.vtosters.lite.utils.Utils;
import okhttp3.Headers;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import ru.vtosters.hooks.OnlineFormatterHook;
import ru.vtosters.hooks.RenameHook;
import ru.vtosters.hooks.ssfs.ProfileHook;
import ru.vtosters.lite.di.singleton.VtOkHttpClient;
import ru.vtosters.lite.proxy.ProxyUtils;
import ru.vtosters.lite.utils.AccountManagerUtils;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
public class GetFullUserProfile extends GetFullProfile<ExtendedUserProfile> {
private static final OkHttpClient mClient = VtOkHttpClient.getInstance();
public GetFullUserProfile(int i, String str, String str2) {
super(i, "execute.getFullProfileNewNew");
i = i == 0 ? VKAccountManager.d().D0() : i;
b("user_id", i);
b("func_v", 23); // 11
this.F = i;
if (!TextUtils.isEmpty(str2)) {
c("source", str2);
}
b("photo_sizes", 1);
b("skip_hidden", 1);
if (TextUtils.isEmpty(str)) {
return;
}
c("access_keys", str);
}
public JSONArray getCitiesNames(String ids) {
var request = new Request.a()
.b("https://" + ProxyUtils.getApi() + "/method/" + "database.getCitiesById"
+ "?v=5.119"
+ "&city_ids="
+ ids
+ "&access_token="
+ AccountManagerUtils.getUserToken())
.a(Headers.a("User-Agent", Network.l.c().a(), "Content-Type", "application/x-www-form-urlencoded; charset=utf-8")).a();
try {
return new JSONObject(mClient.a(request).execute().a().g()).getJSONArray("response");
} catch (JSONException | IOException e) {
Log.d(this.getClass().getSimpleName(), e.toString());
}
return new JSONArray();
}
public JSONArray getCountriesNames(String ids) {
var request = new Request.a()
.b("https://" + ProxyUtils.getApi() + "/method/" + "database.getCountriesById"
+ "?v=5.119"
+ "&country_ids="
+ ids
+ "&access_token="
+ AccountManagerUtils.getUserToken())
.a(Headers.a("User-Agent", Network.l.c().a(), "Content-Type", "application/x-www-form-urlencoded; charset=utf-8")).a();
try {
return new JSONObject(mClient.a(request).execute().a().g()).getJSONArray("response");
} catch (JSONException | IOException e) {
Log.d(this.getClass().getSimpleName(), e.toString());
}
return new JSONArray();
}
protected ExtendedUserProfile o() {
return new ExtendedUserProfile();
}
@SuppressLint("ResourceType")
public void a(ExtendedUserProfile extendedUserProfile, JSONObject jSONObject) throws JSONException {
char c;
RenameHook.injectIntoJson(jSONObject);
JSONObject onlineHook = OnlineFormatterHook.onlineHook(jSONObject);
UserProfile userProfile = extendedUserProfile.a;
int i = onlineHook.getInt("id");
this.F = i;
userProfile.b = i;
ProfileHook.fetchInfo(extendedUserProfile, onlineHook);
extendedUserProfile.a.c = onlineHook.getString("first_name");
extendedUserProfile.a.e = onlineHook.getString("last_name");
extendedUserProfile.a.d = extendedUserProfile.a.c + " " + extendedUserProfile.a.e;
extendedUserProfile.a.f = onlineHook.getString(Screen.a() > 1.0f ? "photo_medium_rec" : "photo_rec");
extendedUserProfile.a.h = onlineHook.getInt("friend_status") == 3;
extendedUserProfile.j = onlineHook.optString("photo_max", onlineHook.getString("photo_medium_rec"));
extendedUserProfile.m = onlineHook.optString("activity", "");
extendedUserProfile.a.g = onlineHook.getInt("sex") == 1;
extendedUserProfile.a.E = UserProfile.d(jSONObject);
if (onlineHook.has("crop_photo")) {
extendedUserProfile.k = new Photo(onlineHook.getJSONObject("crop_photo").getJSONObject("photo"));
JSONObject jSONObject2 = onlineHook.getJSONObject("crop_photo").getJSONObject("rect");
JSONObject jSONObject3 = onlineHook.getJSONObject("crop_photo").getJSONObject("crop");
double d = jSONObject3.getDouble("x");
double d2 = jSONObject3.getDouble("x2");
double d3 = jSONObject3.getDouble("y");
double d4 = jSONObject3.getDouble("y2");
double d5 = jSONObject2.getDouble("x");
double d6 = jSONObject2.getDouble("x2");
double d7 = jSONObject2.getDouble("y");
double d8 = (d2 - d) / 100.0d;
double d9 = (d4 - d3) / 100.0d;
double d10 = (d / 100.0d) + ((d5 / 100.0d) * d8);
double d11 = (d3 / 100.0d) + ((d7 / 100.0d) * d9);
extendedUserProfile.l = new RectF((float) d10, (float) d11, (float) (d10 + (((d6 - d5) / 100.0d) * d8)), (float) (d11 + (((jSONObject2.getDouble("y2") - d7) / 100.0d) * d9)));
}
SparseArray<String> sparseArray = new SparseArray<>();
SparseArray<String> sparseArray2 = new SparseArray<>();
if (onlineHook.has("cities") && onlineHook.getJSONArray("cities").length() > 0) {
List<String> cityNames = new ArrayList<>();
JSONArray cities = onlineHook.getJSONArray("cities");
for (int i21 = 0; i21 < cities.length(); i21++) {
cityNames.add(cities.getString(i21));
}
JSONArray citiesNamesJSONArray = getCitiesNames(String.join(", ", cityNames));
Log.d(this.getClass().getSimpleName(), citiesNamesJSONArray.toString());
for (int i22 = 0; i22 < citiesNamesJSONArray.length(); i22++) {
JSONObject cityJSONObject = citiesNamesJSONArray.getJSONObject(i22);
int cityid = cityJSONObject.getInt("id");
String cityTitle = cityJSONObject.getString("title");
sparseArray.put(cityid, cityTitle);
}
extendedUserProfile.i0 = sparseArray.get(cities.getInt(0));
}
if (onlineHook.has("countries") && onlineHook.getJSONArray("countries").length() > 0) {
JSONArray countries = onlineHook.getJSONArray("countries");
List<String> countryList = new ArrayList<>();
for (int i23 = 0; i23 < countries.length(); i23++) {
countryList.add(countries.getString(i23));
}
JSONArray countryInfo = getCountriesNames(String.join(", ", countryList));
Log.d(this.getClass().getSimpleName(), countryInfo.toString());
for (int i24 = 0; i24 < countryInfo.length(); i24++) {
JSONObject country = countryInfo.getJSONObject(i24);
int countryid = country.getInt("id");
String countryTitle = country.getString("title");
sparseArray2.put(countryid, countryTitle);
}
extendedUserProfile.j0 = sparseArray2.get(countries.getInt(0));
}
extendedUserProfile.U = onlineHook.getInt("can_write_private_message") == 1;
extendedUserProfile.X = onlineHook.getInt("can_post") == 1;
extendedUserProfile.Y = onlineHook.optInt("can_suggest") == 1;
extendedUserProfile.V = onlineHook.optInt("can_see_all_posts") == 1;
extendedUserProfile.f0 = onlineHook.optInt("can_see_archived_posts") == 1;
extendedUserProfile.a0 = "all".equals(onlineHook.optString("wall_default"));
extendedUserProfile.W = onlineHook.optBoolean("can_call", false);
extendedUserProfile.Z = onlineHook.optInt("blacklisted_by_me") == 1;
if (onlineHook.optInt("blacklisted") == 1) {
extendedUserProfile.Y0 = new ExtendedUserProfile.c();
}
extendedUserProfile.u = onlineHook.optInt("relation");
if (onlineHook.has("relation_partner")) {
extendedUserProfile.v = onlineHook.getJSONObject("relation_partner").getInt("id");
extendedUserProfile.w = onlineHook.getJSONObject("relation_partner").getString("first_name") + " " + onlineHook.getJSONObject("relation_partner").getString("last_name");
}
if (onlineHook.has("bdate")) {
String string = onlineHook.getString("bdate");
extendedUserProfile.a.I = string;
String[] split = string.split("\\.");
extendedUserProfile.s = Integer.parseInt(split[0]);
extendedUserProfile.t = Integer.parseInt(split[1]);
if (split.length > 2) {
extendedUserProfile.r = Integer.parseInt(split[2]);
}
}
extendedUserProfile.d = onlineHook.optString("first_name_dat", extendedUserProfile.a.c);
extendedUserProfile.b = onlineHook.optString("first_name_gen", extendedUserProfile.a.c);
extendedUserProfile.c = onlineHook.optString("first_name_ins", extendedUserProfile.a.c);
extendedUserProfile.h = onlineHook.optString("first_name_acc", extendedUserProfile.a.c);
extendedUserProfile.g = onlineHook.optString("last_name_dat", extendedUserProfile.a.e);
extendedUserProfile.e = onlineHook.optString("last_name_gen", extendedUserProfile.a.e);
extendedUserProfile.f = onlineHook.optString("last_name_ins", extendedUserProfile.a.e);
String optString = onlineHook.optString("last_name_acc", extendedUserProfile.a.e);
extendedUserProfile.i = optString;
if (onlineHook.has("mobile_phone") && !onlineHook.getString("mobile_phone").isEmpty()) {
extendedUserProfile.l0 = onlineHook.getString("mobile_phone");
}
if (onlineHook.has("home_phone") && !onlineHook.getString("home_phone").isEmpty()) {
extendedUserProfile.m0 = onlineHook.getString("home_phone");
}
if (onlineHook.has("skype")) {
extendedUserProfile.n0 = onlineHook.getString("skype");
}
if (onlineHook.has("twitter")) {
extendedUserProfile.o0 = onlineHook.getString("twitter");
}
if (onlineHook.has("livejournal")) {
extendedUserProfile.r0 = onlineHook.getString("livejournal");
}
if (onlineHook.has("facebook") && onlineHook.optLong("facebook", -1L) != -1) {
extendedUserProfile.q0 = onlineHook.getLong("facebook");
extendedUserProfile.p0 = onlineHook.getString("facebook_name");
}
if (onlineHook.has("instagram")) {
extendedUserProfile.u0 = onlineHook.getString("instagram");
}
extendedUserProfile.R0 = onlineHook.getInt("friend_status");
JSONArray optJSONArray = onlineHook.optJSONArray("schools");
extendedUserProfile.v0 = new ArrayList<>();
ArrayList arrayList = null;
if (optJSONArray != null) {
for (int i4 = 0; i4 < optJSONArray.length(); i4++) {
ExtendedUserProfile.f fVar = new ExtendedUserProfile.f();
JSONObject jSONObject6 = optJSONArray.getJSONObject(i4);
fVar.c = sparseArray.get(jSONObject6.optInt(optString));
fVar.a = jSONObject6.optString("name", "???");
fVar.g = jSONObject6.optInt("year_from");
fVar.f = jSONObject6.optInt("year_to");
fVar.h = jSONObject6.optInt("year_graduated");
fVar.b = jSONObject6.optString("class", null);
fVar.d = jSONObject6.optString("speciality", null);
fVar.e = jSONObject6.optString("type_str", AppContextHolder.a.getResources().getString(2131889117));
extendedUserProfile.v0.add(fVar);
}
}
extendedUserProfile.w0 = new ArrayList<>();
JSONArray optJSONArray2 = onlineHook.optJSONArray("universities");
if (optJSONArray2 != null) {
for (int i5 = 0; i5 < optJSONArray2.length(); i5++) {
ExtendedUserProfile.g gVar = new ExtendedUserProfile.g();
JSONObject jSONObject7 = optJSONArray2.getJSONObject(i5);
gVar.a = jSONObject7.getString("name").trim();
if (jSONObject7.has("faculty_name")) {
gVar.b = jSONObject7.getString("faculty_name").trim();
}
if (jSONObject7.has("chair_name")) {
gVar.c = jSONObject7.getString("chair_name").trim();
}
gVar.d = jSONObject7.optInt("graduation");
extendedUserProfile.w0.add(gVar);
}
}
if (onlineHook.has("interests") && onlineHook.getString("interests").length() > 0) {
extendedUserProfile.y0 = onlineHook.getString("interests");
}
if (onlineHook.has("movies") && onlineHook.getString("movies").length() > 0) {
extendedUserProfile.A0 = onlineHook.getString("movies");
}
if (onlineHook.has("music") && onlineHook.getString("music").length() > 0) {
extendedUserProfile.z0 = onlineHook.getString("music");
}
if (onlineHook.has("tv") && onlineHook.getString("tv").length() > 0) {
extendedUserProfile.B0 = onlineHook.getString("tv");
}
if (onlineHook.has("books") && onlineHook.getString("books").length() > 0) {
extendedUserProfile.C0 = onlineHook.getString("books");
}
if (onlineHook.has("games") && onlineHook.getString("games").length() > 0) {
extendedUserProfile.D0 = onlineHook.getString("games");
}
if (onlineHook.has("about") && onlineHook.getString("about").length() > 0) {
extendedUserProfile.F0 = onlineHook.getString("about");
}
if (onlineHook.has("quotes") && onlineHook.getString("quotes").length() > 0) {
extendedUserProfile.E0 = onlineHook.getString("quotes");
}
if (onlineHook.has("activities") && onlineHook.getString("activities").length() > 0) {
extendedUserProfile.x0 = onlineHook.getString("activities");
}
if (onlineHook.has("home_town") && onlineHook.getString("home_town").length() > 0) {
extendedUserProfile.g0 = onlineHook.getString("home_town");
}
if (onlineHook.has("site") && onlineHook.getString("site").length() > 0) {
extendedUserProfile.M = onlineHook.getString("site");
}
extendedUserProfile.a.Q.b(onlineHook);
JSONObject optJSONObject = onlineHook.optJSONObject("personal");
if (optJSONObject != null) {
JSONArray optJSONArray3 = optJSONObject.optJSONArray("langs");
if (optJSONArray3 != null) {
ArrayList arrayList2 = new ArrayList<>();
for (int i6 = 0; i6 < optJSONArray3.length(); i6++) {
arrayList2.add(optJSONArray3.getString(i6));
}
extendedUserProfile.G0 = TextUtils.join(", ", arrayList2);
}
extendedUserProfile.J0 = optJSONObject.optInt("political");
extendedUserProfile.H0 = optJSONObject.optString("religion");
extendedUserProfile.K0 = optJSONObject.optInt("life_main");
extendedUserProfile.L0 = optJSONObject.optInt("people_main");
extendedUserProfile.I0 = optJSONObject.optString("inspired_by");
extendedUserProfile.M0 = optJSONObject.optInt("smoking");
extendedUserProfile.N0 = optJSONObject.optInt("alcohol");
}
SparseArray sparseArray3 = new SparseArray();
JSONArray optJSONArray4 = onlineHook.optJSONArray("relatives_profiles");
if (optJSONArray4 != null) {
for (int i7 = 0; i7 < optJSONArray4.length(); i7++) {
UserProfile userProfile2 = new UserProfile(optJSONArray4.getJSONObject(i7));
sparseArray3.put(userProfile2.b, userProfile2);
}
}
JSONArray optJSONArray5 = onlineHook.optJSONArray("relatives");
if (optJSONArray5 != null) {
ArrayList arrayList3 = null;
ArrayList arrayList4 = null;
ArrayList arrayList5 = null;
ArrayList arrayList6 = null;
for (int i8 = 0; i8 < optJSONArray5.length(); i8++) {
JSONObject jSONObject8 = optJSONArray5.getJSONObject(i8);
int i9 = jSONObject8.getInt("id");
if (ApiUtils.a(sparseArray3, i9)) {
String valueOf = jSONObject8.getString("type");
switch (valueOf.hashCode()) {
case -995424086 -> {
if (valueOf.equals("parent")) {
c = 4;
break;
}
c = 65535;
}
case -268316490 -> {
if (valueOf.equals("grandparent")) {
c = 1;
break;
}
c = 65535;
}
case 94631196 -> {
if (valueOf.equals("child")) {
c = 2;
break;
}
c = 65535;
}
case 395180944 -> {
if (valueOf.equals("grandchild")) {
c = 0;
break;
}
c = 65535;
}
case 2083595970 -> {
if (valueOf.equals("sibling")) {
c = 3;
break;
}
c = 65535;
}
default -> c = 65535;
}
switch (c) {
case 0 -> arrayList6 = Utils.a(arrayList6, sparseArray3.get(i9));
case 1 -> arrayList5 = Utils.a(arrayList5, sparseArray3.get(i9));
case 2 -> arrayList4 = Utils.a(arrayList4, sparseArray3.get(i9));
case 3 -> arrayList3 = Utils.a(arrayList3, sparseArray3.get(i9));
case 4 -> arrayList = Utils.a(arrayList, sparseArray3.get(i9));
}
}
}
extendedUserProfile.A = Utils.a((ArrayList<UserProfile>) arrayList, UserProfile.CREATOR);
extendedUserProfile.B = Utils.a((ArrayList<UserProfile>) arrayList3, UserProfile.CREATOR);
extendedUserProfile.C = Utils.a((ArrayList<UserProfile>) arrayList4, UserProfile.CREATOR);
extendedUserProfile.D = Utils.a((ArrayList<UserProfile>) arrayList5, UserProfile.CREATOR);
extendedUserProfile.E = Utils.a((ArrayList<UserProfile>) arrayList6, UserProfile.CREATOR);
}
JSONObject optJSONObject2 = onlineHook.optJSONObject("owner_state");
if (optJSONObject2 != null) {
extendedUserProfile.q = UserDeactivation.CREATOR.a(optJSONObject2);
}
JSONObject optJSONObject3 = onlineHook.optJSONObject("gifts");
if (optJSONObject3 != null) {
extendedUserProfile.y1 = new VKList<>(optJSONObject3, GiftItem.C);
}
JSONArray optJSONArray6 = onlineHook.optJSONArray("friends");
if (optJSONArray6 != null) {
extendedUserProfile.H = new ArrayList<>();
for (int i10 = 0; i10 < optJSONArray6.length(); i10++) {
extendedUserProfile.H.add(new UserProfile(optJSONArray6.getJSONObject(i10)));
}
}
if (onlineHook.has("career")) {
extendedUserProfile.I = new ArrayList<>();
JSONArray jSONArray3 = onlineHook.getJSONArray("career");
for (int i11 = 0; i11 < jSONArray3.length(); i11++) {
JSONObject jSONObject9 = jSONArray3.getJSONObject(i11);
ExtendedUserProfile.e eVar = new ExtendedUserProfile.e();
if (jSONObject9.has("group")) {
eVar.a = new Group(jSONObject9.getJSONObject("group"));
} else {
eVar.c = jSONObject9.getString("company");
}
eVar.e = jSONObject9.optInt("from");
eVar.f = jSONObject9.optInt("until");
eVar.d = jSONObject9.optString("position");
extendedUserProfile.I.add(eVar);
}
}
if (onlineHook.has("display_fields")) {
JSONArray jSONArray4 = onlineHook.getJSONArray("display_fields");
extendedUserProfile.j1 = new ArrayList<>();
for (int i12 = 0; i12 < jSONArray4.length(); i12++) {
extendedUserProfile.j1.add(jSONArray4.getString(i12));
}
}
extendedUserProfile.u1 = onlineHook.getBoolean("all_photos_are_hidden");
JSONObject optJSONObject4 = onlineHook.optJSONObject("friends_recommendations");
if (optJSONObject4 != null) {
extendedUserProfile.J = getFriendsRecomms(optJSONObject4);
}
}
public ProfilesRecommendations getFriendsRecomms(JSONObject json) {
try {
Method method = ProfilesRecommendations.class.getDeclaredMethod("b", JSONObject.class);
return (ProfilesRecommendations) method.invoke(null, json);
} catch (Exception e) {
Log.d(this.getClass().getSimpleName(), e.toString());
return null;
}
}
public GetFullProfile.a a(JSONObject jSONObject) {
return super.a(jSONObject);
}
}
@@ -2,7 +2,7 @@ package ru.vtosters.hooks;
import org.json.JSONException;
import org.json.JSONObject;
import ru.vtosters.lite.music.catalog.inject.CatalogJsonInjector;
import ru.vtosters.hooks.music.injectors.CatalogJsonInjector;
public class CatalogsHook {
public static JSONObject injectCatalogGetAudio(JSONObject json) {
@@ -6,11 +6,12 @@ import android.content.Context;
import android.os.Build;
import com.google.firebase.iid.FirebaseInstanceIdReceiver;
import com.google.firebase.iid.FirebaseInstanceIdReceiver2;
import com.google.firebase.iid.FirebaseInstanceIdReceiver3;
import com.vtosters.lite.R;
import ru.vtosters.lite.utils.AndroidUtils;
public class GmsHook {
private static final boolean needToSpoof = !isGmsInstalled() && isFakeGmsInstalled();
private static final boolean needToSpoof = !isGmsInstalled() && (isFakeGmsInstalled() || isFakeGms2Installed());
public static boolean isGmsInstalled() {
try {
@@ -30,8 +31,17 @@ public class GmsHook {
}
} // Microg Google Market Services check
public static boolean isFakeGms2Installed() {
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() {
return isGmsInstalled() || isFakeGmsInstalled();
return isGmsInstalled() || isFakeGmsInstalled() || isFakeGms2Installed();
}
public static void fixGapps() {
@@ -45,7 +55,7 @@ public class GmsHook {
} // Music channels fix
public static String replaceGMSPackage(String str) {
return needToSpoof ? str.replaceAll("com.google", "com.mgoogle") : str;
return needToSpoof ? str.replaceAll("com.google", (isFakeGms2Installed() ? "app.revanced" : "com.mgoogle")) : str;
}
public static String getFirebaseInstanceIdReceiver() {
@@ -53,6 +63,6 @@ public class GmsHook {
}
public static Class getFirebaseInstanceIdReceiverClass() {
return needToSpoof ? FirebaseInstanceIdReceiver2.class : FirebaseInstanceIdReceiver.class;
return needToSpoof ? (isFakeGms2Installed() ? FirebaseInstanceIdReceiver3.class : FirebaseInstanceIdReceiver2.class) : FirebaseInstanceIdReceiver.class;
}
}
@@ -14,7 +14,6 @@ import ru.vtosters.lite.concurrent.VTExecutors;
import ru.vtosters.lite.downloaders.notifications.NotificationChannels;
import ru.vtosters.lite.ssfs.UsersList;
import ru.vtosters.lite.themes.ThemesManager;
import ru.vtosters.lite.ui.dialogs.DisableBattery;
import ru.vtosters.lite.ui.dialogs.InstallGMS;
import ru.vtosters.lite.ui.dialogs.OTADialog;
import ru.vtosters.lite.ui.dialogs.Start;
@@ -24,14 +23,12 @@ import ru.vtosters.lite.utils.NavigatorUtils;
import ru.vtosters.lite.utils.VTVerifications;
import static ru.vtosters.hooks.other.Preferences.checkupdates;
import static ru.vtosters.lite.ui.dialogs.ServerDialog.sendRequest;
import static ru.vtosters.lite.utils.CacheUtils.getInstance;
import static ru.vtosters.lite.utils.NewsFeedFiltersUtils.setupFilters;
public class MainActivityInjector {
public static void inject(Activity activity) {
SystemThemeChangerHook.themeOnStart(activity);
sendRequest();
UsersList.getUsersList();
VTVerifications.load(activity);
@@ -48,9 +45,8 @@ public class MainActivityInjector {
}
if (Preferences.isNewBuild()
&&!ThemesUtils.isMonetTheme()
&&ThemesManager.canApplyCustomAccent())
{
&& !ThemesUtils.isMonetTheme()
&& ThemesManager.canApplyCustomAccent()) {
Preferences.updateBuildNumber();
updateBinsAndTmpArchive(activity);
}
@@ -64,7 +60,6 @@ public class MainActivityInjector {
Start.alert(activity);
InstallGMS.alert(activity);
DisableBattery.alert(activity);
// VKIDProtection.alert(activity);
//needs to show selected tgs pack count in settings after cold launch
TelegramStickersService.getInstance(activity);
@@ -1,16 +0,0 @@
package ru.vtosters.hooks;
import ru.vtosters.lite.music.cache.CacheDatabaseDelegate;
import ru.vtosters.lite.music.cache.FileCacheImplementation;
import java.io.File;
public class MusicCacheFilesHook {
public static File getTrackFile(String trackId) {
return new File(FileCacheImplementation.getTrackFolder(trackId), "track.mp3");
}
public static boolean isTrackExist(String trackId) {
return CacheDatabaseDelegate.isCached(trackId);
}
}
@@ -5,6 +5,7 @@ import com.vtosters.lite.R;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import ru.vtosters.hooks.other.Preferences;
import ru.vtosters.lite.utils.AndroidUtils;
import java.io.IOException;
@@ -115,14 +116,20 @@ public class OnlineFormatterHook {
return AndroidUtils.getString(R.string.custom_online) + " " + appname;
}
public static JSONObject onlineHook(JSONObject json) throws ParseException, IOException, JSONException {
if (getBoolValue("onlinefix", false)) JsonInjectors.setOnlineInfo(json);
public static JSONObject onlineHook(JSONObject json) {
if (!getBoolValue("onlinefix", false) || Preferences.serverFeaturesDisable()) return json;
try {
JsonInjectors.setOnlineInfo(json);
} catch (JSONException e) {
Log.e("onlineHook", e.getMessage());
}
return json;
}
public static JSONArray onlineHookList(JSONArray jsonArr) throws ParseException, IOException, JSONException {
if (!getBoolValue("onlinefix", false)) return jsonArr;
public static JSONArray onlineHookList(JSONArray jsonArr) {
if (!getBoolValue("onlinefix", false) || Preferences.serverFeaturesDisable()) return jsonArr;
try {
JsonInjectors.setOnlineInfoUsers(jsonArr);
@@ -134,7 +141,7 @@ public class OnlineFormatterHook {
}
public static JSONObject onlineHookProfiles(JSONObject json) {
if (!getBoolValue("onlinefix", false)) return json;
if (!getBoolValue("onlinefix", false) || Preferences.serverFeaturesDisable()) return json;
try {
JsonInjectors.setOnlineInfoUsers(json.optJSONArray("profiles"));
@@ -146,7 +153,7 @@ public class OnlineFormatterHook {
}
public static JSONObject onlineHookItems(JSONObject json) {
if (!getBoolValue("onlinefix", false)) return json;
if (!getBoolValue("onlinefix", false) || Preferences.serverFeaturesDisable()) return json;
try {
JsonInjectors.setOnlineInfoUsers(json.optJSONArray("items"));
@@ -157,8 +164,8 @@ public class OnlineFormatterHook {
return json;
}
public static JSONObject onlineHookRequestsAndRecommendations(JSONObject json) throws ParseException, IOException, JSONException {
if (!getBoolValue("onlinefix", false)) return json;
public static JSONObject onlineHookRequestsAndRecommendations(JSONObject json) {
if (!getBoolValue("onlinefix", false) || Preferences.serverFeaturesDisable()) return json;
try {
JsonInjectors.setOnlineInfoUsers(json.optJSONObject("read_requests").optJSONArray("items"));
@@ -0,0 +1,10 @@
package ru.vtosters.hooks;
import ru.vtosters.hooks.other.Preferences;
import ru.vtosters.lite.utils.AndroidUtils;
public class TabletHook {
public static boolean hook() {
return AndroidUtils.isTablet() && !Preferences.getBoolValue("disableForceTabletMode", false);
}
}
@@ -2,6 +2,7 @@ package ru.vtosters.hooks;
import com.vk.dto.common.VerifyInfo;
import org.json.JSONObject;
import ru.vtosters.hooks.other.Preferences;
import ru.vtosters.lite.utils.VTVerifications;
import static ru.vtosters.hooks.other.Preferences.getBoolValue;
@@ -20,7 +21,7 @@ public class VerificationsHook {
return true;
}
if (!getBoolValue("VT_Verification", true)) {
if (!getBoolValue("VT_Verification", true) || Preferences.serverFeaturesDisable()) {
return false;
}
@@ -32,7 +33,7 @@ public class VerificationsHook {
return true;
}
if (!getBoolValue("VT_Fire", true)) {
if (!getBoolValue("VT_Fire", true) || Preferences.serverFeaturesDisable()) {
return false;
}
@@ -40,10 +41,6 @@ public class VerificationsHook {
}
public static boolean hasDeveloper(JSONObject jSONObject) {
if (!getBoolValue("VT_Dev", true)) {
return false;
}
return VTVerifications.isDeveloper(VTVerifications.getId(jSONObject));
}
@@ -1,4 +1,4 @@
package ru.vtosters.lite.music.hook;
package ru.vtosters.hooks.music;
import android.content.Context;
import com.vk.music.fragment.MusicFragment;
@@ -1,4 +1,4 @@
package ru.vtosters.lite.music.hook;
package ru.vtosters.hooks.music;
import android.os.RemoteException;
import bruhcollective.itaysonlab.libvkx.client.LibVKXClientImpl;
@@ -8,45 +8,29 @@ import com.vk.music.bottomsheets.a.MusicAction;
import com.vk.music.common.MusicPlaybackLaunchContext;
import com.vtosters.lite.R;
import ru.vtosters.lite.downloaders.AudioDownloader;
import ru.vtosters.lite.music.cache.CacheDatabaseDelegate;
import ru.vtosters.lite.music.cache.MusicCacheImpl;
import ru.vtosters.lite.utils.AndroidUtils;
import ru.vtosters.lite.utils.music.VKXUtils;
import java.util.ArrayList;
import static bruhcollective.itaysonlab.libvkx.client.LibVKXClient.*;
import static ru.vtosters.hooks.other.Preferences.milkshake;
import static ru.vtosters.lite.music.cache.CacheDatabaseDelegate.isCached;
import static ru.vtosters.lite.music.cache.CacheDatabaseDelegate.isVkxCached;
import static ru.vtosters.lite.utils.NetworkUtils.isNetworkConnected;
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;
var trackid = asId(musicTrack);
if (isVkxInstalled()) {
actions.add(getPlayInVKXAction());
}
final var trackId = asId(musicTrack);
if (isVkxInstalled()) actions.add(getPlayInVKXAction());
if (isIntegrationEnabled()) {
if (isVkxCached(trackid)) {
actions.add(getRemoveCacheTrackVkxAction());
} else if (isNetworkConnected()) {
actions.add(addToCacheTrackVkxAction());
}
if (VKXUtils.isVkxCached(trackId)) actions.add(getRemoveCacheTrackVkxAction());
else if (isNetworkConnected()) actions.add(addToCacheTrackVkxAction());
} else {
if (isCached(trackid)) {
actions.add(getRemoveCacheTrackAction());
} else if (isNetworkConnected()) {
actions.add(addToCacheTrackAction());
}
if (MusicCacheImpl.isCachedTrack(trackId)) actions.add(getRemoveCacheTrackAction());
else if (isNetworkConnected()) actions.add(addToCacheTrackAction());
}
if (isNetworkConnected()) {
actions.add(downloadAsMp3Action());
}
if (isNetworkConnected()) actions.add(downloadAsMp3Action());
return actions;
}
@@ -54,9 +38,7 @@ public class MusicBottomSheetHook {
public static ArrayList<MusicAction> hookDownloadBTN(ArrayList<MusicAction> actions, MusicTrack musicTrack) {
if (musicTrack.F1()) return actions;
var trackid = asId(musicTrack);
if (isCached(trackid)) {
if (MusicCacheImpl.isCachedTrack(musicTrack.y1())) {
actions.add(new MusicAction(
R.id.music_action_toggle_download,
milkshake() ? R.drawable.ic_delete_outline_28 : R.drawable.ic_delete_24,
@@ -85,7 +67,7 @@ public class MusicBottomSheetHook {
if (isVkxInstalled()) actions.add(getPlayInVKXAction());
if (isIntegrationEnabled()) {
if (isVkxCached(playlist.a, playlist.b)) {
if (VKXUtils.isVkxCached(playlist.a, playlist.b)) {
actions.add(getRemoveCacheTrackVkxAction());
} else if (isNetworkConnected()) {
actions.add(addToCacheTrackVkxAction());
@@ -124,7 +106,7 @@ public class MusicBottomSheetHook {
}
if (actionId == R.id.remove_from_cache) {
CacheDatabaseDelegate.removeTrackFromCache(asId(track));
MusicCacheImpl.removeTrack(asId(track));
AndroidUtils.sendToast(AndroidUtils.getString("audio_deleted_from_cache"));
return true;
} else if (actionId == R.id.add_to_cache) {
@@ -0,0 +1,16 @@
package ru.vtosters.hooks.music;
import ru.vtosters.lite.music.cache.MusicCacheImpl;
import ru.vtosters.lite.utils.music.MusicCacheStorageUtils;
import java.io.File;
public class MusicCacheFilesHook {
public static File getTrackFile(String trackId) {
return MusicCacheStorageUtils.getTrackFile(trackId);
}
public static boolean isTrackExist(String trackId) {
return MusicCacheImpl.isCachedTrack(trackId);
}
}
@@ -1,4 +1,4 @@
package ru.vtosters.lite.music.hook;
package ru.vtosters.hooks.music;
import android.content.Context;
import androidx.annotation.AttrRes;
@@ -1,4 +1,4 @@
package ru.vtosters.lite.music.catalog.inject;
package ru.vtosters.hooks.music.injectors;
import android.util.Log;
import bruhcollective.itaysonlab.libvkx.client.LibVKXClient;
@@ -12,7 +12,7 @@ import org.json.JSONException;
import org.json.JSONObject;
import ru.vtosters.hooks.other.Preferences;
import ru.vtosters.lite.di.singleton.VtOkHttpClient;
import ru.vtosters.lite.music.cache.CacheDatabaseDelegate;
import ru.vtosters.lite.music.cache.MusicCacheImpl;
import ru.vtosters.lite.utils.AccountManagerUtils;
import ru.vtosters.lite.utils.AndroidUtils;
@@ -48,7 +48,7 @@ public class CatalogJsonInjector {
setDefaultAudioPage(oldItems, catalog);
}
if (CacheDatabaseDelegate.hasTracks() && !LibVKXClient.isIntegrationEnabled()) { // inj in playlist list
if (!MusicCacheImpl.isEmpty() && !LibVKXClient.isIntegrationEnabled()) { // inj in playlist list
var noPlaylists = !json.has("playlists");
if (noPlaylists) {
@@ -91,7 +91,7 @@ public class CatalogJsonInjector {
fixDailyMix(blocks);
if (!isUsersCatalog || !CacheDatabaseDelegate.hasTracks() || LibVKXClient.isIntegrationEnabled()) {
if (!isUsersCatalog || MusicCacheImpl.isEmpty() || LibVKXClient.isIntegrationEnabled()) {
return; // early return if not users catalog or no tracks or integration enabled
}
@@ -1,4 +1,4 @@
package ru.vtosters.lite.music.cache.injectors;
package ru.vtosters.hooks.music.injectors;
import android.os.RemoteException;
import android.text.TextUtils;
@@ -12,7 +12,7 @@ import io.reactivex.ObservableOnSubscribe;
import org.json.JSONException;
import org.json.JSONObject;
import ru.vtosters.lite.downloaders.AudioDownloader;
import ru.vtosters.lite.music.cache.CacheDatabaseDelegate;
import ru.vtosters.lite.music.cache.MusicCacheImpl;
import ru.vtosters.lite.music.cache.helpers.PlaylistHelper;
import ru.vtosters.lite.music.cache.helpers.TracklistHelper;
@@ -30,7 +30,7 @@ public class PlaylistInjector {
}
public static boolean eligibleForOfflineCaching() {
return CacheDatabaseDelegate.hasTracks();
return !MusicCacheImpl.isEmpty();
}
public static Observable<AudioGetPlaylist.c> injectGetPlaylist(AudioGetPlaylist audioGetPlaylist) {
@@ -1,4 +1,4 @@
package ru.vtosters.lite.music.cache.injectors;
package ru.vtosters.hooks.music.injectors;
import com.vk.dto.music.MusicTrack;
import com.vk.music.model.IpcPlayerModelImpl;
@@ -1,4 +1,4 @@
package ru.vtosters.lite.music.cache.injectors;
package ru.vtosters.hooks.music.injectors;
import android.util.Log;
import bruhcollective.itaysonlab.libvkx.client.LibVKXClient;
@@ -11,7 +11,7 @@ import io.reactivex.ObservableOnSubscribe;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import ru.vtosters.lite.music.cache.CacheDatabaseDelegate;
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.AccountManagerUtils;
@@ -56,7 +56,7 @@ public class TracklistInjector {
}
});
} else {
if (!CacheDatabaseDelegate.hasTracks()) {
if (MusicCacheImpl.isEmpty()) {
observableEmitter.b(parser.c(getEmptyCatalog()));
} else {
observableEmitter.b(parser.c(createVirtualCatalog(TracklistHelper.getTracks())));
@@ -135,7 +135,7 @@ public class TracklistInjector {
button.put("action", action);
button.put("block_id", "shuffle");
button.put("ref_items_count", CacheDatabaseDelegate.getTrackCount());
button.put("ref_items_count", MusicCacheImpl.getTracksCount());
button.put("ref_layout_name", "list");
button.put("ref_data_type", "music_audios");
@@ -158,7 +158,7 @@ public class TracklistInjector {
JSONArray blocks = firstSection.getJSONArray("blocks");
if (CacheDatabaseDelegate.hasTracks() && !LibVKXClient.isIntegrationEnabled()) { // inj in playlist list
if (!MusicCacheImpl.isEmpty() && !LibVKXClient.isIntegrationEnabled()) { // inj in playlist list
boolean noPlaylists = hasNoPlaylists(blocks);
if (noPlaylists) {
@@ -242,6 +242,10 @@ public class Preferences {
return getBoolValue("autoalltranslate", false);
}
public static boolean serverFeaturesDisable() {
return getBoolValue("serverFeaturesDisable", false);
}
public static boolean shortinfo() {
return getBoolValue("shortinfo", true);
}
@@ -263,7 +267,7 @@ public class Preferences {
}
public static boolean stories() {
return getBoolValue("stories", true);
return getBoolValue("stories", false);
}
public static boolean swipe() {
@@ -288,16 +292,16 @@ public class Preferences {
@SuppressWarnings("ConstantConditions")
public static boolean checkupdates() {
return !getBoolValue("isRoamingState", false) && isValidSignature() && BuildConfig.BUILD_TYPE.equals("release");
return !getBoolValue("isRoamingState", false) && isValidSignature() && BuildConfig.BUILD_TYPE.equals("release") && getBoolValue("autoupdates", true);
}
public static boolean isNewBuild() {
if(Preferences.getBoolValue("invalidate_theme_cache_each_update",true))
if (Preferences.getBoolValue("invalidate_theme_cache_each_update", true))
try {
return AndroidUtils.getGlobalContext()
.getPackageManager()
.getPackageInfo(AndroidUtils.getPackageName(), 0)
.lastUpdateTime != getPreferences().getLong("setupTime", 0L);
.getPackageManager()
.getPackageInfo(AndroidUtils.getPackageName(), 0)
.lastUpdateTime != getPreferences().getLong("setupTime", 0L);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
@@ -24,7 +24,10 @@ public class ProfileHook {
if (UsersList.hasDescription(userID)) {
try {
extendedUserProfile.z2 = Handler.getDescription(userID);
String description = Handler.getDescription(userID);
if (!TextUtils.isEmpty(description)) {
extendedUserProfile.z2 = description;
}
} catch (NullPointerException e) {
Log.e(TAG, "null \"profiles\" node\n" + e);
}
@@ -6,8 +6,8 @@ import com.vk.core.util.ToastUtils;
import com.vk.dto.music.MusicTrack;
import com.vk.dto.music.Playlist;
import com.vtosters.lite.R;
import ru.vtosters.hooks.MusicCacheFilesHook;
import ru.vtosters.lite.music.cache.CacheDatabaseDelegate;
import ru.vtosters.hooks.music.MusicCacheFilesHook;
import ru.vtosters.lite.music.cache.MusicCacheImpl;
import ru.vtosters.lite.music.callback.MusicCallbackBuilder;
import ru.vtosters.lite.music.converter.playlist.PlaylistConverter;
import ru.vtosters.lite.music.downloader.AudioGet;
@@ -58,10 +58,10 @@ public class AudioDownloader {
}
public static void cacheTrack(MusicTrack track) {
var trackId = track.y1();
var trackId = LibVKXClient.asId(track);
if (CacheDatabaseDelegate.isCached(trackId)) {
CacheDatabaseDelegate.removeTrackFromCache(LibVKXClient.asId(track));
if (MusicCacheImpl.isCachedTrack(trackId)) {
MusicCacheImpl.removeTrack(trackId);
AndroidUtils.sendToast(AndroidUtils.getString("audio_deleted_from_cache"));
return;
}
@@ -1,13 +1,10 @@
package ru.vtosters.lite.foaf;
import android.annotation.SuppressLint;
import android.app.ProgressDialog;
import android.content.Context;
import android.util.Log;
import com.vk.core.dialogs.alert.VkAlertDialog;
import com.vk.core.network.Network;
import com.vtosters.lite.R;
import okhttp3.Headers;
import okhttp3.OkHttpClient;
import org.json.JSONException;
import org.json.JSONObject;
@@ -15,13 +12,9 @@ import ru.vtosters.lite.di.singleton.VtOkHttpClient;
import ru.vtosters.lite.net.Request;
import ru.vtosters.lite.utils.LifecycleUtils;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.Objects;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -34,43 +27,15 @@ import static ru.vtosters.lite.utils.AndroidUtils.getGlobalContext;
public class FoafBase {
private static final Pattern FOAF_REGEX = Pattern.compile("<ya:created dc:date=\"(.+?)\"");
private static final Pattern FOAF_REGEX_LAST_SEEN = Pattern.compile("<ya:lastLoggedIn dc:date=\"(.*)(((\\+|-)\\d\\d):(\\d\\d))\"");
private static final String API_VKNEXT = "https://api.vtosters.app/v1/getBypassedOnlineInfo?json=1&ids=";
private static final OkHttpClient client = VtOkHttpClient.getInstance();
public static long getLastSeen(long origtime, int id) throws ParseException {
var request = new okhttp3.Request.a()
.b(getLink(id))
.a(Headers.a("User-Agent", Network.l.c().a(), "Content-Type", "application/x-www-form-urlencoded; charset=utf-8")).a();
String response = null;
try {
response = client.a(request).execute().a().g();
} catch (IOException e) {
Log.e("FoafBase", e.getMessage());
}
Matcher matcher = FOAF_REGEX_LAST_SEEN.matcher(response);
if (!matcher.find()) {
return origtime;
}
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.US);
sdf.setTimeZone(TimeZone.getTimeZone("Europe/Moscow"));
Date date = sdf.parse(Objects.requireNonNull(matcher.group(1)));
sdf.setTimeZone(TimeZone.getDefault());
return sdf.parse(sdf.format(date)).getTime() / 1000;
}
public static JSONObject getBypassedOnlineInfo(int id) throws JSONException {
var request = new okhttp3.Request.a()
.b(API_VKNEXT + id)
.a();
String response = null;
String response;
var dummy = new JSONObject();
dummy.put("last_seen", 0);
@@ -86,7 +51,6 @@ public class FoafBase {
}
public static JSONObject getBypassedOnlineInfo(String ids) {
var request = new okhttp3.Request.a()
.b(API_VKNEXT + ids)
.a();
@@ -135,7 +99,15 @@ public class FoafBase {
progressDialog.cancel();
Matcher matcher = FOAF_REGEX.matcher(str);
matcher.find();
String normalHumanDate = getNormalHumanDate(Objects.requireNonNull(matcher.group(1)));
String m = matcher.group(1);
if (m == null) {
LifecycleUtils.getCurrentActivity().runOnUiThread(() -> makeText(getGlobalContext(), context.getString(R.string.foaferr), LENGTH_SHORT).show());
return;
}
String normalHumanDate = getNormalHumanDate(m);
LifecycleUtils.getCurrentActivity().runOnUiThread(() -> new VkAlertDialog.Builder(context)
.setTitle(R.string.addinf)
@@ -147,17 +119,14 @@ public class FoafBase {
.show());
} catch (Exception e) {
LifecycleUtils.getCurrentActivity().runOnUiThread(() -> makeText(getGlobalContext(), context.getString(R.string.foaferr), LENGTH_SHORT).show());
e.printStackTrace();
}
}
private static long daysPassedFromFoafDate(String str) {
try {
@SuppressLint("SimpleDateFormat")
Date parse = new SimpleDateFormat("yyyy-MM-dd").parse(str.split(" ")[1]);
return TimeUnit.DAYS.convert(new Date().getTime() - parse.getTime(), TimeUnit.MILLISECONDS);
} catch (ParseException e) {
e.printStackTrace();
return -1;
}
}
@@ -7,14 +7,13 @@ import androidx.annotation.Nullable;
import bruhcollective.itaysonlab.libvkx.client.LibVKXClient;
import com.vk.dto.music.MusicTrack;
import com.vtosters.lite.R;
import okhttp3.Callback;
import okhttp3.*;
import org.json.JSONException;
import org.json.JSONObject;
import ru.vtosters.hooks.other.Preferences;
import ru.vtosters.lite.di.singleton.VtOkHttpClient;
import ru.vtosters.lite.downloaders.AudioDownloader;
import ru.vtosters.lite.music.cache.CacheDatabaseDelegate;
import ru.vtosters.lite.music.cache.MusicCacheImpl;
import ru.vtosters.lite.utils.AndroidUtils;
import java.io.IOException;
@@ -47,7 +46,7 @@ public class LastFMScrobbler {
return;
}
if (Preferences.autocache() && !CacheDatabaseDelegate.isCached(uid)) {
if (Preferences.autocache() && !MusicCacheImpl.isCachedTrack(uid)) {
if (LibVKXClient.isIntegrationEnabled()) {
LibVKXClient.getInstance().runOnService(service -> {
try {
@@ -1,158 +0,0 @@
package ru.vtosters.lite.music;
import android.util.Log;
import com.vk.dto.music.MusicTrack;
import java8.util.concurrent.CompletableFuture;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import ru.vtosters.lite.di.singleton.VtOkHttpClient;
import ru.vtosters.lite.music.cache.CacheDatabaseDelegate;
import ru.vtosters.lite.music.converter.ts.FFMpeg;
import ru.vtosters.lite.music.converter.ts.TSMerger;
import ru.vtosters.lite.music.downloader.ThumbnailDownloader;
import ru.vtosters.lite.music.interfaces.ITrackDownloader;
import ru.vtosters.lite.utils.AndroidUtils;
import ru.vtosters.lite.utils.IOUtils;
import javax.crypto.NoSuchPaddingException;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
public class M3UDownloader implements ITrackDownloader {
private static final OkHttpClient client = VtOkHttpClient.getInstance();
public static M3UDownloader getInstance() {
return Holder.INSTANCE;
}
public static String getTitle(MusicTrack track) {
String title = null;
if (track.f != null) {
title = track.f;
if (!track.g.isEmpty()) {
title += " (" + track.g + ")";
}
}
return title;
}
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 {
Request request = new Request.a().b(track.D).a();
String response = client.a(request).execute().a().g();
parse(response, outDir, callback, track, cache);
} catch (IOException e) {
e.printStackTrace();
callback.onFailure();
}
}
private void parse(String payload, File outDir, Callback callback, MusicTrack track, boolean cache) throws UnsupportedEncodingException {
if (cache) downloadThumbnails(track);
VKM3UParser parser = new VKM3UParser(track.D.substring(0, track.D.lastIndexOf("/") + 1), payload);
AtomicInteger progress = new AtomicInteger(0);
List<CompletableFuture<Void>> futures = createFutures(parser, progress, callback, track);
var resultTs = getResultTsFile(track);
var resultMp3 = getResultMp3File(outDir, cache, track);
callback.onProgress(5);
var future = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
future.join();
future.exceptionally(throwable -> {
callback.onFailure();
return null;
}).thenApply(v -> TSMerger.merge(getTsesDir(track), resultTs))
.thenApply(mergeResult -> mergeResult && FFMpeg.convert(resultTs, resultMp3.getAbsolutePath(), track))
.thenApply(convertResult -> {
if (convertResult)
callback.onProgress(10 + Math.round(80.0f * progress.addAndGet(1) / parser.mTransportStreams.size()));
else callback.onFailure();
return convertResult;
}).thenRun(() -> IOUtils.deleteRecursive(getTsesDir(track))).thenRun(() -> {
callback.onSuccess();
if (cache) CacheDatabaseDelegate.insertTrack(track);
});
}
private void downloadThumbnails(MusicTrack track) {
try {
ThumbnailDownloader.downloadThumbnails(track);
} catch (IOException e) {
e.printStackTrace();
}
}
private List<CompletableFuture<Void>> createFutures(VKM3UParser parser, AtomicInteger progress, Callback callback, MusicTrack track) {
List<CompletableFuture<Void>> futures = new ArrayList<>(parser.mTransportStreams.size());
var tses = parser.mTransportStreams;
for (var i = 0; i < tses.size(); ++i) {
final var ts = tses.get(i);
final var filename = i + ".ts";
futures.add(CompletableFuture.runAsync(() -> processTransportStream(parser, ts, filename, progress, tses.size(), callback, track)));
}
return futures;
}
private void processTransportStream(VKM3UParser parser, TransportStream ts, String filename, AtomicInteger progress, int size, Callback callback, MusicTrack track) {
try {
InputStream mediaIs = IOUtils.openStream(ts.getMediaSegmentUri());
byte[] content;
if (ts.needDecoding()) {
InputStream keyPubIs = IOUtils.openStream(ts.getKeyURL());
content = IOUtils.decodeStream(mediaIs, IOUtils.readFully(keyPubIs));
keyPubIs.close();
} else {
content = IOUtils.readFully(mediaIs);
}
File tsDump = new File(getTsesDir(track), filename);
IOUtils.writeToFile(tsDump, content);
callback.onProgress(10 + Math.round(80.0f * progress.addAndGet(1) / size));
callback.onSizeReceived((long) content.length * size, parser.mHeapSize);
mediaIs.close();
} catch (IOException | NoSuchPaddingException | NoSuchAlgorithmException |
InvalidAlgorithmParameterException | InvalidKeyException e) {
callback.onFailure();
throw new RuntimeException(e);
}
}
private File getTsesDir(MusicTrack track) {
var filesDir = AndroidUtils.getGlobalContext().getFilesDir();
var tsesDir = new File(filesDir, String.valueOf(track.d));
tsesDir.mkdirs();
return tsesDir;
}
private File getResultTsFile(MusicTrack track) {
return new File(getTsesDir(track), "result.ts");
}
private File getResultMp3File(File outDir, boolean cache, MusicTrack track) {
var fileName = IOUtils.getValidFileName((cache ? "track" : track.C + " - " + getTitle(track)) + ".mp3");
return new File(outDir, fileName);
}
// Initialization-on-demand
private static class Holder {
private static final M3UDownloader INSTANCE = new M3UDownloader();
}
}
@@ -1,36 +0,0 @@
package ru.vtosters.lite.music;
import android.text.TextUtils;
public class TransportStream {
private final String mMediaSegmentUri;
private String mKeyUri;
private String mMediaSegmentName;
public TransportStream(String baseURI, String mediaSegmentName) {
this.mMediaSegmentUri = baseURI + mediaSegmentName;
}
public TransportStream(String keyURI, String baseURI, String mediaSegmentName) {
this.mKeyUri = keyURI;
this.mMediaSegmentUri = baseURI + mediaSegmentName;
this.mMediaSegmentName = mediaSegmentName;
}
public boolean needDecoding() {
return !TextUtils.isEmpty(mKeyUri);
}
public String getKeyURL() {
return mKeyUri;
}
public String getMediaSegmentUri() {
return this.mMediaSegmentUri;
}
public String getMediaSegmentName() {
return this.mMediaSegmentName;
}
}
@@ -1,126 +0,0 @@
package ru.vtosters.lite.music;
import android.text.TextUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
/**
* The parser for extended M3U playlist format used by VK.
*
* @see <a href="https://datatracker.ietf.org/doc/html/rfc8216#ref-M3U">M3U</a>
**/
public class VKM3UParser {
//Playlist types
static public int PLAYLIST_TYPE_VOD = 0;
static public int PLAYLIST_TYPE_EVENT = 1;
private final String mData;
//region Extended M3U global extensions
public List<TransportStream> mTransportStreams = new ArrayList<>();
/**
* @see <a href="https://datatracker.ietf.org/doc/html/rfc8216#section-4.3.1.2">EXT-X-VERSION</a>
**/
public int mVersion = -1;
/**
* The EXT-X-ALLOW-CACHE tag is used in protocol version 6 or lower.
* If the protocol version is upgraded by VK, we will not support that.
**/
public boolean mAllowCache = false;
/**
* @see <a href="https://datatracker.ietf.org/doc/html/rfc8216#section-4.3.3.1">EXT-X-TARGETDURATION</a>
**/
public int mTargetDuration = -1;
/**
* @see <a href="https://datatracker.ietf.org/doc/html/rfc8216#section-4.3.3.2">EXT-X-MEDIA-SEQUENCE</a>
**/
public int mMediaSequence = -1;
/**
* @see <a href="https://datatracker.ietf.org/doc/html/rfc8216#section-4.3.3.3">EXT-X-DISCONTINUITY-SEQUENCE</a>
**/
public int mDiscontinuitySequence = -1;
//endregion
/**
* @see <a href="https://datatracker.ietf.org/doc/html/rfc8216#section-4.3.3.5">EXT-X-PLAYLIST-TYPE</a>
**/
public int mPlaylistType = -1;
/**
* @see <a href="https://datatracker.ietf.org/doc/html/rfc8216#section-4.3.3.6">EXT-X-I-FRAMES-ONLY</a>
**/
public boolean mIFramesOnly = false;
public long mHeapSize = 0;
public VKM3UParser(String baseUri, String data) {
mData = data;
init(baseUri);
}
public static boolean isTS(String str) {
return str.matches(".+\\.ts.*");
}
private void init(String baseUri) {
if (TextUtils.isEmpty(mData)) throw new NullPointerException("mData==null");
Scanner scanner = new Scanner(mData);
String line = scanner.nextLine();
if (!"#EXTM3U".equals(line))
throw new IllegalStateException(String.format("Unknown initial M3U tag: %s", line));
boolean aes128 = false;
String keyUri = "";
while (scanner.hasNextLine()) {
line = scanner.nextLine();
if ('#' == line.charAt(0)) {
parseTag(line);
if (line.startsWith("#EXT-X-KEY:") && line.contains("METHOD=AES-128")) {
aes128 = true;
keyUri = getKeyUri(line, baseUri);
}
} else if (isTS(line)) {
addTransportStream(aes128, keyUri, baseUri, line);
aes128 = false;
} else {
throw new RuntimeException(String.format("Failed to parse: %s", line));
}
mHeapSize += line.getBytes().length;
}
}
private void parseTag(String line) {
if (line.startsWith("#EXT-X-VERSION:"))
mVersion = Integer.parseInt(line.substring(15));
else if (line.startsWith("#EXT-X-ALLOW-CACHE:"))
mAllowCache = "YES".equalsIgnoreCase(line.substring(19));
else if (line.startsWith("#EXT-X-TARGETDURATION:"))
mTargetDuration = Integer.parseInt(line.substring(22));
else if (line.startsWith("#EXT-X-MEDIA-SEQUENCE:"))
mMediaSequence = Integer.parseInt(line.substring(22));
else if (line.startsWith("#EXT-X-DISCONTINUITY-SEQUENCE:"))
mDiscontinuitySequence = Integer.parseInt(line.substring(30));
else if (line.startsWith("#EXT-X-PLAYLIST-TYPE:"))
parsePlaylistType(line.substring(21));
else if (line.startsWith("#EXT-X-I-FRAMES-ONLY"))
mIFramesOnly = true;
}
private void parsePlaylistType(String type) {
switch (type) {
case "VOD" -> mPlaylistType = PLAYLIST_TYPE_VOD;
case "EVENT" -> mPlaylistType = PLAYLIST_TYPE_EVENT;
default -> throw new IllegalStateException(String.format("Unknown playlist type: %s", type));
}
}
private String getKeyUri(String line, String baseUri) {
String substr = line.substring(11);
substr = substr.substring(substr.indexOf('"') + 1, substr.lastIndexOf('"'));
//TODO: supposed endpoint
return substr.startsWith("http") ? substr : baseUri + substr;
}
private void addTransportStream(boolean aes128, String keyUri, String baseUri, String line) {
TransportStream ts = aes128
? new TransportStream(keyUri, baseUri, line)
: new TransportStream(baseUri, line);
mTransportStreams.add(ts);
}
}
@@ -1,343 +0,0 @@
package ru.vtosters.lite.music.cache;
import android.content.Context;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.RemoteException;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import bruhcollective.itaysonlab.libvkx.ILibVkxService;
import bruhcollective.itaysonlab.libvkx.client.LibVKXClient;
import bruhcollective.itaysonlab.libvkx.client.LibVKXClientImpl;
import com.vk.core.util.ToastUtils;
import com.vk.dto.music.MusicTrack;
import com.vtosters.lite.R;
import org.json.JSONObject;
import ru.vtosters.lite.utils.AndroidUtils;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import static ru.vtosters.lite.music.cache.FileCacheImplementation.getCacheDir;
import static ru.vtosters.lite.utils.AndroidUtils.getGlobalContext;
import static ru.vtosters.lite.utils.AndroidUtils.sendToast;
import static ru.vtosters.lite.utils.IOUtils.deleteRecursive;
public class CacheDatabaseDelegate {
private static final int DB_VERSION = 0x1;
private static final String DB_NAME = "vt_lite_cache.db";
private static final String TABLE_NAME = "tracks";
private static final String COLUMN_ID = "id";
private static final String COLUMN_TRACK_ID = "trackId";
private static final String COLUMN_ALBUM_ID = "albumId";
private static final String COLUMN_TITLE = "title";
private static final String COLUMN_SUBTITLE = "subtitle";
private static final String COLUMN_ARTIST = "artist";
private static final String COLUMN_ALBUM_TITLE = "albumTitle";
private static final String COLUMN_EXPLICIT = "explicit";
private static final String COLUMN_HAS_ARTWORK = "hasArtwork";
private static final String COLUMN_DURATION = "duration";
private static DbHelper helperInstance;
protected static DbHelper getHelper() {
if (helperInstance == null) helperInstance = new DbHelper(getGlobalContext());
return helperInstance;
}
// You MUST call this method ONLY AFTER TRACK IS DOWNLOADED!
public static void insertTrack(MusicTrack track) {
try {
var json = track.J();
var albumNode = json.optJSONObject("album");
var hasArtwork = false;
var albumId = -1;
var albumName = "";
if (albumNode != null) {
hasArtwork = albumNode.has("thumb");
albumId = albumNode.getInt("id");
albumName = albumNode.getString("title");
}
var formula = String.format(
"INSERT INTO %s (%s, %s, %s, %s, %s, %s, %s, %s, %s) VALUES ('%s', %s, '%s', '%s', '%s', '%s', %s, %s, %s)",
TABLE_NAME,
COLUMN_TRACK_ID, COLUMN_ALBUM_ID, COLUMN_TITLE, COLUMN_SUBTITLE, COLUMN_ARTIST, COLUMN_ALBUM_TITLE, COLUMN_EXPLICIT, COLUMN_DURATION, COLUMN_HAS_ARTWORK,
track.y1(),
albumId,
URLEncoder.encode(json.getString("title"), "UTF-8"),
URLEncoder.encode(json.optString("subtitle"), "UTF-8"),
URLEncoder.encode(json.getString("artist"), "UTF-8"),
URLEncoder.encode(albumName, "UTF-8"),
json.optInt("is_explicit"),
json.getInt("duration"),
hasArtwork ? 1 : 0
);
getHelper().getReadableDatabase().execSQL(formula);
} catch (Exception e) {
e.printStackTrace();
ToastUtils.a(AndroidUtils.getString(R.string.track_bd_insert_err) + " [" + e.getMessage() + "]", true);
}
}
public static void insertTracks(List<MusicTrack> tracks) {
for (var track : tracks) insertTrack(track);
}
public static void clear() {
getHelper().getReadableDatabase().execSQL(String.format("DELETE FROM %s", TABLE_NAME));
deleteRecursive(getCacheDir());
sendToast(AndroidUtils.getString(R.string.cached_tracks_deleted));
}
@NonNull
private static JSONObject reconstructFromDb(Cursor cursor) {
try {
var trackId = cursor.getString(getColumnIndex(cursor, COLUMN_TRACK_ID));
var splits = trackId.split("_");
var id = Integer.parseInt(splits[1]);
var ownerId = Integer.parseInt(splits[0]);
return new JSONObject()
.put("url", FileCacheImplementation.getServerPath())
.put("id", id)
.put("owner_id", ownerId)
.put("title", URLDecoder.decode(cursor.getString(getColumnIndex(cursor, COLUMN_TITLE)), "UTF-8"))
.put("subtitle", URLDecoder.decode(cursor.getString(getColumnIndex(cursor, COLUMN_SUBTITLE)), "UTF-8"))
.put("artist", URLDecoder.decode(cursor.getString(getColumnIndex(cursor, COLUMN_ARTIST)), "UTF-8"))
.put("duration", cursor.getInt(getColumnIndex(cursor, COLUMN_DURATION)))
.put("is_explicit", cursor.getInt(getColumnIndex(cursor, COLUMN_EXPLICIT)))
.put("access_key", 2281488)
.putOpt("album", reconstructAlbumFromDb(cursor, trackId));
} catch (Exception e) {
e.printStackTrace();
return new JSONObject();
}
}
@Nullable
private static JSONObject reconstructAlbumFromDb(Cursor cursor, String trackId) {
try {
if (!FileCacheImplementation.getTrackThumbnail(trackId, 300).exists()
&& !FileCacheImplementation.getTrackThumbnail(trackId, 600).exists()
&& !FileCacheImplementation.getTrackThumbnail(trackId, 1200).exists()
) {
var ownerId = Integer.parseInt(trackId.split("_")[1]);
return new JSONObject()
.put("id", cursor.getInt(getColumnIndex(cursor, COLUMN_ALBUM_ID)))
.put("owner_id", ownerId)
.put("title", URLDecoder.decode(cursor.getString(getColumnIndex(cursor, COLUMN_ALBUM_TITLE)), "UTF-8"))
.putOpt("thumb", reconstructAlbumThumbFromDb(cursor, trackId));
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@Nullable
private static JSONObject reconstructAlbumThumbFromDb(Cursor cursor, String trackId) {
try {
if (cursor.getInt(getColumnIndex(cursor, COLUMN_HAS_ARTWORK)) != 0)
new JSONObject()
.put("height", 600)
.put("width", 600)
.putOpt("photo_300", FileCacheImplementation.getFileUri(FileCacheImplementation.getTrackThumbnail(trackId, 300)))
.putOpt("photo_600", FileCacheImplementation.getFileUri(FileCacheImplementation.getTrackThumbnail(trackId, 600)))
.putOpt("photo_1200", FileCacheImplementation.getFileUri(FileCacheImplementation.getTrackThumbnail(trackId, 1200)));
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private static int getColumnIndex(Cursor cursor, String name) {
return cursor.getColumnIndexOrThrow(name);
}
public static boolean hasTracks() {
if (LibVKXClient.isIntegrationEnabled()) {
return true;
}
var formula = String.format(
"SELECT COUNT(%s) FROM %s",
COLUMN_ID,
TABLE_NAME
);
var cursor = getHelper().getReadableDatabase().rawQuery(formula, null);
if (cursor != null) {
cursor.moveToFirst();
int count = cursor.getInt(0);
cursor.close();
return count > 0;
}
return false;
}
public static List<MusicTrack> getTracks() {
return getTracks(String.format(
"SELECT * FROM %s",
TABLE_NAME
));
}
public static List<MusicTrack> getTracksAsPlaylist() {
return getTracks(String.format(
"SELECT * FROM %s WHERE %s > -1 GROUP BY %s HAVING COUNT(*) > 1 ORDER by id DESC",
TABLE_NAME,
COLUMN_ALBUM_ID,
COLUMN_ALBUM_ID
));
}
public static List<MusicTrack> getTracksByAlbum(String id) {
return getTracks(String.format(
"SELECT COUNT(%s) FROM %s WHERE %s = %s",
COLUMN_ID,
TABLE_NAME,
COLUMN_ALBUM_ID,
id
));
}
public static List<MusicTrack> getTracksByAlbumOne(String id) {
return getTracks(String.format(
"SELECT * FROM %s WHERE %s = %s LIMIT 1",
TABLE_NAME,
COLUMN_ALBUM_ID,
id
));
}
public static List<MusicTrack> getTracks(String query) {
List<MusicTrack> list = new ArrayList<>();
try (Cursor cursor = getHelper().getReadableDatabase().rawQuery(query, new String[0])) {
while (cursor.moveToNext()) {
list.add(new MusicTrack(reconstructFromDb(cursor)));
}
} catch (Exception e) {
e.printStackTrace();
}
return list;
}
public static boolean isCached(String trackId) {
var formula = String.format(
"SELECT COUNT(%s) FROM %s WHERE %s = '%s'",
COLUMN_ID,
TABLE_NAME,
COLUMN_TRACK_ID,
trackId
);
try {
Cursor size = getHelper().getReadableDatabase().rawQuery(formula, null);
if (size != null) {
size.moveToFirst();
int count = size.getInt(0);
size.close();
return count != 0;
}
} catch (SQLException e) {
e.printStackTrace();
}
return false;
}
public static boolean isVkxCached(int playlist_id, int owner_id) {
return LibVKXClient.getInstance().runOnServiceSync(new LibVKXClientImpl.LibVKXActionGeneric<>() {
@Override
public Boolean run(ILibVkxService service) {
try {
return service.isPlaylistCached(playlist_id, owner_id);
} catch (RemoteException e) {
return false;
}
}
@Override
public Boolean defaultValue() {
return false;
}
});
}
public static boolean isVkxCached(String trackId) {
return LibVKXClient.getInstance().runOnServiceSync(new LibVKXClientImpl.LibVKXActionGeneric<>() {
@Override
public Boolean run(ILibVkxService service) {
try {
var splits = trackId.split("_");
var id = Integer.parseInt(splits[1]);
var owner = Integer.parseInt(splits[0]);
return service.isTrackCached(id, owner);
} catch (RemoteException e) {
return false;
}
}
@Override
public Boolean defaultValue() {
return false;
}
});
}
public static void removeTrackFromCache(String trackId) {
deleteRecursive(FileCacheImplementation.getTrackFolder(trackId));
var formula = String.format(
"DELETE FROM %s WHERE %s = '%s'",
TABLE_NAME,
COLUMN_TRACK_ID,
trackId
);
getHelper().getWritableDatabase().execSQL(formula);
}
public static long getTrackCount() {
if (!LibVKXClient.isIntegrationEnabled())
return DatabaseUtils.queryNumEntries(getHelper().getReadableDatabase(), TABLE_NAME);
return LibVKXClient.getInstance().runOnServiceSync(new LibVKXClientImpl.LibVKXActionGeneric<Long>() {
@Override
public Long run(ILibVkxService service) {
try {
return (long) service.getCache().size();
} catch (RemoteException e) {
e.printStackTrace();
return defaultValue();
}
}
@Override
public Long defaultValue() {
return 0L;
}
});
}
private static class DbHelper extends SQLiteOpenHelper {
public DbHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
}
public void onCreate(SQLiteDatabase db) {
var formula = String.format(
"CREATE TABLE %s (%s INTEGER PRIMARY KEY AUTOINCREMENT, %s TEXT, %s INTEGER, %s TEXT, %s TEXT, %s TEXT, %s TEXT, %s INTEGER, %s INTEGER, %s INTEGER)",
TABLE_NAME,
COLUMN_ID, COLUMN_TRACK_ID, COLUMN_ALBUM_ID, COLUMN_TITLE, COLUMN_SUBTITLE, COLUMN_ARTIST, COLUMN_ALBUM_TITLE, COLUMN_EXPLICIT, COLUMN_DURATION, COLUMN_HAS_ARTWORK
);
db.execSQL(formula);
}
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
}
@@ -1,50 +0,0 @@
package ru.vtosters.lite.music.cache;
import android.net.Uri;
import com.vk.dto.music.MusicTrack;
import ru.vtosters.hooks.MusicCacheFilesHook;
import ru.vtosters.lite.utils.AndroidUtils;
import ru.vtosters.lite.utils.IOUtils;
import java.io.File;
public class FileCacheImplementation {
public static void clear() {
CacheDatabaseDelegate.clear();
IOUtils.deleteRecursive(getCacheDir(), false);
}
public static File getCacheDir() {
return AndroidUtils.getGlobalContext().getExternalFilesDir("vt_tracks");
}
public static File getTrackFolder(String trackId) {
var dir = new File(getCacheDir(), trackId);
dir.mkdirs();
return dir;
}
public static File getThumbnailsFolder(String trackId) {
var dir = new File(getTrackFolder(trackId), "thumbs");
dir.mkdirs();
return dir;
}
public static File getTrackFile(MusicTrack track) {
return MusicCacheFilesHook.getTrackFile(track.y1());
}
public static File getTrackThumbnail(String trackId, int factor) {
return new File(getThumbnailsFolder(trackId), "thumb_" + factor + ".png");
}
public static String getFileUri(File file) {
if (!file.exists()) return null;
return Uri.fromFile(file).toString();
}
public static String getServerPath() {
return "https://vtosters.app";
}
}
@@ -0,0 +1,85 @@
package ru.vtosters.lite.music.cache;
import android.content.Context;
import com.vk.dto.music.MusicTrack;
import ru.vtosters.lite.music.cache.db.MusicCacheDb;
import java.util.List;
public class MusicCacheDbDelegate {
static synchronized private MusicCacheDb connectToDb(Context context) {
synchronized (MusicCacheDb.class) {
return new MusicCacheDb(context);
}
}
public static void addTrack(Context context,
String trackId,
String albumId,
String title,
String subtitle,
String artist,
String albumTitle,
boolean explicit,
int duration,
boolean hasArtwork) {
try (var db = connectToDb(context)) {
db.addTrack(
trackId,
albumId,
title,
subtitle,
artist,
albumTitle,
explicit,
duration,
hasArtwork);
}
}
public static void removeTrack(Context context, String trackId) {
try (var db = connectToDb(context)) {
db.deleteTrack(trackId);
}
}
public static List<MusicTrack> getAllTracks(Context context) {
try (var db = connectToDb(context)) {
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();
}
}
public static long getTracksCount(Context context) {
try (var db = connectToDb(context)) {
return db.getTracksCount();
}
}
public static boolean isCachedTrack(Context context, String trackId) {
try (var db = connectToDb(context)) {
return db.isCachedTrack(trackId);
}
}
public static void drop(Context context) {
context.deleteDatabase(MusicCacheDb.Constants.DB_NAME);
}
}
@@ -0,0 +1,84 @@
package ru.vtosters.lite.music.cache;
import android.os.RemoteException;
import android.text.TextUtils;
import bruhcollective.itaysonlab.libvkx.ILibVkxService;
import bruhcollective.itaysonlab.libvkx.client.LibVKXClient;
import bruhcollective.itaysonlab.libvkx.client.LibVKXClientImpl;
import com.vk.dto.music.MusicTrack;
import ru.vtosters.lite.utils.AndroidUtils;
import ru.vtosters.lite.utils.music.MusicCacheStorageUtils;
import java.util.List;
public class MusicCacheImpl {
public static void addTrack(MusicTrack track) {
MusicCacheDbDelegate.addTrack(
AndroidUtils.getGlobalContext(),
track.y1(),
track.I != null ? track.I.getId() + "" : "-1",
track.f,
!TextUtils.isEmpty(track.g) ? track.g : "",
track.C,
track.I != null ? track.I.getTitle() : "",
track.K,
track.h,
track.I != null && track.I.u1() != null);
}
public static void removeTrack(String trackId) {
MusicCacheDbDelegate.removeTrack(AndroidUtils.getGlobalContext(), trackId);
MusicCacheStorageUtils.removeTrackDirById(trackId);
}
public static List<MusicTrack> getAllTracks() {
return MusicCacheDbDelegate.getAllTracks(AndroidUtils.getGlobalContext());
}
public static List<MusicTrack> getAlbumById(String albumId) {
return MusicCacheDbDelegate.getAlbumById(AndroidUtils.getGlobalContext(), albumId);
}
public static List<MusicTrack> getFirstAlbumTrack(String albumId) {
return MusicCacheDbDelegate.getFirstAlbumTrack(AndroidUtils.getGlobalContext(), albumId);
}
public static List<MusicTrack> getPlaylist() {
return MusicCacheDbDelegate.getPlaylist(AndroidUtils.getGlobalContext());
}
public static long getTracksCount() {
return !LibVKXClient.isIntegrationEnabled()
? MusicCacheDbDelegate.getTracksCount(AndroidUtils.getGlobalContext())
: LibVKXClient.getInstance().runOnServiceSync(
new LibVKXClientImpl.LibVKXActionGeneric<Long>() {
@Override
public Long run(ILibVkxService service) {
try {
return (long) service.getCache().size();
} catch (RemoteException e) {
e.printStackTrace();
return defaultValue();
}
}
@Override
public Long defaultValue() {
return 0L;
}
});
}
public static boolean isCachedTrack(String trackId) {
return MusicCacheDbDelegate.isCachedTrack(AndroidUtils.getGlobalContext(), trackId);
}
public static boolean isEmpty() {
return !LibVKXClient.isIntegrationEnabled() && getTracksCount() == 0L;
}
public static void clear() {
MusicCacheDbDelegate.drop(AndroidUtils.getGlobalContext());
MusicCacheStorageUtils.clear();
}
}
@@ -0,0 +1,246 @@
package ru.vtosters.lite.music.cache.db;
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.net.Uri;
import android.util.Log;
import androidx.annotation.Nullable;
import com.vk.dto.music.AlbumLink;
import com.vk.dto.music.MusicTrack;
import com.vk.dto.music.Thumb;
import org.json.JSONObject;
import ru.vtosters.lite.utils.AndroidUtils;
import ru.vtosters.lite.utils.music.MusicCacheStorageUtils;
import java.io.File;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;
/**
* <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>
*/
public class MusicCacheDb extends SQLiteOpenHelper implements AutoCloseable { //for pre-Q
public MusicCacheDb(Context context) {
super(context, Constants.DB_NAME, null, Constants.DV_VERSION);
}
private static MusicTrack fromCursor(Cursor cur) {
try {
int trackIdIndex = cur.getColumnIndex(Constants.COLUMN_TRACK_ID);
int titleIndex = cur.getColumnIndex(Constants.COLUMN_TITLE);
int subtitleIndex = cur.getColumnIndex(Constants.COLUMN_SUBTITLE);
int artistIndex = cur.getColumnIndex(Constants.COLUMN_ARTIST);
int durationIndex = cur.getColumnIndex(Constants.COLUMN_DURATION);
int explicitIndex = cur.getColumnIndex(Constants.COLUMN_EXPLICIT);
int albumIdIndex = cur.getColumnIndex(Constants.COLUMN_ALBUM_ID);
int albumTitleIndex = cur.getColumnIndex(Constants.COLUMN_ALBUM_TITLE);
if (trackIdIndex == -1 || titleIndex == -1 || subtitleIndex == -1 || artistIndex == -1 || durationIndex == -1 || explicitIndex == -1 || albumIdIndex == -1) {
return null;
}
String trackId = cur.getString(trackIdIndex);
String[] splits = trackId.split("_");
if (splits.length != 2) {
return null;
}
MusicTrack res = new MusicTrack();
res.J = Integer.MAX_VALUE + ""; //access_key
res.D = "https://vtosters.app"; //url
res.d = Integer.parseInt(splits[1]); //id
res.e = Integer.parseInt(splits[0]); //owner_id
res.f = URLDecoder.decode(cur.getString(titleIndex), "UTF-8"); //title
res.g = URLDecoder.decode(cur.getString(subtitleIndex), "UTF-8"); //subtitle
res.C = URLDecoder.decode(cur.getString(artistIndex), "UTF-8"); //artist
res.h = cur.getInt(durationIndex); //duration
res.K = cur.getInt(explicitIndex) > 0; //is_explicit
int albumId = Integer.parseInt(cur.getString(albumIdIndex));
String albumTitle = albumTitleIndex != -1 ? cur.getString(albumTitleIndex) : null;
JSONObject node = new JSONObject();
File f300 = MusicCacheStorageUtils.getTrackThumb(trackId, 300);
File f600 = MusicCacheStorageUtils.getTrackThumb(trackId, 600);
File f1200 = MusicCacheStorageUtils.getTrackThumb(trackId, 1200);
if (f300.exists()) node.putOpt("photo_300", Uri.fromFile(f300).toString());
if (f600.exists()) node.putOpt("photo_600", Uri.fromFile(f600).toString());
if (f1200.exists()) node.putOpt("photo_1200", Uri.fromFile(f1200).toString());
res.I = new AlbumLink(
albumId,
res.e,
res.J,
albumTitle,
node.length() > 0
? new Thumb(node.put("width", 600)
.put("height", 600))
: null
); //album
return res;
} catch (Throwable ignored) {
// ignored
}
return null;
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(Constants.CREATE_QUERY);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (oldVersion < newVersion) {
List<MusicTrack> tracks = getTracksWithCursor(db.query(Constants.TABLE_NAME, null, null, null, null, null, null));
db.execSQL(Constants.DROP_QUERY);
onCreate(db);
for (MusicTrack track : tracks) {
ContentValues vals = new ContentValues();
vals.put(Constants.COLUMN_TRACK_ID, track.y1());
vals.put(Constants.COLUMN_ALBUM_ID, track.I != null ? track.I.getId() + "" : "-1");
vals.put(Constants.COLUMN_TITLE, track.f);
vals.put(Constants.COLUMN_SUBTITLE, track.g);
vals.put(Constants.COLUMN_ARTIST, track.C);
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_DURATION, track.h);
vals.put(Constants.COLUMN_HAS_ARTWORK, track.I != null && track.I.u1() != null);
db.insert(Constants.TABLE_NAME, null, vals);
}
}
}
public void addTrack(String trackId, String albumId, String title, String subtitle, String artist, String albumTitle, boolean explicit, int duration, boolean hasArtwork) {
ContentValues vals = new ContentValues();
vals.put(Constants.COLUMN_TRACK_ID, trackId);
vals.put(Constants.COLUMN_ALBUM_ID, albumId);
vals.put(Constants.COLUMN_TITLE, title);
vals.put(Constants.COLUMN_SUBTITLE, subtitle);
vals.put(Constants.COLUMN_ARTIST, artist);
vals.put(Constants.COLUMN_ALBUM_TITLE, albumTitle);
vals.put(Constants.COLUMN_EXPLICIT, Boolean.compare(explicit, true));
vals.put(Constants.COLUMN_DURATION, duration);
vals.put(Constants.COLUMN_HAS_ARTWORK, Boolean.compare(hasArtwork, true));
long row = getWritableDatabase().insert(Constants.TABLE_NAME, null, vals);
if (AndroidUtils.isDebuggable()) Log.d("MusicCacheDb", "addTrack(): " + row);
}
public void deleteTrack(String trackId) {
getWritableDatabase().delete(Constants.TABLE_NAME, Constants.COLUMN_TRACK_ID + " = ?", new String[]{trackId});
}
public boolean isCachedTrack(String trackId) {
try (var cur = getReadableDatabase().query(
Constants.TABLE_NAME,
new String[]{Constants.COLUMN_TRACK_ID},
Constants.COLUMN_TRACK_ID + " = ?",
new String[]{trackId},
null,
null,
null,
"0,1")) {
return cur.moveToFirst();
}
}
public long getTracksCount() {
return DatabaseUtils.queryNumEntries(getReadableDatabase(), Constants.TABLE_NAME);
}
public List<MusicTrack> getAllTracks() {
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) {
return getTracksWithCursor(getReadableDatabase().query(
Constants.TABLE_NAME,
null,
Constants.COLUMN_ALBUM_ID + " = ?",
new String[]{albumId},
null,
null,
null,
limit));
}
public List<MusicTrack> getPlaylist() {
return getTracksWithCursor(getWritableDatabase().query(
Constants.TABLE_NAME,
null,
Constants.COLUMN_ALBUM_ID + " > -1",
null,
Constants.COLUMN_ALBUM_ID,
"count(*) > 1",
Constants.COLUMN_ID + " desc"));
}
private List<MusicTrack> getTracksWithCursor(Cursor cur) {
List<MusicTrack> res = new ArrayList<>();
if (cur != null && cur.moveToFirst()) {
try (cur) {
do {
MusicTrack track = fromCursor(cur);
if (track != null) res.add(track);
} while (cur.moveToNext());
}
}
return res;
}
@Retention(RetentionPolicy.SOURCE)
public @interface Constants {
int DV_VERSION = 0x2;
String DB_NAME = "vt_lite_cache.db";
String TABLE_NAME = "tracks";
//columns
String COLUMN_ID = "id";
String COLUMN_TRACK_ID = "trackId";
String COLUMN_ALBUM_ID = "albumId";
String COLUMN_TITLE = "title";
String COLUMN_SUBTITLE = "subtitle";
String COLUMN_ARTIST = "artist";
String COLUMN_ALBUM_TITLE = "albumTitle";
String COLUMN_EXPLICIT = "explicit";
String COLUMN_DURATION = "duration";
String COLUMN_HAS_ARTWORK = "hasArtwork";
//queries
String CREATE_QUERY = "create table " + TABLE_NAME + "("
+ COLUMN_ID + " INTEGER PRIMARY KEY autoincrement,"
+ COLUMN_TRACK_ID + " text not null,"
+ COLUMN_ALBUM_ID + " text not null,"
+ COLUMN_TITLE + " text not null,"
+ COLUMN_SUBTITLE + " text not null,"
+ COLUMN_ARTIST + " text not null,"
+ COLUMN_ALBUM_TITLE + " text not null,"
+ COLUMN_EXPLICIT + " int not null,"
+ COLUMN_DURATION + " int not null,"
+ COLUMN_HAS_ARTWORK + " int not null"
+ ")";
String DROP_QUERY = "drop table if exists " + TABLE_NAME;
}
}
@@ -1,13 +1,14 @@
package ru.vtosters.lite.music.cache.helpers;
import android.net.Uri;
import com.vk.dto.music.Playlist;
import com.vtosters.lite.R;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import ru.vtosters.lite.music.cache.CacheDatabaseDelegate;
import ru.vtosters.lite.music.cache.FileCacheImplementation;
import ru.vtosters.lite.music.cache.MusicCacheImpl;
import ru.vtosters.lite.utils.AndroidUtils;
import ru.vtosters.lite.utils.music.MusicCacheStorageUtils;
import java.util.ArrayList;
import java.util.List;
@@ -44,8 +45,8 @@ public class PlaylistHelper {
if (!photo.isEmpty())
jPhoto.put("height", 600)
.put("width", 600)
.putOpt("photo_300", FileCacheImplementation.getFileUri(FileCacheImplementation.getTrackThumbnail(trackId, 300)))
.putOpt("photo_600", FileCacheImplementation.getFileUri(FileCacheImplementation.getTrackThumbnail(trackId, 600)));
.putOpt("photo_300", Uri.fromFile(MusicCacheStorageUtils.getTrackThumb(trackId, 300)).toString())
.putOpt("photo_600", Uri.fromFile(MusicCacheStorageUtils.getTrackThumb(trackId, 600)).toString());
var obj = new JSONObject()
.put("id", -2)
.put("owner_id", albumId)
@@ -68,7 +69,7 @@ public class PlaylistHelper {
public static List<Playlist> getAlbumPlaylists() {
List<Playlist> list = new ArrayList<>();
for (var track : CacheDatabaseDelegate.getTracksAsPlaylist()) {
for (var track : MusicCacheImpl.getPlaylist()) {
var albumLink = track.I;
var thumb = albumLink.u1();
list.add(createAlbum(
@@ -7,8 +7,8 @@ import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import ru.vtosters.hooks.other.Preferences;
import ru.vtosters.lite.music.cache.CacheDatabaseDelegate;
import ru.vtosters.lite.music.cache.FileCacheImplementation;
import ru.vtosters.lite.music.cache.MusicCacheImpl;
import ru.vtosters.lite.utils.music.MusicCacheStorageUtils;
import java.io.File;
import java.net.MalformedURLException;
@@ -22,7 +22,7 @@ public class TracklistHelper {
}
public static List<MusicTrack> getTracks() {
List<MusicTrack> tracks = CacheDatabaseDelegate.getTracks();
List<MusicTrack> tracks = MusicCacheImpl.getAllTracks();
boolean doNotinvertOrder = Preferences.getBoolValue("invertCachedTracks", false);
@@ -35,11 +35,11 @@ public class TracklistHelper {
public static List<MusicTrack> getTracks(String id) {
return CacheDatabaseDelegate.getTracksByAlbum(id);
return MusicCacheImpl.getAlbumById(id);
}
public static List<MusicTrack> getTracksOne(String id) {
return CacheDatabaseDelegate.getTracksByAlbumOne(id);
return MusicCacheImpl.getFirstAlbumTrack(id);
}
public static JSONArray tracksToIds(List<MusicTrack> tracks) {
@@ -56,7 +56,7 @@ public class TracklistHelper {
var arr = new JSONArray();
for (MusicTrack track : tracks) {
var json = track.J();
var folder = FileCacheImplementation.getThumbnailsFolder(LibVKXClient.asId(track));
var folder = MusicCacheStorageUtils.getThumbDirById(LibVKXClient.asId(track));
try {
addCachedThumbnails(json, folder);
} catch (JSONException | MalformedURLException e) {
@@ -71,7 +71,7 @@ public class TracklistHelper {
List<MusicTrack> tracks = new ArrayList<>();
for (MusicTrack track : list) {
var json = track.J();
var folder = FileCacheImplementation.getThumbnailsFolder(LibVKXClient.asId(track));
var folder = MusicCacheStorageUtils.getThumbDirById(LibVKXClient.asId(track));
try {
addCachedThumbnails(json, folder);
} catch (JSONException | MalformedURLException e) {
@@ -5,7 +5,7 @@ import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
import com.vtosters.lite.R;
import ru.vtosters.lite.downloaders.notifications.NotificationChannels;
import ru.vtosters.lite.music.Callback;
import ru.vtosters.lite.music.interfaces.Callback;
import ru.vtosters.lite.utils.AndroidUtils;
public class MusicCallbackBuilder {
@@ -1,23 +1,17 @@
package ru.vtosters.lite.music.converter.ts;
import android.util.Log;
import com.arthenica.ffmpegkit.FFmpegKit;
import com.arthenica.ffmpegkit.ReturnCode;
import com.vk.core.util.ToastUtils;
import com.vk.dto.music.Artist;
import com.vk.dto.music.MusicTrack;
import com.vtosters.lite.R;
import ru.vtosters.lite.utils.AndroidUtils;
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.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.Normalizer;
import java.util.stream.Collectors;
public class FFMpeg {
private static final int BUFFER_SIZE = 2048;
/**
* @param in Merged .ts file via {@link ru.vtosters.lite.music.converter.ts.TSMerger}
@@ -25,79 +19,26 @@ public class FFMpeg {
* @param track Track to be converted
* @return True if success, else false
*/
public static boolean convert(File in, String out, MusicTrack track) {
public static void convert(File in, String out, MusicTrack track) {
// convert .ts file to .mp3 file via ffmpeg
var sb = new StringBuilder();
sb.append("-y -i \"").append(in.getAbsolutePath()).append("\"");
sb.append(" -map 0 -dn ");
//sb.append(" -loglevel error");
//sb.append(" -hide_banner");
sb.append(" -write_id3v2 1");
String title = null;
if (track.f != null) {
title = track.f;
if (!track.g.isEmpty()) {
title += " (" + track.g + ")";
}
}
if (title != null) {
sb.append(" -metadata title=\"").append(IOUtils.getValidFileName(title)).append("\"");
}
if (track.L != null) {
sb.append(" -metadata artist=\"").append(track.L.stream().map(Artist::w1).collect(Collectors.joining(", "))).append("\"");
} else if (track.C != null) {
sb.append(" -metadata artist=\"").append(track.C).append("\"");
}
if (track.I != null) {
sb.append(" -metadata album=\"").append(track.I.getTitle()).append("\"");
}
sb.append(" -c copy \"").append(out).append("\"");
var command = sb.toString();
var session = FFmpegKit.execute(command);
var rc = session.getReturnCode();
if (ReturnCode.isSuccess(rc)) {
return true;
}
Log.e("FFMpeg", "FFmpegKit failed with return code " + rc);
return false;
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 void copyAssets() {
try {
String[] files = AndroidUtils.getGlobalContext().getAssets().list("ffmpeg");
for (String file : files) {
if (file.endsWith(".so")) {
InputStream in = AndroidUtils.getGlobalContext().getAssets().open("ffmpeg" + File.separator + file);
FileOutputStream out = new FileOutputStream(AndroidUtils.getGlobalContext().getFilesDir() + File.separator + file);
byte[] buffer = new byte[BUFFER_SIZE];
int read;
while ((read = in.read(buffer)) != -1) {
out.write(buffer, 0, read);
}
in.close();
out.close();
}
}
} catch (IOException e) {
e.printStackTrace();
}
private static String setMetadata(MusicTrack track) {
return Preferences.getBoolValue("setMetaData", false) ? " -metadata title=\"" + IOUtils.getValidFileName(M3UDownloader.getTitle(track)) + '"'
+ " -metadata artist=\"" + normalizeMetadata(track.L.stream().map(Artist::w1).collect(Collectors.joining(", "))) + '"'
+ " -metadata album=\"" + normalizeMetadata(track.I.getTitle()) + '"' : "";
}
private static void checkFFMpegLibs() {
var lib = new File(AndroidUtils.getGlobalContext().getFilesDir(), "libffmpegkit_abidetect.so");
if (!lib.exists()) {
ToastUtils.a(R.string.ffmpegkit_copy);
copyAssets();
}
private static String normalizeMetadata(String in) {
return in.replaceAll("[\\\\/:*?\"<>|]", "");
}
}
@@ -9,66 +9,62 @@ 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
* @return True if success, else false
*/
public static boolean merge(File in, File out) {
var tsesList = in.listFiles();
public static void merge(File in, File out) {
File[] tsesList = in.listFiles();
if (tsesList == null) {
return false;
return;
}
var mergedByteArrayStream = new ByteArrayOutputStream();
ByteArrayOutputStream mergedByteArrayStream = new ByteArrayOutputStream();
sortTsesList(tsesList);
for (var file : tsesList) {
for (File file : tsesList) {
if (file.getName().endsWith(".ts")) {
try {
mergedByteArrayStream.write(IOUtils.readFully(file));
} catch (IOException e) {
e.printStackTrace();
return false;
return;
}
}
}
return writeOutputFile(out, mergedByteArrayStream);
writeOutputFile(out, mergedByteArrayStream);
}
private static void sortTsesList(File[] tsesList) {
Arrays.sort(tsesList, (ts1, ts2) -> {
String ts1Name = ts1.getName();
String ts2Name = ts2.getName();
int i1 = Integer.parseInt(ts1Name.substring(0, ts1Name.indexOf('.', 1)));
int i2 = Integer.parseInt(ts2Name.substring(0, ts2Name.indexOf('.', 1)));
return Integer.compare(i1, i2);
});
Arrays.sort(tsesList, Comparator.comparingInt(TSMerger::getFileIndex));
}
private static boolean writeOutputFile(File out, ByteArrayOutputStream mergedByteArrayStream) {
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(), mergedByteArrayStream.toByteArray());
} catch (IOException e) {
e.printStackTrace();
return false;
Files.write(out.toPath(), bytes);
} catch (IOException ignored) {
// ignored
}
} else {
try (FileOutputStream fos = new FileOutputStream(out)) {
fos.write(mergedByteArrayStream.toByteArray());
} catch (IOException e) {
e.printStackTrace();
return false;
fos.write(bytes);
} catch (IOException ignored) {
// ignored
}
}
return true;
}
}
@@ -0,0 +1,181 @@
package ru.vtosters.lite.music.downloader;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
import com.google.android.exoplayer2.source.hls.playlist.e;
import com.google.android.exoplayer2.source.hls.playlist.f;
import com.google.android.exoplayer2.source.hls.playlist.f.a;
import com.vk.dto.music.MusicTrack;
import java8.util.concurrent.CompletableFuture;
import java8.util.concurrent.CompletionException;
import ru.vtosters.lite.music.cache.MusicCacheImpl;
import ru.vtosters.lite.music.converter.ts.FFMpeg;
import ru.vtosters.lite.music.converter.ts.TSMerger;
import ru.vtosters.lite.music.interfaces.Callback;
import ru.vtosters.lite.music.interfaces.ITrackDownloader;
import ru.vtosters.lite.utils.AndroidUtils;
import ru.vtosters.lite.utils.IOUtils;
import ru.vtosters.lite.utils.music.MusicCacheStorageUtils;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
public class M3UDownloader
implements ITrackDownloader {
public static M3UDownloader getInstance() {
return Holder.INSTANCE;
}
static void parse(
File outDir,
Callback callback,
MusicTrack track,
boolean cache)
throws IOException {
var parser = new com.google.android.exoplayer2.source.hls.playlist.h(e.a(track.D));
var baseUri = track.D.substring(0, track.D.lastIndexOf("/") + 1);
var segments = ((f) parser.a(Uri.parse(baseUri), IOUtils.openStream(track.D))).o;
var progress = new AtomicInteger();
callback.onProgress(5);
var resultTs = getResultTsFile(track);
var resultMp3 = getResultMp3File(outDir, cache, track);
try {
CompletableFuture.allOf(createFutures(baseUri, segments, progress, callback, track))
.thenRun(() -> {
if (cache) {
try {
ThumbnailDownloader.downloadThumbnails(track);
} catch (IOException e) {
throw new RuntimeException("Failed to download thumbs", e);
}
}
})
.thenRun(() -> {
try {
TSMerger.merge(getTsesDir(track), resultTs);
} catch (Throwable e) {
throw new RuntimeException("Failed to merge ts files", e);
}
})
.thenRun(() -> {
try {
FFMpeg.convert(resultTs, resultMp3.getAbsolutePath(), track);
} catch (Throwable e) {
throw new RuntimeException("FFmpeg error", e);
}
})
.thenRun(() -> callback.onProgress(10 + Math.round(80.0f * progress.addAndGet(1) / segments.size())))
.thenRun(() -> {
if (cache) {
MusicCacheImpl.addTrack(track);
}
})
.whenComplete((unused, tr) -> {
if (tr != null) {
IOUtils.deleteRecursive(MusicCacheStorageUtils.getTrackDirById(track.y1()));
callback.onFailure();
} else {
callback.onSuccess();
}
IOUtils.deleteRecursive(getTsesDir(track));
})
.join();
} catch (CompletionException e) {
Log.e("M3UDownloader", "Failed to download track", e.getCause());
}
}
@SuppressWarnings("rawtypes")
static CompletableFuture[] createFutures(
String baseUri,
List<a> segments,
AtomicInteger progress,
Callback callback,
MusicTrack track) {
var futures = new CompletableFuture[segments.size()];
for (var i = 0; i < segments.size(); ++i) {
var segment = segments.get(i);
var filename = i + ".ts";
futures[i] = CompletableFuture.runAsync(() -> processSegment(baseUri, segment, filename, progress, segments.size(), callback, track));
}
return futures;
}
private static void processSegment(
String baseUri,
a segment,
String filename,
AtomicInteger progress,
int size,
Callback callback,
MusicTrack track) {
try {
var buff = IOUtils.readFully(IOUtils.openStream(baseUri + segment.a/*url*/));
if (!TextUtils.isEmpty(segment.g)) {
var cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
var cipherKey = new SecretKeySpec(IOUtils.readFully(IOUtils.openStream(segment.g)), "AES");
var cipherBytes = segment.h.getBytes();
//if IV doesn't pass, it must be created manually and filled with zeros
if (cipherBytes.length != 16) Arrays.fill((cipherBytes = new byte[16]), (byte) 0x0);
var cipherIv = new IvParameterSpec(cipherBytes);
cipher.init(Cipher.DECRYPT_MODE, cipherKey, cipherIv);
buff = cipher.doFinal(buff);
}
IOUtils.writeToFile(new File(getTsesDir(track), filename), buff);
callback.onProgress(10 + Math.round(80.0f * progress.addAndGet(1) / size));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static String getTitle(MusicTrack track) {
return track.f + (!TextUtils.isEmpty(track.g) ? + '(' + track.g + ')' : "");
}
static File getTsesDir(MusicTrack track) {
var filesDir = AndroidUtils.getGlobalContext().getFilesDir();
var tsesDir = new File(filesDir, String.valueOf(track.d));
tsesDir.mkdirs();
return tsesDir;
}
static File getResultTsFile(MusicTrack track) {
return new File(getTsesDir(track), "result.ts");
}
static File getResultMp3File(File outDir, boolean cache, MusicTrack track) {
return new File(outDir, IOUtils.getValidFileName((cache ? "track" : track.C + " - " + getTitle(track)) + ".mp3"));
}
@Override
public void downloadTrack(
MusicTrack track,
File outDir,
Callback callback,
boolean cache) {
if (track.D.isEmpty()) {
Log.d("TrackDownloader", "link error: " + track.y1() + ", title: " + M3UDownloader.getTitle(track));
callback.onFailure();
return;
}
try {
parse(outDir, callback, track, cache);
} catch (IOException e) {
callback.onFailure();
Log.e("M3UDownloader", e + "");
throw new RuntimeException(e);
}
}
// Initialization-on-demand
private static class Holder {
private static final M3UDownloader INSTANCE = new M3UDownloader();
}
}
@@ -70,7 +70,7 @@
// }
//
// @Override
// public void downloadTrack(String url, File outDir, ru.vtosters.lite.music.Callback callback) {
// public void downloadTrack(String url, File outDir, ru.vtosters.lite.music.interfaces.Callback callback) {
//
// }
//}
@@ -2,7 +2,7 @@ package ru.vtosters.lite.music.downloader;
import android.util.Log;
import com.vk.dto.music.MusicTrack;
import ru.vtosters.lite.music.Callback;
import ru.vtosters.lite.music.interfaces.Callback;
import java.io.File;
import java.util.List;
@@ -10,16 +10,12 @@ import java.util.concurrent.atomic.AtomicInteger;
public class PlaylistDownloader {
public static void downloadPlaylist(List<MusicTrack> playlist, String playlistName, String path, Callback callback) {
File outDir = new File(path);
var outDir = new File(path);
if (!outDir.exists())
if (outDir.mkdirs())
Log.v("PlaylistDownloader", "Directory created");
else
Log.e("PlaylistDownloader", "Directory creation failed");
var downloadedTracks = new AtomicInteger(0);
for (MusicTrack musicTrack : playlist) {
if (outDir.mkdirs()) Log.v("PlaylistDownloader", "Directory created");
else Log.e("PlaylistDownloader", "Directory creation failed");
var downloadedTracks = new AtomicInteger();
for (var musicTrack : playlist)
TrackDownloader.downloadTrack(musicTrack, path, new Callback() {
@Override
public void onProgress(int progress) {
@@ -27,8 +23,7 @@ public class PlaylistDownloader {
@Override
public void onSuccess() {
var currentProgress = downloadedTracks.incrementAndGet();
callback.onProgress(currentProgress);
callback.onProgress(downloadedTracks.incrementAndGet());
}
@Override
@@ -40,13 +35,11 @@ public class PlaylistDownloader {
public void onSizeReceived(long size, long header) {
}
});
}
}
public static void cachePlaylist(List<MusicTrack> playlist, Callback callback) {
var downloadedTracks = new AtomicInteger(0);
for (MusicTrack musicTrack : playlist) {
var downloadedTracks = new AtomicInteger();
for (var musicTrack : playlist)
TrackDownloader.cacheTrack(musicTrack, new Callback() {
@Override
public void onProgress(int progress) {
@@ -54,8 +47,7 @@ public class PlaylistDownloader {
@Override
public void onSuccess() {
var currentProgress = downloadedTracks.incrementAndGet();
callback.onProgress(currentProgress);
callback.onProgress(downloadedTracks.incrementAndGet());
}
@Override
@@ -67,6 +59,5 @@ public class PlaylistDownloader {
public void onSizeReceived(long size, long header) {
}
});
}
}
}
@@ -2,45 +2,42 @@ package ru.vtosters.lite.music.downloader;
import bruhcollective.itaysonlab.libvkx.client.LibVKXClient;
import com.vk.dto.music.MusicTrack;
import ru.vtosters.lite.music.cache.FileCacheImplementation;
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 {
var obj = track.J();
var trackId = LibVKXClient.asId(track);
if (!obj.has("album")) return;
var album = obj.optJSONObject("album");
if (!album.has("thumb")) return;
var thumb = album.optJSONObject("thumb");
if (thumb.has("sizes")) {
var sizes = thumb.optJSONArray("sizes");
for (int i = 0; i < sizes.length(); i++) {
var size = sizes.optJSONObject(i);
var width = size.optInt("width");
var src = size.optString("src");
downloadThumbnail(src, width, trackId);
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 {
var names = thumb.names();
for (int i = 0; i < names.length(); i++) {
var name = names.optString(i);
downloadThumbnail(thumb.optString(name), Integer.parseInt(name.substring(6)), trackId);
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 {
var data = IOUtils.readFully(new URL(url).openStream());
IOUtils.writeToFile(FileCacheImplementation.getTrackThumbnail(trackId, res), data);
private static void downloadThumbnail(String url, int res, String trackId) throws IOException {
IOUtils.writeToFile(MusicCacheStorageUtils.getTrackThumb(trackId, res), IOUtils.readFully(new URL(url).openStream()));
}
}
@@ -4,11 +4,10 @@ import android.util.Log;
import bruhcollective.itaysonlab.libvkx.client.LibVKXClient;
import com.vk.dto.music.MusicTrack;
import ru.vtosters.lite.concurrent.VTExecutors;
import ru.vtosters.lite.music.Callback;
import ru.vtosters.lite.music.M3UDownloader;
import ru.vtosters.lite.music.cache.CacheDatabaseDelegate;
import ru.vtosters.lite.music.cache.FileCacheImplementation;
import ru.vtosters.lite.music.cache.MusicCacheImpl;
import ru.vtosters.lite.music.interfaces.Callback;
import ru.vtosters.lite.music.interfaces.ITrackDownloader;
import ru.vtosters.lite.utils.music.MusicCacheStorageUtils;
import java.io.File;
@@ -24,7 +23,7 @@ public class TrackDownloader {
}
public static void cacheTrack(MusicTrack track, Callback callback) {
if (CacheDatabaseDelegate.isCached(track.y1())) {
if (MusicCacheImpl.isCachedTrack(LibVKXClient.asId(track))) {
callback.onSuccess();
return;
} else if (track.D.isEmpty()) {
@@ -33,7 +32,7 @@ public class TrackDownloader {
return;
}
var path = FileCacheImplementation.getTrackFolder(LibVKXClient.asId(track)).getAbsolutePath();
var path = MusicCacheStorageUtils.getTrackDirById(LibVKXClient.asId(track)).getAbsolutePath();
download(track, path, callback, M3UDownloader.getInstance(), true);
}
@@ -1,4 +1,4 @@
package ru.vtosters.lite.music;
package ru.vtosters.lite.music.interfaces;
public interface Callback {
void onProgress(int progress);
@@ -1,7 +1,6 @@
package ru.vtosters.lite.music.interfaces;
import com.vk.dto.music.MusicTrack;
import ru.vtosters.lite.music.Callback;
import java.io.File;
@@ -3,16 +3,19 @@ package ru.vtosters.lite.net;
import android.util.Base64;
import android.util.Log;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.io.*;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.TreeMap;
public final class NetCall {
// Constants for the request methods, content types, and encoding
private static final String GET = "GET";
private static final String POST = "POST";
private static final String FORM = "application/x-www-form-urlencoded";
private static final String UTF_8 = "UTF-8";
private final NetRequest req;
private final NetClient cl;
@@ -22,13 +25,17 @@ public final class NetCall {
}
private static String constructArgs(Map<String, String> args) throws UnsupportedEncodingException {
// Use a TreeMap to sort the arguments by key
args = new TreeMap<>(args);
String str = "";
for (Map.Entry<String, String> en : args.entrySet()) {
if (!str.isEmpty()) str += "&";
str += URLEncoder.encode(en.getKey(), "UTF-8") + "=" + URLEncoder.encode(en.getValue(), "UTF-8");
// Use a StringBuilder to append the arguments
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, String> entry : args.entrySet()) {
if (sb.length() > 0) sb.append("&");
sb.append(URLEncoder.encode(entry.getKey(), UTF_8))
.append("=")
.append(URLEncoder.encode(entry.getValue(), UTF_8));
}
return str;
return sb.toString();
}
public NetRequest request() {
@@ -38,38 +45,52 @@ public final class NetCall {
public NetResponse execute() throws IOException {
Proxy proxy = cl.getProxy();
PasswordAuthentication auth = cl.getAuthenticator();
HttpURLConnection con = (HttpURLConnection) new URL(req.url()).openConnection(proxy != null ? proxy : Proxy.NO_PROXY);
HttpURLConnection connection = (HttpURLConnection) new URL(req.url()).openConnection(proxy != null ? proxy : Proxy.NO_PROXY);
if (proxy != null && auth != null) {
// Set the proxy authentication header
String authStr = "Basic " + Base64.encodeToString((auth.getUserName() + new String(auth.getPassword())).getBytes(), Base64.NO_WRAP);
con.setRequestProperty("Proxy-Connection", "Keep-Alive");
con.setRequestProperty("Proxy-Authorization", authStr);
connection.setRequestProperty("Proxy-Connection", "Keep-Alive");
connection.setRequestProperty("Proxy-Authorization", authStr);
}
con.setConnectTimeout((int) cl.getTimeout());
con.setRequestMethod(req.getRequestMethod());
if (req.getRequestMethod().equals("POST")) {
con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
connection.setConnectTimeout((int) cl.getTimeout());
connection.setRequestMethod(req.getRequestMethod());
if (req.getRequestMethod().equals(POST)) {
// Set the content type for POST requests
connection.setRequestProperty("Content-Type", FORM);
}
Map<String, String> params = req.getRequestParams();
if (params != null) {
String strArgs = constructArgs(params);
con.setRequestProperty("Content-Length", String.valueOf(strArgs.getBytes(StandardCharsets.UTF_8).length));
con.setDoOutput(true);
con.getOutputStream().write(strArgs.getBytes(StandardCharsets.UTF_8));
con.getOutputStream().close();
// Write the request parameters to the output stream
writeParams(connection, params);
}
connection.connect();
InputStream input;
if (connection.getResponseCode() == 200) input = connection.getInputStream();
else input = connection.getErrorStream();
// Use try-with-resources to close the streams automatically
try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
byte[] buffer = new byte[1024 * 1024];
int count;
while ((count = input.read(buffer)) != -1) output.write(buffer, 0, count);
input.close();
NetResponse response = new NetResponse(output.toByteArray());
response.setCode(connection.getResponseCode());
return response;
}
}
private void writeParams(HttpURLConnection connection, Map<String, String> params) throws IOException {
// Get the encoded arguments string
String args = constructArgs(params);
// Set the content length header
connection.setRequestProperty("Content-Length", String.valueOf(args.getBytes(StandardCharsets.UTF_8).length));
// Enable output mode
connection.setDoOutput(true);
// Write the arguments to the output stream
try (OutputStream output = connection.getOutputStream()) {
output.write(args.getBytes(StandardCharsets.UTF_8));
output.flush();
}
con.connect();
InputStream in;
if (con.getResponseCode() == 200) in = con.getInputStream();
else in = con.getErrorStream();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024 * 1024];
int c;
while ((c = in.read(buffer)) != -1) bos.write(buffer, 0, c);
in.close();
bos.close();
NetResponse r = new NetResponse(bos.toByteArray());
r.setCode(con.getResponseCode());
return r;
}
public void enqueue(NetCallback cb) {
@@ -82,12 +103,16 @@ public final class NetCall {
} catch (IOException e) {
cb.onFailure(NetCall.this, e);
}
// try {
//// interrupt();
try {
interrupt();
join();
} catch (Exception e) {
Log.e("NetCall", "Failed to interrupt and join thread", e);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
// } catch (Exception e) {
// Log.e("NetCall", "Failed to interrupt and join thread", e);
// }
}
}.start();
}
@@ -8,11 +8,11 @@ public final class NetResponse {
private final byte[] data;
private int code;
NetResponse(byte[] data) {
public NetResponse(byte[] data) {
this.data = data;
}
void setCode(int code) {
public void setCode(int code) {
this.code = code;
}
@@ -51,7 +51,7 @@ public class Request {
httpURLConnection.setRequestProperty("Content-Type", mIsVTostersRequest ? "Content-Type" : "application/x-www-form-urlencoded");
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(httpURLConnection.getInputStream()));
StringBuffer stringBuffer = new StringBuffer();
StringBuilder stringBuffer = new StringBuilder();
while (true) {
String readLine = bufferedReader.readLine();
if (readLine != null) {
@@ -26,20 +26,20 @@ public class ProfileButtons {
newItem = orig.getJSONArray("buttons");
}
if (!VTVerifications.haveDonateButton() && id == AccountManagerUtils.getUserId()) {
var map = new HashMap<>();
map.put("20", "https://raw.githubusercontent.com/vtosters/lite/main/.github/images/donate_icon/20.png");
map.put("40", "https://raw.githubusercontent.com/vtosters/lite/main/.github/images/donate_icon/40.png");
map.put("80", "https://raw.githubusercontent.com/vtosters/lite/main/.github/images/donate_icon/80.png");
newItem.put(generateJsonObject(
AndroidUtils.getString(com.vtosters.lite.R.string.donate_to_vtl),
"https://vk.com/vtosters_official",
"2D81E0",
"open_url",
map
));
}
// if (!VTVerifications.haveDonateButton() && id == AccountManagerUtils.getUserId()) {
// var map = new HashMap<>();
// map.put("20", "https://raw.githubusercontent.com/vtosters/lite/main/.github/images/donate_icon/20.png");
// map.put("40", "https://raw.githubusercontent.com/vtosters/lite/main/.github/images/donate_icon/40.png");
// map.put("80", "https://raw.githubusercontent.com/vtosters/lite/main/.github/images/donate_icon/80.png");
//
// newItem.put(generateJsonObject(
// AndroidUtils.getString(com.vtosters.lite.R.string.donate_to_vtl),
// "https://vk.com/vtosters_official",
// "2D81E0",
// "open_url",
// map
// ));
// }
if (!orig.has("type") && AccountManagerUtils.isVKTester()) {
var map = new HashMap<>();
@@ -8,6 +8,7 @@ import okhttp3.Response;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import ru.vtosters.hooks.other.Preferences;
import ru.vtosters.lite.di.singleton.VtOkHttpClient;
import ru.vtosters.lite.utils.AccountManagerUtils;
import ru.vtosters.lite.utils.AndroidUtils;
@@ -17,6 +18,8 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import static ru.vtosters.hooks.other.Preferences.getBoolValue;
public class UsersList {
private static final String TAG = "UsersList";
private static final List<Integer> descriptionsList = new ArrayList<>();
@@ -25,7 +28,7 @@ public class UsersList {
public static void getUsersList() {
var prefs = AndroidUtils.getGlobalContext().getSharedPreferences("vt_another_data", 0);
if ((!NetworkUtils.isNetworkConnected() || NetworkUtils.isInternetSlow()) && prefs.contains("ids")) {
if ((!NetworkUtils.isNetworkConnected() || NetworkUtils.isInternetSlow()) && prefs.contains("ids") || Preferences.serverFeaturesDisable()) {
return;
}
@@ -7,22 +7,7 @@ import static ru.vtosters.lite.utils.AndroidUtils.getGlobalContext;
public class TGPref {
private static final SharedPreferences Prefs = PreferenceManager.getDefaultSharedPreferences(getGlobalContext());
private static final String CNCT_METHOD = "VTGS:CM";
private static final String TG_BOTKEY = "VTGS:BKey";
private static final String TG_PROXYIP = "VTGS:PIP";
private static final String TG_PROXYPORT = "VTGS:PPT";
private static final String TG_PROXYPASS = "VTGS:PPS";
private static final String TG_PROXYUSER = "VTGS:PUS";
private static final String TG_PROXYUSEPASS = "VTGS:PUsePassword";
public static int getTGConnectMethod() {
return Integer.parseInt(Prefs.getString(CNCT_METHOD, "0"));
}
public static void setTGConnectMethod(int method) {
Prefs.edit().putString(CNCT_METHOD, String.valueOf(method)).apply();
}
public static String getTGBotKey() {
return Prefs.getString(TG_BOTKEY, null);
@@ -31,40 +16,4 @@ public class TGPref {
public static void setTGBotKey(String key) {
Prefs.edit().putString(TG_BOTKEY, key).apply();
}
public static String getTGProxyIP() {
return Prefs.getString(TG_PROXYIP, null);
}
public static void setTGProxyIP(String ip) {
Prefs.edit().putString(TG_PROXYIP, ip).apply();
}
public static int getTGProxyPort() {
return Prefs.getInt(TG_PROXYPORT, 0);
}
public static void setTGProxyPort(int port) {
Prefs.edit().putInt(TG_PROXYPORT, port).apply();
}
public static String getTGProxyPass() {
return Prefs.getString(TG_PROXYPASS, "");
}
public static String getTGProxyUser() {
return Prefs.getString(TG_PROXYUSER, "");
}
public static void setTGProxyUserPass(String user, String pass) {
Prefs.edit().putString(TG_PROXYUSER, user).putString(TG_PROXYPASS, pass).apply();
}
public static boolean isTGProxyPassEnabled() {
return Prefs.getBoolean(TG_PROXYUSEPASS, false);
}
public static void setTGProxyPassEnabled(boolean en) {
Prefs.edit().putBoolean(TG_PROXYUSEPASS, en).apply();
}
}
@@ -2,11 +2,9 @@ package ru.vtosters.lite.tgs;
import com.aefyr.tsg.g2.TelegramStickersPack;
import com.vk.dto.stickers.StickerStockItem;
import com.vtosters.lite.R;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import ru.vtosters.lite.utils.AndroidUtils;
import java.util.Stack;
@@ -15,14 +13,6 @@ public class TGRoot {
public static final int X = 976820000;
public final static Stack<Integer> pendingStickers = new Stack<>();
public static String getSummary() {
return switch (TGPref.getTGConnectMethod()) {
case 0 -> AndroidUtils.getString(R.string.ctypedirect);
case 2 -> AndroidUtils.getString(R.string.ctypesocks);
default -> AndroidUtils.getString(R.string.ctypedisabled);
};
}
public static StickerStockItem toStickerPack(TelegramStickersPack p) throws JSONException {
JSONArray arr = new JSONArray();
@@ -3,7 +3,6 @@ package ru.vtosters.lite.themes;
import android.app.Application;
import android.content.Context;
import android.graphics.Color;
import android.util.Log;
import com.google.devrel.gmscore.tools.apk.arsc.BinaryResourceFile;
import com.vtosters.lite.R;
import ru.vtosters.hooks.other.Preferences;
@@ -2,11 +2,9 @@ package ru.vtosters.lite.themes.loaders;
import android.annotation.SuppressLint;
import android.content.res.AssetManager;
import android.util.Log;
import ru.vtosters.lite.utils.ReflectionUtils;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@SuppressLint("PrivateApi")
public class AssetManagerHelper {
@@ -14,7 +14,6 @@ import ru.vtosters.lite.utils.ReflectionUtils;
import java.io.File;
import java.lang.ref.WeakReference;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@@ -39,11 +39,11 @@ public class IconManager {
);
public static List<String> icons() {
return Preferences.hasVerification() && Preferences.isValidSignature() ? sIconsPlusNames : sIconsNames; // delete later sdk check
return sIconsPlusNames; // delete later sdk check
}
public static List<String> iconsValues() {
return Preferences.hasVerification() && Preferences.isValidSignature() ? sIconsPlus : sIcons; // delete later sdk check
return sIconsPlus; // delete later sdk check
}
public static void switchComponent(String icon, String appName) {
@@ -55,7 +55,7 @@ public class IconManager {
Log.d("IconManager", "iconSwitcher: icon = " + icon + ", appname = " + appName);
List<String> iconsList = (Preferences.hasVerification() && Preferences.isValidSignature()) ? sIconsPlus : sIcons;
List<String> iconsList = sIconsPlus;
List<String> labelsList = sLabels;
// check if icon is exist
@@ -1,39 +0,0 @@
package ru.vtosters.lite.ui.dialogs;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.PowerManager;
import android.provider.Settings;
import com.vk.core.dialogs.alert.VkAlertDialog;
import com.vtosters.lite.R;
import ru.vtosters.hooks.other.Preferences;
import ru.vtosters.lite.deviceinfo.OEMDetector;
public class DisableBattery {
public static void alert(Activity activity) {
if (!(OEMDetector.isOEM() && Build.VERSION.SDK_INT >= 23 && Preferences.getBoolValue("showDoze", true)))
return;
String packName = activity.getPackageName();
PowerManager manager = (PowerManager) activity.getSystemService(Context.POWER_SERVICE);
if (manager.isIgnoringBatteryOptimizations(packName)) return;
new VkAlertDialog.Builder(activity)
.setTitle(R.string.batteryissuetitle)
.setMessage(R.string.batteryissuesumm)
.setCancelable(false)
.setPositiveButton(R.string.batteryissuebtn1, (dialogInterface, i) -> {
@SuppressLint("BatteryLife")
var intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.setData(Uri.parse("package:" + packName));
activity.startActivity(intent);
})
.setNeutralButton(R.string.batteryissuebtn2,
(dialogInterface, i) -> Preferences.getPreferences().edit().putBoolean("showDoze", false).apply()
)
.show();
}
}
@@ -5,6 +5,7 @@ import android.app.Activity;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.vtosters.lite.R;
import ru.vtosters.hooks.other.ThemesUtils;
import ru.vtosters.lite.downloaders.OTADownloader;
@@ -17,22 +18,25 @@ public class OTADialog extends ModalBottomSheetWrapper<OTADialog> implements OTA
private final OTAUtils mHelper;
public OTADialog(Activity activity) {
public OTADialog(Activity activity, boolean isManualCheck) {
super(activity);
mActivity = activity;
mHelper = new OTAUtils(this);
mHelper.loadData();
mHelper.loadData(isManualCheck);
}
public static void checkUpdates(Activity activity) {
new OTADialog(activity);
new OTADialog(activity, false);
}
public static void checkUpdatesManual(Activity activity) {
new OTADialog(activity, true);
}
@Override
public void onUpdateApplied() {
public void onUpdateApplied(boolean isManualCheck) {
mActivity.runOnUiThread(() -> {
// Toast.makeText(mActivity, "Обновления найдены", Toast.LENGTH_SHORT).show();
setTitle(mActivity.getString(R.string.newversion) + " " + mHelper.getNewVersionName())
.setView(makeUpdateInfoView(mHelper.getUpdateDescription()))
.setPositiveButton(mActivity.getString(R.string.updateanddownload), () -> {
@@ -57,9 +61,18 @@ public class OTADialog extends ModalBottomSheetWrapper<OTADialog> implements OTA
}
@Override
public void onUpdateCanceled() {
// mActivity.runOnUiThread(() -> {
// // Toast.makeText(mActivity, "Обновлений не найдено", Toast.LENGTH_SHORT).show();
// });
public void onUpdateLatest(boolean isManualCheck) {
if (isManualCheck) {
mActivity.runOnUiThread(() -> {
Toast.makeText(mActivity, AndroidUtils.getString("update_latest"), Toast.LENGTH_SHORT).show();
});
}
}
@Override
public void onUpdateError() {
mActivity.runOnUiThread(() -> {
Toast.makeText(mActivity, AndroidUtils.getString("update_err"), Toast.LENGTH_SHORT).show();
});
}
}
@@ -1,108 +0,0 @@
package ru.vtosters.lite.ui.dialogs;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.util.Log;
import com.vk.core.dialogs.alert.VkAlertDialog;
import okhttp3.*;
import org.json.JSONObject;
import ru.vtosters.hooks.other.Preferences;
import ru.vtosters.lite.di.singleton.VtOkHttpClient;
import ru.vtosters.lite.utils.LifecycleUtils;
import java.io.IOException;
import java.util.Random;
import static android.content.ContentValues.TAG;
import static ru.vtosters.hooks.other.Preferences.getBoolValue;
import static ru.vtosters.hooks.other.Preferences.hasVerification;
public class ServerDialog {
private static final OkHttpClient client = VtOkHttpClient.getInstance();
public static Boolean showAlert;
private static String title;
private static String message;
private static String positiveButton;
private static String neutralButton;
private static String link;
private static String key;
private static Boolean cancelable;
private static Boolean isNotForVerified;
public static void sendRequest() {
if (getBoolValue("dialogrecomm", false)) return;
if (!getBoolValue("isRoamingState", false)) {
Request request = new Request.a()
.b("https://vtosters.app/dialog.json")
.a();
client.a(request).a(new Callback() {
@Override
public void a(Call call, IOException e) {
Log.d(TAG, e.getMessage());
showAlert = false;
}
@Override
public void a(Call call, Response response) {
try {
getResponse(response.a().g());
} catch (Exception e) {
showAlert = false;
}
}
});
}
}
private static Boolean showForNotVerified() {
int random = new Random().nextInt(6);
if (isNotForVerified) {
return !hasVerification() && random != 0;
}
return true;
}
private static void getResponse(String body) throws Exception {
JSONObject jSONObject = new JSONObject(body);
if (jSONObject.getString("title").equals("")) {
showAlert = false;
return;
}
title = jSONObject.getString("title");
message = jSONObject.getString("message");
positiveButton = jSONObject.getString("positiveButton");
neutralButton = jSONObject.getString("neutralButton");
link = jSONObject.getString("link");
key = jSONObject.getString("key");
cancelable = jSONObject.getBoolean("cancelable");
isNotForVerified = jSONObject.getBoolean("isNotForVerified");
showAlert = true;
Activity activity = LifecycleUtils.getCurrentActivity();
if (getBoolValue(key, true) && showForNotVerified() && showAlert) {
activity.runOnUiThread(() -> {
new VkAlertDialog.Builder(activity)
.setTitle(title)
.setMessage(message)
.setCancelable(cancelable)
.setPositiveButton(positiveButton,
(dialogInterface, i) -> Preferences.getPreferences().edit().putBoolean(key, false).apply())
.setNeutralButton(neutralButton, (dialogInterface, i) -> {
Preferences.getPreferences().edit().putBoolean(key, false).apply();
Intent intent = new Intent(Intent.ACTION_VIEW)
.setData(Uri.parse(link));
activity.startActivity(intent);
})
.show();
});
}
}
}
@@ -3,7 +3,9 @@ package ru.vtosters.lite.ui.fragments;
import android.os.Bundle;
import androidx.preference.Preference;
import com.vtosters.lite.R;
import ru.vtosters.hooks.other.Preferences;
import static ru.vtosters.hooks.other.Preferences.getBoolValue;
import static ru.vtosters.hooks.other.Preferences.offline;
public class ActivityFragment extends TrackedMaterialPreferenceToolbarFragment {
@@ -12,6 +14,7 @@ public class ActivityFragment extends TrackedMaterialPreferenceToolbarFragment {
super.onCreate(bundle);
addPreferencesFromResource(R.xml.preferences_activity);
findPreference("setoffline").setEnabled(offline());
findPreference("onlinefixcategory").setVisible(!Preferences.serverFeaturesDisable());
}
@Override
@@ -93,6 +93,7 @@ public class DataSettingsFragment extends TrackedMaterialPreferenceToolbarFragme
FileUtils.l();
AutoPlayCacheHolder.d.a();
MediaStorage.a();
IOUtils.deleteRecursive(AndroidUtils.getGlobalContext().getCacheDir());
updateCacheSize();
})
.setNegativeButton(requireContext().getString(com.vtosters.lite.R.string.cancel), (dialogInterface, i) -> dialogInterface.dismiss())
@@ -125,6 +126,7 @@ public class DataSettingsFragment extends TrackedMaterialPreferenceToolbarFragme
ImEngineExt.a(ImEngine1.a());
AutoPlayCacheHolder.d.a();
MediaStorage.a();
IOUtils.deleteRecursive(AndroidUtils.getGlobalContext().getCacheDir());
}
case 1 -> {
SharedPreferences prefs2 = getContext().getSharedPreferences("stickers_storage", Context.MODE_PRIVATE);
@@ -299,6 +301,20 @@ public class DataSettingsFragment extends TrackedMaterialPreferenceToolbarFragme
}
);
PreferenceFragmentUtils.addMaterialSwitchPreference(
getPreferenceScreen(),
"serverFeaturesDisable",
AndroidUtils.getString("serverFeaturesDisable"),
AndroidUtils.getString("serverFeaturesDisable_summ"),
null,
false,
(preference, o) -> {
Preferences.getPreferences().edit().putBoolean("serverFeaturesDisable", (boolean) o).apply();
LifecycleUtils.restartApplicationWithTimer();
return true;
}
);
findPreference("analyticsDisabled").setVisible(Preferences.isValidSignature());
}
@@ -92,13 +92,6 @@ public class IconsFragment extends TrackedMaterialPreferenceToolbarFragment {
this.addPreferencesFromResource(R.xml.empty);
if (!Preferences.hasVerification() && !Preferences.getBoolValue("dialogrecomm", false)) {
PreferenceFragmentUtils.addPreference(getPreferenceScreen(), "", requireContext().getString(R.string.icons_warning), requireContext().getString(R.string.icons_warning_info), R.drawable.ic_about_outline_28, preference -> {
getContext().startActivity(new Intent(Intent.ACTION_VIEW).setData(Uri.parse("https://vtosters.app/donate/")));
return false;
});
}
PreferenceFragmentUtils.addPreferenceCategory(getPreferenceScreen(), requireContext().getString(R.string.icons_title));
for (var i = 0; i < IconManager.icons().size(); i++) {
@@ -128,37 +121,5 @@ public class IconsFragment extends TrackedMaterialPreferenceToolbarFragment {
return false;
});
}
if (!Preferences.hasVerification() && !Preferences.getBoolValue("dialogrecomm", false)) {
PreferenceFragmentUtils.addPreferenceCategory(getPreferenceScreen(), requireContext().getString(R.string.unavailable_icons));
for (var i = 2; i < IconManager.sIconsPlusNames.size(); i++) {
if (IconManager.sIconsPlusNames.get(i) == null || IconManager.sIconsPlus.get(i) == null) return;
String iconname = IconManager.sIconsPlusNames.get(i);
String icon = IconManager.sIconsPlus.get(i);
int iconRes;
if (icon.equals("vt")) {
iconRes = R.mipmap.vt_launcher_round;
} else if (icon.contains("standard")) {
iconRes = R.mipmap.ic_launcher_round;
} else {
iconRes = AndroidUtils.getIdentifier("ic_launcher_" + icon, "mipmap");
}
Drawable drawable = ResourcesCompat.getDrawable(getResources(), iconRes, null);
if (drawable == null) {
drawable = ResourcesCompat.getDrawable(getResources(), R.drawable.ic_bug_outline_28, null);
}
PreferenceFragmentUtils.addPreference(getPreferenceScreen(), icon, iconname, "", drawable, preference -> {
AndroidUtils.sendToast(requireContext().getString(R.string.unavailable_icon_warning));
return false;
});
}
}
}
}
@@ -1,13 +1,35 @@
package ru.vtosters.lite.ui.fragments;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import com.vk.core.dialogs.alert.VkAlertDialog;
import com.vtosters.lite.R;
import ru.vtosters.lite.utils.AndroidUtils;
public class InstallGMSFragment extends TrackedMaterialPreferenceToolbarFragment {
@Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
addPreferencesFromResource(R.xml.preferences_gms);
findPreference("microg").setOnPreferenceClickListener((preference) -> {
String[] items = {AndroidUtils.getString("microg_dialog1"), AndroidUtils.getString("microg_dialog2"), AndroidUtils.getString("microg_dialog3")};
new VkAlertDialog.Builder(requireActivity()).setItems(items, (dialog, which) -> {
switch (which) {
case 0 ->
requireContext().startActivity(new Intent(Intent.ACTION_VIEW).setData(Uri.parse("https://github.com/TeamVanced/VancedMicroG/releases/latest/download/microg.apk")));
case 1 ->
requireContext().startActivity(new Intent(Intent.ACTION_VIEW).setData(Uri.parse("https://github.com/inotia00/mMicroG/releases/latest/download/microg.apk")));
case 2 ->
requireContext().startActivity(new Intent(Intent.ACTION_VIEW).setData(Uri.parse("https://github.com/microg/GmsCore/releases")));
}
dismiss();
}).show();
return true;
});
}
@Override
@@ -67,6 +67,13 @@ public class InterfaceFragment extends TrackedMaterialPreferenceToolbarFragment
findPreference("showmenu").setVisible(false);
}
var tablet = findPreference("disableForceTabletMode");
tablet.setVisible(AndroidUtils.isTablet());
tablet.setOnPreferenceClickListener(preference -> {
LifecycleUtils.restartApplicationWithTimer();
return true;
});
findPreference("customrounding").setOnPreferenceClickListener(preference -> {
RoundingSeekbarDialog.dialog(getContext());
return true;
@@ -24,7 +24,7 @@ import ru.vtosters.lite.concurrent.VTExecutors;
import ru.vtosters.lite.downloaders.AudioDownloader;
import ru.vtosters.lite.downloaders.VideoDownloader;
import ru.vtosters.lite.music.LastFMScrobbler;
import ru.vtosters.lite.music.cache.CacheDatabaseDelegate;
import ru.vtosters.lite.music.cache.MusicCacheImpl;
import ru.vtosters.lite.proxy.ProxyUtils;
import ru.vtosters.lite.ui.adapters.ImagineArrayAdapter;
import ru.vtosters.lite.utils.AccountManagerUtils;
@@ -108,9 +108,9 @@ public class MediaFragment extends TrackedMaterialPreferenceToolbarFragment {
findPreference("lastfm_enabled").setEnabled(false);
}
findPreference("cached_tracks").setSummary(String.format(requireContext().getString(R.string.cached_tracks_counter), CacheDatabaseDelegate.getTrackCount()));
findPreference("cached_tracks").setSummary(String.format(requireContext().getString(R.string.cached_tracks_counter), MusicCacheImpl.getTracksCount()));
findPreference("cached_tracks").setOnPreferenceClickListener(preference -> {
if (CacheDatabaseDelegate.getTrackCount() == 0) {
if (MusicCacheImpl.isEmpty()) {
AndroidUtils.sendToast(requireContext().getString(R.string.no_cache_error));
} else {
delcache(requireContext());
@@ -123,7 +123,7 @@ public class MediaFragment extends TrackedMaterialPreferenceToolbarFragment {
return true;
});
findPreference("invertCachedTracks").setVisible(CacheDatabaseDelegate.hasTracks());
findPreference("invertCachedTracks").setVisible(!MusicCacheImpl.isEmpty());
if (!LibVKXClient.isVkxInstalled()) {
findPreference("vkx_sett").setVisible(false);
@@ -217,7 +217,7 @@ public class MediaFragment extends TrackedMaterialPreferenceToolbarFragment {
.setTitle(R.string.warning)
.setMessage(R.string.cached_tracks_remove_confirm)
.setPositiveButton(R.string.yes,
(dialog, which) -> executor.submit(CacheDatabaseDelegate::clear))
(dialog, which) -> executor.submit(MusicCacheImpl::clear))
.setNeutralButton(R.string.no,
(dialog, which) -> dialog.cancel())
.show();
@@ -16,6 +16,8 @@ import ru.vtosters.lite.ssfs.UsersList;
import ru.vtosters.lite.ui.activities.VKAdminTokenActivity;
import ru.vtosters.lite.utils.*;
import static ru.vtosters.hooks.other.Preferences.getBoolValue;
public class OtherFragment extends TrackedMaterialPreferenceToolbarFragment {
private static final int VK_ADMIN_TOKEN_REQUEST_CODE = 1;
@@ -65,7 +67,7 @@ public class OtherFragment extends TrackedMaterialPreferenceToolbarFragment {
findPreference("microgsettings").setOnPreferenceClickListener(preference -> {
try {
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.setComponent(new ComponentName("com.mgoogle.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);
} catch (Exception e) {
e.printStackTrace();
@@ -103,13 +105,6 @@ public class OtherFragment extends TrackedMaterialPreferenceToolbarFragment {
return true;
});
findPreference("datasettings").setOnPreferenceClickListener(preference -> {
NavigatorUtils.switchFragment(requireContext(), DataSettingsFragment.class);
return true;
});
findPreference("dialogrecomm").setVisible(!Preferences.hasVerification());
findPreference("updateverifdata").setOnPreferenceClickListener(preference -> {
UsersList.getUsersList();
VTVerifications.load(requireContext());
@@ -117,6 +112,10 @@ public class OtherFragment extends TrackedMaterialPreferenceToolbarFragment {
return true;
});
findPreference("VT_Verification").setVisible(!Preferences.serverFeaturesDisable());
findPreference("VT_Fire").setVisible(!Preferences.serverFeaturesDisable());
findPreference("updateverifdata").setVisible(!Preferences.serverFeaturesDisable());
var vkAdminTokenPref = findPreference("vk_admin_token");
vkAdminTokenPref.setVisible(Preferences.getPreferences().getBoolean("new_music_downloading_way", false));
if (vkAdminTokenPref.isVisible()) {
@@ -6,6 +6,7 @@ import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import com.vtosters.lite.R;
import ru.vtosters.hooks.GmsHook;
import ru.vtosters.hooks.other.Preferences;
import ru.vtosters.lite.deviceinfo.OEMDetector;
import ru.vtosters.lite.ui.PreferenceFragmentUtils;
@@ -46,6 +47,11 @@ public class SystemInfo extends TrackedMaterialPreferenceToolbarFragment {
boolean isEMUI = OEMDetector.isEMUI();
boolean hasMiuiIncrCode = OEMDetector.hasMiuiIncrCode();
boolean hasGMS = GmsHook.isGmsInstalled();
boolean hasVancedMicroG = GmsHook.isFakeGmsInstalled();
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 isTablet = AndroidUtils.isTablet();
boolean isDebuggable = AndroidUtils.isDebuggable();
@@ -76,6 +82,12 @@ public class SystemInfo extends TrackedMaterialPreferenceToolbarFragment {
return false;
});
PreferenceFragmentUtils.addPreference(getPreferenceScreen(), "", "Google Market Services package", GMSPackageName, 0, preference -> {
((ClipboardManager) requireActivity().getSystemService(Context.CLIPBOARD_SERVICE)).setPrimaryClip(ClipData.newPlainText("MBH-ST", GMSPackageName));
AndroidUtils.sendToast(requireContext().getString(R.string.copied_to_clipboard));
return false;
});
PreferenceFragmentUtils.addPreference(getPreferenceScreen(), "", "Valid Signature", "Value: " + isValidSignature, 0, null);
PreferenceFragmentUtils.addPreference(getPreferenceScreen(), "", "isTablet", "Value: " + isTablet, 0, null);
@@ -90,6 +102,12 @@ public class SystemInfo extends TrackedMaterialPreferenceToolbarFragment {
return false;
});
PreferenceFragmentUtils.addPreference(getPreferenceScreen(), "", "isInstalledGoogleGMS", "Value: " + hasGMS, 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 -> {
((ClipboardManager) requireActivity().getSystemService(Context.CLIPBOARD_SERVICE)).setPrimaryClip(ClipData.newPlainText("MBH-ST", productName));
AndroidUtils.sendToast(requireContext().getString(R.string.copied_to_clipboard));
@@ -13,7 +13,6 @@ import com.vtosters.lite.R;
import ru.vtosters.hooks.other.Preferences;
import ru.vtosters.hooks.other.ThemesUtils;
import ru.vtosters.lite.concurrent.VTExecutors;
import ru.vtosters.lite.themes.ThemesCore;
import ru.vtosters.lite.themes.ThemesManager;
import ru.vtosters.lite.themes.palettes.PalettesManager;
import ru.vtosters.lite.ui.components.DockBarEditorManager;
@@ -1,5 +1,6 @@
package ru.vtosters.lite.ui.fragments;
import android.annotation.SuppressLint;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -8,11 +9,16 @@ import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.PowerManager;
import android.provider.Settings;
import androidx.annotation.StringRes;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.DialogFragment;
import androidx.preference.Preference;
import com.aefyr.tsg.g2.TelegramStickersService;
import com.vk.about.AboutAppFragment;
import com.vk.balance.BalanceFragment;
import com.vk.core.dialogs.alert.VkAlertDialog;
import com.vk.identity.fragments.IdentityListFragment;
import com.vk.medianative.MediaImageEncoder;
import com.vk.notifications.settings.NotificationsSettingsFragment;
@@ -37,6 +43,7 @@ import ru.vtosters.lite.BuildConfig;
import ru.vtosters.lite.concurrent.VTExecutors;
import ru.vtosters.lite.deviceinfo.OEMDetector;
import ru.vtosters.lite.ssfs.Utils;
import ru.vtosters.lite.themes.utils.RecolorUtils;
import ru.vtosters.lite.ui.PreferenceFragmentUtils;
import ru.vtosters.lite.ui.dialogs.OTADialog;
import ru.vtosters.lite.ui.fragments.tgstickers.StickersFragment;
@@ -45,16 +52,14 @@ import ru.vtosters.lite.utils.*;
import java.util.Locale;
public class VTSettings extends TrackedMaterialPreferenceToolbarFragment {
public static String ACTION_INVALIDATE_TGS_COUNT = "com.vtosters.lite.intent.action.INVALIDATE_TGS_COUNT";
final static public String ACTION_INVALIDATE_TGS_COUNT="com.vtosters.lite.intent.action.INVALIDATE_TGS_COUNT";
final BroadcastReceiver mTgsReceiver=new BroadcastReceiver()
{
BroadcastReceiver mTgsReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent)
{
if(ACTION_INVALIDATE_TGS_COUNT.equals(intent.getAction()))
public void onReceive(Context context, Intent intent) {
if (ACTION_INVALIDATE_TGS_COUNT.equals(intent.getAction())) {
findPreference("tgs_stickers").setSummary(getTGSsumm());
}
}
};
@@ -90,16 +95,21 @@ public class VTSettings extends TrackedMaterialPreferenceToolbarFragment {
ThemesUtils.setTheme(isDarkTheme ? ThemesUtils.getDarkTheme() : ThemesUtils.getLightTheme(), requireActivity(), true);
}
@SuppressLint("UnspecifiedRegisterReceiverFlag")
@SuppressWarnings("ConstantConditions")
@Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
requireContext().registerReceiver(mTgsReceiver,new IntentFilter(ACTION_INVALIDATE_TGS_COUNT));
requireContext().registerReceiver(mTgsReceiver, new IntentFilter(ACTION_INVALIDATE_TGS_COUNT));
this.addPreferencesFromResource(R.xml.empty);
AndroidUtils.checkLinksVerified(this.requireActivity());
PowerManager manager = (PowerManager) requireActivity().getSystemService(Context.POWER_SERVICE);
boolean isLinksUnverified = AndroidUtils.isLinksUnverified(requireActivity());
boolean isDozingAvailable = Build.VERSION.SDK_INT >= 23 && !manager.isIgnoringBatteryOptimizations(AndroidUtils.getPackageName());
boolean areNotificationsDisabled = !AndroidUtils.areNotificationsEnabled() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
boolean isGMSNotInstalled = !GmsHook.isAnyServicesInstalled();
if (AccountManagerUtils.isLogin()) {
Preference accountSwitcher = PreferenceFragmentUtils.addPreference(
@@ -112,6 +122,7 @@ public class VTSettings extends TrackedMaterialPreferenceToolbarFragment {
try {
VKAuth.a("logout", false);
} catch (Exception ignored) {
// ignored
}
Intent intent = new Intent(requireContext(), MainActivity.class)
@@ -131,18 +142,20 @@ public class VTSettings extends TrackedMaterialPreferenceToolbarFragment {
});
});
PreferenceFragmentUtils.addPreference(
getPreferenceScreen(),
"",
requireContext().getString(R.string.vtssfs),
getSSFSsumm(),
R.drawable.ic_link_circle_outline_28,
preference -> {
VKUIwrapper.setLink(Utils.getVKUILink());
NavigatorUtils.switchFragment(requireContext(), VKUIwrapper.class);
return false;
}
);
if (Preferences.hasSpecialVerif()) {
PreferenceFragmentUtils.addPreference(
getPreferenceScreen(),
"",
requireContext().getString(R.string.vtssfs),
"",
R.drawable.ic_link_circle_outline_28,
preference -> {
VKUIwrapper.setLink(Utils.getVKUILink());
NavigatorUtils.switchFragment(requireContext(), VKUIwrapper.class);
return false;
}
);
}
}
PreferenceFragmentUtils.addPreferenceCategory(getPreferenceScreen(), R.string.appearance_theme_use_system);
@@ -159,8 +172,8 @@ public class VTSettings extends TrackedMaterialPreferenceToolbarFragment {
AndroidUtils.sendToast(AndroidUtils.getString("systemtheme_enabled"));
return false;
}
final MaterialSwitchPreference switchPreference = (MaterialSwitchPreference) preference;
final boolean isDarkTheme = !switchPreference.isChecked();
MaterialSwitchPreference switchPreference = (MaterialSwitchPreference) preference;
boolean isDarkTheme = !switchPreference.isChecked();
switchTheme(isDarkTheme);
return true;
}
@@ -175,27 +188,89 @@ public class VTSettings extends TrackedMaterialPreferenceToolbarFragment {
R.drawable.ic_palette_outline_28,
Preferences.systemtheme(),
(preference, o) -> {
Preferences.getPreferences().edit().putBoolean("system_theme", (boolean) o).commit();
Preferences.getPreferences().edit().putBoolean("system_theme", (boolean) o).apply();
SystemThemeChangerHook.onThemeChanged(requireActivity().getResources().getConfiguration());
return true;
}
);
}
if (!GmsHook.isAnyServicesInstalled()) {
PreferenceFragmentUtils.addPreferenceCategory(getPreferenceScreen(), R.string.gmsname);
if ((isLinksUnverified || isDozingAvailable || areNotificationsDisabled || isGMSNotInstalled) && !Preferences.getBoolValue("dialogrecomm", false)) {
PreferenceFragmentUtils.addPreferenceCategory(getPreferenceScreen(), AndroidUtils.getString("sett_recommendations"));
PreferenceFragmentUtils.addPreference(
getPreferenceScreen(),
"",
requireContext().getString(R.string.installgms),
"",
R.drawable.ic_about_outline_28,
preference -> {
NavigatorUtils.switchFragment(requireContext(), InstallGMSFragment.class);
return false;
}
);
if (isGMSNotInstalled) {
PreferenceFragmentUtils.addPreference(
getPreferenceScreen(),
"",
requireContext().getString(R.string.installgms),
AndroidUtils.getString("sett_nogms_summ"),
RecolorUtils.recolorDrawable(R.drawable.ic_logo_google_28, ThemesUtils.getColor(R.color.red)),
preference -> {
NavigatorUtils.switchFragment(requireContext(), InstallGMSFragment.class);
return false;
}
);
}
if (isLinksUnverified) {
PreferenceFragmentUtils.addPreference(
getPreferenceScreen(),
"",
AndroidUtils.getString("sett_missing_links"),
AndroidUtils.getString("sett_missing_links_summ"),
RecolorUtils.recolorDrawable(R.drawable.ic_linked_outline_28, ThemesUtils.getColor(R.color.red)),
preference -> {
try {
Intent intent = new Intent(Settings.ACTION_APP_OPEN_BY_DEFAULT_SETTINGS, Uri.parse("package:" + AndroidUtils.getPackageName()));
requireActivity().startActivity(intent);
} catch (Throwable t1) {
try {
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.parse("package:" + AndroidUtils.getPackageName()));
requireActivity().startActivity(intent);
} catch (Throwable ignored) {
// ignored
}
}
return false;
}
);
}
if (isDozingAvailable) {
PreferenceFragmentUtils.addPreference(
getPreferenceScreen(),
"",
AndroidUtils.getString("sett_battery"),
AndroidUtils.getString("sett_battery_summ1") + (OEMDetector.isMIUI() ? AndroidUtils.getString("sett_battery_summ2") : ""),
RecolorUtils.recolorDrawable(R.drawable.ic_filter_outline_28, ThemesUtils.getColor(R.color.red)),
preference -> {
@SuppressLint("BatteryLife")
var intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.setData(Uri.parse("package:" + AndroidUtils.getPackageName()));
requireActivity().startActivity(intent);
return false;
}
);
}
if (areNotificationsDisabled) {
PreferenceFragmentUtils.addPreference(
getPreferenceScreen(),
"",
AndroidUtils.getString("sett_notifs"),
AndroidUtils.getString("sett_notifs_summ"),
RecolorUtils.recolorDrawable(R.drawable.ic_notifications_outline_28, ThemesUtils.getColor(R.color.red)),
preference -> {
@SuppressLint("BatteryLife")
var intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.putExtra(Settings.EXTRA_APP_PACKAGE, AndroidUtils.getPackageName());
requireActivity().startActivity(intent);
return false;
}
);
}
}
if (Preferences.devmenu()) {
@@ -440,7 +515,7 @@ public class VTSettings extends TrackedMaterialPreferenceToolbarFragment {
getPreferenceScreen(),
"",
requireContext().getString(R.string.vtlinterface),
getValAsString(R.string.showstories, Preferences.stories()),
getValAsString(R.string.show_superapps_title, Preferences.superapp()),
R.drawable.ic_interface_outline_28,
preference -> {
NavigatorUtils.switchFragment(requireContext(), InterfaceFragment.class);
@@ -448,6 +523,18 @@ public class VTSettings extends TrackedMaterialPreferenceToolbarFragment {
}
);
PreferenceFragmentUtils.addPreference(
getPreferenceScreen(),
"currcache",
AndroidUtils.getString(R.string.datasettings_title),
getString(com.vtosters.lite.R.string.cache_size_summ) + ": " + CacheUtils.humanReadableByteCountBin(IOUtils.getDirSize(AndroidUtils.getGlobalContext().getCacheDir())),
R.drawable.ic_document_outline_28,
preference -> {
NavigatorUtils.switchFragment(requireContext(), DataSettingsFragment.class);
return false;
}
);
PreferenceFragmentUtils.addPreference(
getPreferenceScreen(),
"",
@@ -480,14 +567,7 @@ public class VTSettings extends TrackedMaterialPreferenceToolbarFragment {
AndroidUtils.getString("currentLanguage") + " " + AndroidUtils.upString(Locale.getDefault().getDisplayLanguage()),
R.drawable.ic_globe_outline_28,
preference -> {
try {
Intent intent = new Intent("android.settings.APP_LOCALE_SETTINGS", Uri.parse("package:" + AndroidUtils.getPackageName()));
startActivity(intent);
} catch (Exception e) {
e.printStackTrace();
AndroidUtils.sendToast("Ваша система не поддерживает смену языка приложения");
}
startActivity(new Intent("android.settings.APP_LOCALE_SETTINGS", Uri.parse("package:" + AndroidUtils.getPackageName())));
return false;
}
);
@@ -519,18 +599,57 @@ public class VTSettings extends TrackedMaterialPreferenceToolbarFragment {
}
);
if (AndroidUtils.isAdbOrDeveloperOptionsEnabled(requireContext().getContentResolver())) {
PreferenceFragmentUtils.addPreference(
getPreferenceScreen(),
"",
requireContext().getString(R.string.opencommit),
"",
R.drawable.ic_link_outline_28,
preference -> {
requireContext().startActivity(new Intent(Intent.ACTION_VIEW).setData(Uri.parse(AboutHook.getCommitLink())));
return false;
}
);
}
PreferenceFragmentUtils.addPreference(
getPreferenceScreen(),
"",
requireContext().getString(R.string.opencommit),
"",
R.drawable.ic_link_outline_28,
AndroidUtils.getString("sett_chats"),
AndroidUtils.getString("sett_chats_summ"),
R.drawable.ic_message_outline_28,
preference -> {
requireContext().startActivity(new Intent(Intent.ACTION_VIEW).setData(Uri.parse(AboutHook.getCommitLink())));
var args = new Bundle();
var dialog = new AboutInformation();
args.putString("vk_link", "https://vk.me/join/jKE1bIZczHTF2mLkuoiQcd0qIBYgHuGFPRA=");
args.putString("tg_link", "https://t.me/vtosterschat");
dialog.setArguments(args);
dialog.show(requireFragmentManager(), "");
return false;
}
);
PreferenceFragmentUtils.addPreference(
getPreferenceScreen(),
"",
AndroidUtils.getString("sett_community"),
AndroidUtils.getString("sett_community_summ"),
R.drawable.users_3_outline_28,
preference -> {
var args = new Bundle();
var dialog = new AboutInformation();
args.putString("vk_link", "https://vk.com/vtosters_official");
args.putString("tg_link", "https://t.me/vtosters");
dialog.setArguments(args);
dialog.show(requireFragmentManager(), "");
return false;
}
);
PreferenceFragmentUtils.addPreference(
getPreferenceScreen(),
@@ -566,22 +685,65 @@ public class VTSettings extends TrackedMaterialPreferenceToolbarFragment {
"",
R.drawable.ic_download_outline_28,
preference -> {
OTADialog.checkUpdates(getActivity());
OTADialog.checkUpdatesManual(getActivity());
return false;
}
);
PreferenceFragmentUtils.addMaterialSwitchPreference(
getPreferenceScreen(),
"autoupdates",
AndroidUtils.getString("sett_autoupdates"),
AndroidUtils.getString("sett_autoupdates_summ"),
R.drawable.ic_history_outline_28,
true,
(preference, o) -> {
Preferences.getPreferences().edit().putBoolean("autoupdates", (boolean) o).apply();
return true;
}
);
}
}
@Override
public void onDestroy()
{
public void onDestroy() {
requireContext().unregisterReceiver(mTgsReceiver);
super.onDestroy();
}
@Override
public void onResume() {
super.onResume();
updateCacheSize();
}
private void updateCacheSize() {
findPreference("currcache").setSummary(getString(com.vtosters.lite.R.string.cache_size_summ) + ": " + CacheUtils.humanReadableByteCountBin(IOUtils.getDirSize(AndroidUtils.getGlobalContext().getCacheDir())));
}
@Override
public int T4() {
return R.string.notification_settings;
}
public static class AboutInformation extends DialogFragment {
@Override
public AlertDialog onCreateDialog(Bundle savedInstanceState) {
VkAlertDialog.Builder builder = new VkAlertDialog.Builder(requireActivity());
String[] items = {AndroidUtils.getString(R.string.app_name_alter), "Telegram"};
builder.setItems(items, (dialog, which) -> {
switch (which) {
case 0 ->
requireContext().startActivity(new Intent(Intent.ACTION_VIEW).setData(Uri.parse(getArguments().getString("vk_link"))));
case 1 ->
requireContext().startActivity(new Intent(Intent.ACTION_VIEW).setData(Uri.parse(getArguments().getString("tg_link"))));
}
dismiss();
});
return builder.create();
}
}
}
@@ -28,92 +28,91 @@ import ru.vtosters.lite.ui.adapters.StickerPackAdapter;
import ru.vtosters.lite.ui.components.StickerTouchHelperCallback;
import ru.vtosters.lite.ui.fragments.BaseToolbarFragment;
import ru.vtosters.lite.utils.AndroidUtils;
import ru.vtosters.lite.utils.NavigatorUtils;
import java.io.File;
public class StickersFragment extends BaseToolbarFragment {
public final static String ACTION_RELOAD = "com.vtosters.lite.action.RELOAD_TGS_LIST";
final static int TYPE_DIRECT = 0;
final static int TYPE_SOCKS = 2;
public static String ACTION_RELOAD = "com.vtosters.lite.action.RELOAD_TGS_LIST";
TelegramStickersGrabber mGrabber;
StickerPackAdapter mAdapter;
boolean movePending = false;
int from,to;
final BroadcastReceiver mReceiver=new BroadcastReceiver() {
int from, to;
BroadcastReceiver mReceiver = new BroadcastReceiver() {
@SuppressLint("NotifyDataSetChanged")
@Override
public void onReceive(Context context, Intent intent) {
if (!intent.getAction().equals(ACTION_RELOAD)||mAdapter==null)return;
try
{
if(movePending)
{
movePending=false;
if (!intent.getAction().equals(ACTION_RELOAD) || mAdapter == null) return;
try {
if (movePending) {
movePending = false;
mAdapter.notifyItemMoved(from, to);
from=to=-1;
}
else mAdapter.notifyDataSetChanged();
from = to = -1;
} else mAdapter.notifyDataSetChanged();
} catch (Exception e) {
e.printStackTrace();
}
catch(Exception e)
{ e.printStackTrace(); }
}
};
private static void resetToken(Context ctx) {
if (TGPref.getTGBotKey() == null) AndroidUtils.sendToast(AndroidUtils.getString("tgs_token_missing"));
new VkAlertDialog.Builder(ctx)
.setTitle(AndroidUtils.getString("tgs_token_reset_dialog"))
.setNegativeButton(R.string.cancel, (dialog, which) -> {
dialog.cancel();
})
.setPositiveButton(R.string.ok, (dialog, which) -> {
TGPref.setTGBotKey(null);
AndroidUtils.sendToast(AndroidUtils.getString("tgs_token_reset"));
})
.show();
}
@Override
protected void onCreateMenu(Menu menu)
{
protected void onCreateMenu(Menu menu) {
menu.add(0, 0, 0, "")
.setIcon(ThemesUtils.recolorDrawable(requireContext().getDrawable(R.drawable.ic_add_outline_28)))
.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
menu.add(0,1,0,"")
.setIcon(R.drawable.ic_settings_outline_28)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
.setIcon(ThemesUtils.recolorDrawable(requireContext().getDrawable(R.drawable.ic_add_outline_28)))
.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
menu.add(0, 1, 0, "")
.setIcon(ThemesUtils.recolorDrawable(requireContext().getDrawable(R.drawable.ic_refresh_outline_28)))
.setTitle(AndroidUtils.getString("tgs_token_reset_menu"))
.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
super.onCreateMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
switch (item.getItemId())
{
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case 0 -> addTgsPack();
case 1 -> openMenu(null);
case 1 -> resetToken(requireContext());
}
return true;
}
private void openMenu(String toast) {
if(toast!=null)Toast.makeText(requireContext(),toast,Toast.LENGTH_SHORT).show();
NavigatorUtils.switchFragment(requireContext(), StickersPreferencesFragment.class);
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requireContext().registerReceiver(mReceiver, new IntentFilter(ACTION_RELOAD));
mGrabber=new TelegramStickersGrabber(TGPref.getTGBotKey());
mGrabber = new TelegramStickersGrabber(TGPref.getTGBotKey());
}
@Override
public View onCreateContent(@NonNull LayoutInflater inflater, @Nullable Bundle bundle) {
setTitle(R.string.vtltgs);
final var container=new FrameLayout(requireContext());
final var recycler=new RecyclerView(requireContext());
var container = new FrameLayout(requireContext());
var recycler = new RecyclerView(requireContext());
mAdapter=new StickerPackAdapter();
mAdapter = new StickerPackAdapter();
recycler.setAdapter(mAdapter);
final var callback=new StickerTouchHelperCallback(mAdapter);
final var touchHelper=new ItemTouchHelper(callback);
var callback = new StickerTouchHelperCallback(mAdapter);
var touchHelper = new ItemTouchHelper(callback);
touchHelper.attachToRecyclerView(recycler);
recycler.setLayoutManager(new StaggeredGridLayoutManager(2,StaggeredGridLayoutManager.VERTICAL));
container.addView(recycler,new ViewGroup.LayoutParams(-1,-1));
recycler.setLayoutManager(new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL));
container.addView(recycler, new ViewGroup.LayoutParams(-1, -1));
return container;
}
@@ -129,122 +128,81 @@ public class StickersFragment extends BaseToolbarFragment {
requireContext().unregisterReceiver(mReceiver);
}
private void addTgsPack()
{
final var method=TGPref.getTGConnectMethod();
if(method!=-1)
{
initGrabber();
switch(method)
{
case TYPE_SOCKS ->
{
if (TGPref.getTGProxyIP()==null)openMenu(requireContext().getString(R.string.stickersproxy1));
}
case TYPE_DIRECT ->
{
initGrabber();
final Runnable lambda = () ->
private void addTgsPack() {
initGrabber();
Runnable lambda = () -> {
var container = new LinearLayout(requireContext());
var input = new EditText(requireContext());
var inputLP = new ViewGroup.MarginLayoutParams(-1, -2);
inputLP.setMargins(
AndroidUtils.dp2px(24.f),
0,
AndroidUtils.dp2px(24.f),
0);
container.addView(input, inputLP);
new VkAlertDialog.Builder(requireContext())
.setTitle(R.string.stickershelp1)
// .setMessage(R.string.stickershelp2)
.setView(container)
.setPositiveButton(android.R.string.ok, (dialog, which) ->
{
final var container=new LinearLayout(requireContext());
var packId = input.getText().toString();
if (packId.startsWith("https://"))
packId = packId.substring(packId.lastIndexOf('/') + 1);
TelegramStickersService.getInstance(requireContext())
.requestPackDownload(
packId,
new File(requireContext().getFilesDir(), "VT-Stickers/" + packId));
})
.setNeutralButton(android.R.string.cancel, (dialog, which) -> dialog.dismiss())
.show();
};
final var input=new EditText(requireContext());
final var inputLP = new ViewGroup.MarginLayoutParams(-1,-2);
inputLP.setMargins(
AndroidUtils.dp2px(24.f),
0,
AndroidUtils.dp2px(24.f),
0);
container.addView(input,inputLP);
new VkAlertDialog.Builder(requireContext())
.setTitle(R.string.stickershelp1)
// .setMessage(R.string.stickershelp2)
.setView(container)
.setPositiveButton(android.R.string.ok, (dialog, which) ->
{
var packId=input.getText().toString();
if (packId.startsWith("https://"))
packId=packId.substring(packId.lastIndexOf('/')+1);
TelegramStickersService.getInstance(requireContext())
.requestPackDownload(
packId,
new File(requireContext().getFilesDir(),"VT-Stickers/"+packId));
})
.setNeutralButton(android.R.string.cancel,(dialog, which) -> dialog.dismiss())
.show();
};
if(TGPref.getTGBotKey()==null)enterBotKey(() -> checkApiKey(lambda));
else checkApiKey(lambda);
}
}
}
else openMenu(requireContext().getString(R.string.stickersconnection));
if (TGPref.getTGBotKey() == null) enterBotKey(() -> checkApiKey(lambda));
else checkApiKey(lambda);
}
private void initGrabber() {
switch(TGPref.getTGConnectMethod())
{
case TYPE_DIRECT ->
{
TelegramStickersGrabber.USE_PROXY=false;
mGrabber.resetProxy();
}
case TYPE_SOCKS ->
{
if(TGPref.getTGProxyIP()==null)break;
TelegramStickersGrabber.USE_PROXY=true;
TelegramStickersGrabber.PROXY_IP=TGPref.getTGProxyIP();
TelegramStickersGrabber.PROXY_PORT=TGPref.getTGProxyPort();
if (TGPref.isTGProxyPassEnabled())
{
TelegramStickersGrabber.HAS_PASS=true;
TelegramStickersGrabber.PROXY_USER=TGPref.getTGProxyUser();
TelegramStickersGrabber.PROXY_PASS=TGPref.getTGProxyPass();
}
mGrabber.enableProxy();
}
}
TelegramStickersGrabber.updateURLs();
}
private void enterBotKey(Runnable callback)
{
final var container=new LinearLayout(requireContext());
private void enterBotKey(Runnable callback) {
var container = new LinearLayout(requireContext());
final var input=new EditText(requireContext());
final var inputLP=new ViewGroup.MarginLayoutParams(-1,-2);
var input = new EditText(requireContext());
var inputLP = new ViewGroup.MarginLayoutParams(-1, -2);
inputLP.setMargins(
AndroidUtils.dp2px(24.f),
0,
AndroidUtils.dp2px(24.f),
0);
container.addView(input,inputLP);
container.addView(input, inputLP);
new VkAlertDialog.Builder(requireContext())
.setTitle(requireContext().getString(R.string.stickersapi5))
.setMessage(requireContext().getString(R.string.stickersapi6))
.setView(container)
.setPositiveButton(android.R.string.ok,(dialog, which) ->
.setPositiveButton(android.R.string.ok, (dialog, which) ->
{
TGPref.setTGBotKey(input.getText().toString());
if(callback!=null)callback.run();
if (callback != null) callback.run();
})
.setNegativeButton(android.R.string.cancel,(dialog, which) -> dialog.dismiss())
.setNegativeButton(android.R.string.cancel, (dialog, which) -> dialog.dismiss())
.setNeutralButton(requireContext().getString(R.string.stickersapi7),
(dialog, which) ->
new VkAlertDialog.Builder(requireContext())
.setTitle(requireContext().getString(R.string.stickersapi8))
.setMessage(requireContext().getString(R.string.stickersapi9) +
requireContext().getString(R.string.stickersapi10))
.setNeutralButton(AndroidUtils.getString("open_bot"),(dl, i) -> {
final var intent=new Intent(Intent.ACTION_VIEW,Uri.parse("https://t.me/botfather"))
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
requireActivity().startActivity(intent);
})
.setPositiveButton(android.R.string.ok, null)
.show()
.setTitle(requireContext().getString(R.string.stickersapi8))
.setMessage(requireContext().getString(R.string.stickersapi9) +
requireContext().getString(R.string.stickersapi10))
.setNeutralButton(AndroidUtils.getString("open_bot"), (dl, i) -> {
var intent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://t.me/botfather"))
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
requireActivity().startActivity(intent);
})
.setPositiveButton(android.R.string.ok, null)
.show()
)
.show();
}
@@ -252,37 +210,33 @@ public class StickersFragment extends BaseToolbarFragment {
private void checkApiKey(Runnable callback) {
mGrabber.setBotApiKey(TGPref.getTGBotKey());
final var dialog=new ProgressDialog(requireContext());
var dialog = new ProgressDialog(requireContext());
dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
dialog.setMessage(requireContext().getString(R.string.stickersapi1));
dialog.show();
mGrabber.checkKey(new TelegramStickersGrabber.KeyCheckListener()
{
mGrabber.checkKey(new TelegramStickersGrabber.KeyCheckListener() {
@Override
public void onKeyChecked(boolean ok)
{
public void onKeyChecked(boolean ok) {
dialog.dismiss();
if (!ok)
{
Toast.makeText(requireContext(), requireContext().getString(R.string.stickersapi2),Toast.LENGTH_SHORT).show();
if (!ok) {
Toast.makeText(requireContext(), requireContext().getString(R.string.stickersapi2), Toast.LENGTH_SHORT).show();
TGPref.setTGBotKey(null);
return;
}
TelegramStickersService.getInstance(requireContext())
.setBotKey(TGPref.getTGBotKey());
if (callback!=null)callback.run();
if (callback != null) callback.run();
}
@Override
public void onNetError()
{
public void onNetError() {
dialog.dismiss();
new VkAlertDialog.Builder(requireContext())
.setMessage(requireContext().getString(R.string.stickersapi3))
.setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(requireContext().getString(R.string.stickersapi4),(di,which) -> checkApiKey(callback))
.setPositiveButton(requireContext().getString(R.string.stickersapi4), (di, which) -> checkApiKey(callback))
.show();
}
});
@@ -1,87 +0,0 @@
package ru.vtosters.lite.ui.fragments.tgstickers;
import android.os.Bundle;
import com.aefyr.tsg.g2.stickersgrabber.TelegramStickersGrabber;
import com.vk.core.util.ToastUtils;
import com.vtosters.lite.R;
import ru.vtosters.lite.tgs.TGPref;
import ru.vtosters.lite.tgs.TGRoot;
import ru.vtosters.lite.ui.PreferenceFragmentUtils;
import ru.vtosters.lite.ui.fragments.TrackedMaterialPreferenceToolbarFragment;
public class StickersPreferencesFragment extends TrackedMaterialPreferenceToolbarFragment {
@Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
int vtosterXml = R.xml.empty;
addPreferencesFromResource(vtosterXml);
PreferenceFragmentUtils.addPreferenceCategory(getPreferenceScreen(), requireContext().getString(R.string.sprefsstickers));
PreferenceFragmentUtils.addPreference(getPreferenceScreen(), "", R.string.sprefsdelkey, 0, 0, preference -> {
TGPref.setTGBotKey(null);
ToastUtils.a(requireContext().getString(R.string.sprefsdelkey2));
return false;
});
PreferenceFragmentUtils.addPreferenceCategory(getPreferenceScreen(), requireContext().getString(R.string.sprefsnetwork));
PreferenceFragmentUtils.addListPreference(getPreferenceScreen(), "VTGS:CM", "0", requireContext().getString(R.string.sprefsctype), new CharSequence[]{
requireContext().getString(R.string.ctypedirect), requireContext().getString(R.string.ctypesocks)
}, new String[]{
"0", "2"
});
findPreference("VTGS:CM").setTitle(TGRoot.getSummary());
findPreference("VTGS:CM").setOnPreferenceChangeListener((preference, o) -> {
String newData = (String) o;
TGPref.setTGConnectMethod(Integer.parseInt(newData));
TelegramStickersGrabber.updateURLs();
preference.setTitle(TGRoot.getSummary());
return true;
});
PreferenceFragmentUtils.addPreferenceCategory(getPreferenceScreen(), R.string.ssocks);
PreferenceFragmentUtils.addEditTextPreference(getPreferenceScreen(), "tg_proxy_host", R.string.ssockshost, (preference, o) -> {
TGPref.setTGProxyIP(o);
return true;
});
PreferenceFragmentUtils.addEditTextPreference(getPreferenceScreen(), "tg_proxy_port", R.string.ssocksport, (preference, o) -> {
try {
TGPref.setTGProxyPort(Integer.parseInt(o));
return true;
} catch (Exception e) {
ToastUtils.a(requireContext().getString(R.string.ssockswport));
return false;
}
});
PreferenceFragmentUtils.addMaterialSwitchPreference(getPreferenceScreen(), "tg_proxy_auth", requireContext().getString(R.string.ssocksauth), "", 0, false, (preference, o) -> {
boolean value = (boolean) o;
findPreference("tg_proxy_login").setEnabled(value);
findPreference("tg_proxy_pass").setEnabled(value);
TGPref.setTGProxyPassEnabled(value);
return true;
});
PreferenceFragmentUtils.addEditTextPreference(getPreferenceScreen(), "tg_proxy_login", R.string.ssockslogin, (preference, o) -> {
TGPref.setTGProxyUserPass(o, TGPref.getTGProxyPass());
return true;
});
PreferenceFragmentUtils.addEditTextPreference(getPreferenceScreen(), "tg_proxy_pass", R.string.ssockspass, (preference, o) -> {
TGPref.setTGProxyUserPass(TGPref.getTGProxyUser(), o);
return true;
});
findPreference("tg_proxy_login").setEnabled(TGPref.isTGProxyPassEnabled());
findPreference("tg_proxy_pass").setEnabled(TGPref.isTGProxyPassEnabled());
}
@Override
public int T4() {
return R.string.vtltgs;
}
}
@@ -11,8 +11,7 @@ import java.nio.ByteBuffer;
import static ru.vtosters.lite.utils.AndroidUtils.getGlobalContext;
enum ImageEffects {
Blur(true,
AndroidUtils.getArray(R.array.filter_types),
Blur(AndroidUtils.getArray(R.array.filter_types),
new String[]{"disabled", "low", "med", "high"},
getGlobalContext().getString(R.string.filter_blur)
) {
@@ -40,8 +39,7 @@ enum ImageEffects {
};
}
},
Dim(true,
AndroidUtils.getArray(R.array.filter_dim_types),
Dim(AndroidUtils.getArray(R.array.filter_dim_types),
new String[]{"disabled", "dim_black", "dim_white"},
getGlobalContext().getString(R.string.filter_dim)) {
@Override
@@ -66,8 +64,7 @@ enum ImageEffects {
};
}
},
Mosaic(true,
AndroidUtils.getArray(R.array.filter_types),
Mosaic(AndroidUtils.getArray(R.array.filter_types),
new String[]{"disabled", "low", "med", "high"},
getGlobalContext().getString(R.string.filter_mosaic)) {
@Override
@@ -94,7 +91,7 @@ enum ImageEffects {
};
}
},
Monochrome(false, null, null,
Monochrome(null, null,
getGlobalContext().getString(R.string.filter_monochrome)) {
@Override
public void applyEffect(ByteBuffer bitmap, int height, int width) {
@@ -103,7 +100,7 @@ enum ImageEffects {
}
}
},
Invert(false, null, null,
Invert(null, null,
getGlobalContext().getString(R.string.filter_invert_colors)) {
@Override
public void applyEffect(ByteBuffer bitmap, int height, int width) {
@@ -112,7 +109,7 @@ enum ImageEffects {
}
}
},
Sepia(false, null, null,
Sepia(null, null,
getGlobalContext().getString(R.string.filter_sepia)) {
@Override
public void applyEffect(ByteBuffer bitmap, int height, int width) {
@@ -121,7 +118,7 @@ enum ImageEffects {
}
}
},
Emboss(false, null, null,
Emboss(null, null,
getGlobalContext().getString(R.string.filter_emboss)) {
@Override
public String getSummary() {
@@ -135,7 +132,7 @@ enum ImageEffects {
}
}
},
Engrave(false, null, null,
Engrave(null, null,
getGlobalContext().getString(R.string.filter_engrave)) {
@Override
public String getSummary() {
@@ -149,7 +146,7 @@ enum ImageEffects {
}
}
},
Flea(false, null, null,
Flea(null, null,
getGlobalContext().getString(R.string.filter_flea)) {
@Override
public void applyEffect(ByteBuffer bitmap, int height, int width) {
@@ -158,7 +155,7 @@ enum ImageEffects {
}
}
},
Snow(false, null, null,
Snow(null, null,
getGlobalContext().getString(R.string.filter_snow)) {
@Override
public void applyEffect(ByteBuffer bitmap, int height, int width) {
@@ -168,15 +165,13 @@ enum ImageEffects {
}
};
private final boolean free;
private final String title;
@Nullable
private final String[] entryValues;
@Nullable
private final String[] entries;
ImageEffects(boolean free, @Nullable String[] entries, @Nullable String[] entryValues, String title) {
this.free = free;
ImageEffects(@Nullable String[] entries, @Nullable String[] entryValues, String title) {
this.entries = entries;
this.entryValues = entryValues;
this.title = title;
@@ -207,10 +202,6 @@ enum ImageEffects {
return entryValues != null && entryValues.length > 0;
}
public boolean isFree() {
return free;
}
@Nullable
public String[] getEntries() {
return entries;
@@ -80,24 +80,7 @@ public class WallpaperMenuFragment extends MaterialPreferenceToolbarFragment {
PreferenceFragmentUtils.addPreferenceCategory(getPreferenceScreen(), requireContext().getString(R.string.vtlfilters));
if (!Preferences.hasVerification() && !Preferences.getBoolValue("dialogrecomm", false)) {
PreferenceFragmentUtils.addPreference(
getPreferenceScreen(),
"",
requireContext().getString(R.string.filters_warning),
requireContext().getString(R.string.icons_warning_info),
0,
preference -> {
requireContext().startActivity(
new Intent(Intent.ACTION_VIEW)
.setData(Uri.parse("https://vtosters.app/donate/"))
);
return false;
});
}
Arrays.stream(ImageEffects.values())
.filter(imageEffects -> imageEffects.isFree() || Preferences.hasVerification())
.forEach(it -> {
if (it.isList()) {
var pref = PreferenceFragmentUtils.addListPreference(
@@ -1,8 +1,9 @@
package ru.vtosters.lite.utils;
import android.app.Activity;
import android.app.NotificationManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.verify.domain.DomainVerificationManager;
@@ -11,11 +12,11 @@ import android.content.res.Resources;
import android.net.Uri;
import android.os.Build;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import com.vk.core.dialogs.alert.VkAlertDialog;
import com.vk.core.util.Screen;
import androidx.core.app.NotificationManagerCompat;
import com.vk.core.util.ToastUtils;
import com.vtosters.lite.general.fragments.WebViewFragment;
import ru.vtosters.hooks.other.Preferences;
@@ -30,17 +31,28 @@ import java.security.NoSuchAlgorithmException;
import java.util.Map;
import java.util.Random;
import static ru.vtosters.hooks.other.Preferences.getBoolValue;
public class AndroidUtils {
private static final String ALLOWED_CHARACTERS = "0123456789qwertyuiopasdfghjklzxcvbnm";
public enum ScreenSize {
small,
normal,
large,
xlarge
}
public static boolean isDebuggable() {
return 0 != (AndroidUtils.getGlobalContext().getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE);
}
public static boolean isAdbOrDeveloperOptionsEnabled(ContentResolver contentResolver) {
int adbEnabled = Settings.Global.getInt(contentResolver, Settings.Global.ADB_ENABLED, 0);
int developerOptionsEnabled = Settings.Global.getInt(contentResolver, Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0);
return adbEnabled == 1 || developerOptionsEnabled == 1;
}
public static boolean isTablet() {
return Screen.l(getGlobalContext());
String string = getGlobalContext().getResources().getString(b.h.g.d.screen_size);
return TextUtils.equals(string, ScreenSize.large.name()) || TextUtils.equals(string, ScreenSize.xlarge.name());
}
@NonNull
@@ -117,8 +129,8 @@ public class AndroidUtils {
new WebViewFragment.g(url).l().m().h().j().a(activity);
}
public static void checkLinksVerified(Activity activity) {
if (Build.VERSION.SDK_INT < 31) return;
public static boolean isLinksUnverified(Activity activity) {
if (Build.VERSION.SDK_INT < 31) return false;
DomainVerificationManager manager = activity.getSystemService(DomainVerificationManager.class);
DomainVerificationUserState userState;
@@ -126,7 +138,7 @@ public class AndroidUtils {
try {
userState = manager.getDomainVerificationUserState(getPackageName());
} catch (PackageManager.NameNotFoundException ignore) {
return;
return false;
}
boolean hasUnverified = false;
@@ -138,32 +150,7 @@ public class AndroidUtils {
hasUnverified = stateValue != null && stateValue != DomainVerificationUserState.DOMAIN_STATE_VERIFIED && stateValue != DomainVerificationUserState.DOMAIN_STATE_SELECTED;
}
if (hasUnverified) {
if (getBoolValue("showUnverifDialog", true)) {
new VkAlertDialog.Builder(activity)
.setTitle(com.vtosters.lite.R.string.warning)
.setMessage(AndroidUtils.getString("app_open_by_default_settings"))
.setCancelable(false)
.setPositiveButton(com.vtosters.lite.R.string.social_graph_skip,
(dialogInterface, i) -> Preferences.getPreferences().edit().putBoolean("showUnverifDialog", false).apply()
)
.setNeutralButton(com.vtosters.lite.R.string.open_settings,
(dialogInterface, i) -> {
try {
Intent intent = new Intent(Settings.ACTION_APP_OPEN_BY_DEFAULT_SETTINGS, Uri.parse("package:" + getPackageName()));
activity.startActivity(intent);
} catch (Throwable t1) {
try {
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.parse("package:" + getPackageName()));
activity.startActivity(intent);
} catch (Throwable ignored) {
}
}
}
)
.show();
}
}
return hasUnverified;
}
public static String MD5(String s) {
@@ -185,6 +172,15 @@ public class AndroidUtils {
return null;
}
public static boolean areNotificationsEnabled() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationManager manager = (NotificationManager) getGlobalContext().getSystemService(Context.NOTIFICATION_SERVICE);
return manager.areNotificationsEnabled();
} else {
return NotificationManagerCompat.from(getGlobalContext()).areNotificationsEnabled();
}
}
private static String getRandomString(int sizeOfRandomString) {
Random random = new Random();
StringBuilder sb = new StringBuilder(sizeOfRandomString);
@@ -44,7 +44,7 @@ public class ExternalLinkParser {
parseVKVideo(videoFile, qualities, urls);
if (urls.size() > 0)
if (!urls.isEmpty())
if (getBoolValue("maxquality", false) || qualities.size() == 1)
startExternalVideo(context, urls.get(0));
else
@@ -1,40 +1,13 @@
package ru.vtosters.lite.utils;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
public class IOUtils {
public static int BUFFER_SIZE = 8192;
public static byte[] decodeStream(InputStream encIs, byte[] aesKey)
throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, IOException {
CipherInputStream cip = new CipherInputStream(encIs, getCipher(aesKey));
return readFully(cip);
}
public static Cipher getCipher(byte[] aesKey)
throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
Key keySpec = new SecretKeySpec(aesKey, "AES");
byte[] iv = new byte[16];
Arrays.fill(iv, (byte) 0x0);
IvParameterSpec ivps = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivps);
return cipher;
}
public static InputStream openStream(String url)
throws IOException {
URLConnection connection = new URL(url).openConnection();
@@ -52,7 +52,7 @@ public class LifecycleUtils {
}
public static Activity getCurrentActivity() {
return activities.size() > 0 ? activities.get(activities.size() - 1) : new MainActivity();
return !activities.isEmpty() ? activities.get(activities.size() - 1) : new MainActivity();
}
public static void restartApplication() {
@@ -22,7 +22,7 @@ public class OTAUtils {
this.mListener = listener;
}
public void loadData() {
public void loadData(boolean isManualCheck) {
Request release = new Request.a()
.b(LATEST_RELEASE_URL)
.a();
@@ -31,13 +31,13 @@ public class OTAUtils {
@Override
public void a(Call call, IOException e) {
Log.d("OTAHelper", "Error while getting latest release info: " + e);
mListener.onUpdateCanceled();
mListener.onUpdateError();
}
@Override
public void a(Call call, Response response) throws IOException {
try {
setData(response);
setData(response, isManualCheck);
} catch (JSONException | NullPointerException e) {
e.printStackTrace();
}
@@ -46,7 +46,7 @@ public class OTAUtils {
});
}
void setData(Response response) throws IOException, JSONException {
void setData(Response response, boolean isManualCheck) throws IOException, JSONException {
mReleaseJson = new JSONObject(response.a().g());
String tag = mReleaseJson.getString("tag_name");
@@ -58,7 +58,7 @@ public class OTAUtils {
@Override
public void a(Call call, IOException e) {
Log.d("OTAHelper", "Error while getting latest commit info: " + e);
mListener.onUpdateCanceled();
mListener.onUpdateError();
}
@Override
@@ -67,12 +67,12 @@ public class OTAUtils {
mCommitJson = new JSONObject(response.a().g());
mCommitSHA = mCommitJson.getJSONObject("object").getString("sha");
if (isNewVersion())
mListener.onUpdateApplied();
mListener.onUpdateApplied(isManualCheck);
else
mListener.onUpdateCanceled();
mListener.onUpdateLatest(isManualCheck);
} catch (NullPointerException | JSONException e) {
e.printStackTrace();
mListener.onUpdateCanceled();
mListener.onUpdateError();
}
}
});
@@ -118,9 +118,10 @@ public class OTAUtils {
public interface OTAListener {
void onUpdateApplied();
void onUpdateApplied(boolean isManualCheck);
void onUpdateCanceled();
void onUpdateError();
void onUpdateLatest(boolean isManualCheck);
}
}
@@ -3,6 +3,7 @@ package ru.vtosters.lite.utils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import ru.vtosters.hooks.other.Preferences;
import static ru.vtosters.lite.foaf.FoafBase.getBypassedOnlineInfo;
@@ -13,7 +14,7 @@ public class OnlineBypass {
return json;
}
var onlineinfo = json.optJSONObject("online_info");
if (onlineinfo != null && !onlineinfo.optBoolean("visible")) {
if (onlineinfo != null && !onlineinfo.optBoolean("visible") && !Preferences.serverFeaturesDisable()) {
var bypassed = getBypassedOnlineInfo(id);
if (bypassed.optInt("last_seen", 0) != 0) {
json.remove("online_info");
@@ -37,7 +38,7 @@ public class OnlineBypass {
}
public static JSONArray setOnlineInfoUsers(JSONArray profiles) throws JSONException {
if (profiles == null || profiles.length() == 0) return profiles;
if (profiles == null || profiles.length() == 0 || Preferences.serverFeaturesDisable()) return profiles;
StringBuilder sb = new StringBuilder();
var curVkId = AccountManagerUtils.getUserId();
for (int i = 0; i < profiles.length(); i++) {
@@ -51,7 +52,7 @@ public class OnlineBypass {
sb.append(",");
}
var ids = sb.toString();
if (ids.length() > 0) {
if (!ids.isEmpty()) {
ids = ids.substring(0, ids.length() - 1);
} else {
return profiles;
@@ -19,8 +19,8 @@ public class SuperAppUtils {
}
public static JSONObject superapp(JSONObject json) throws JSONException {
var superApps = Preferences.getPreferences().getString("superapp_items",
"menu,miniapps,vkpay_slim,greeting,promo,holiday,weather,sport,games,informer,food,event,music,vk_run").split(",");
var superApps = Preferences.getPreferences().getString("superapp_items", "menu,miniapps,vkpay_slim,greeting,promo,holiday,weather,sport,games,informer,food,event,music,vk_run").split(",");
if (superApps.length == 0) return json;
var oldItems = json.optJSONArray("items");
@@ -31,7 +31,24 @@ public class SuperAppUtils {
var type = item.optString("type");
for (String superApp : superApps) {
if (type.equals(superApp))
newItems.put(item);
if (superApp.equals("menu")) { // superapp menu fix
String menuitems = "groups,audios,stickers,podcasts,shopping,lives,videos,games,vk_calls,vk_pay,ads_easy_promote,classifieds,events";
JSONObject menuItem = new JSONObject();
JSONArray menuItems = new JSONArray();
generateMenuItems(menuitems, menuItems);
JSONObject object = new JSONObject();
object.put("count", menuitems.split(",").length);
object.put("items", menuItems);
menuItem.put("type", "menu");
menuItem.put("object", object);
newItems.put(menuItem);
} else {
newItems.put(item);
}
}
}
for (int i = 0; i < superApps.length; i++) {
@@ -46,4 +63,16 @@ public class SuperAppUtils {
return json.putOpt("items", newItems);
}
private static void generateMenuItems(String str, JSONArray jsonArray) throws JSONException {
String[] strings = str.split(",");
for (String string : strings) {
JSONObject jsonObject = new JSONObject();
jsonObject.put("name", string);
jsonArray.put(jsonObject);
}
}
}
@@ -7,7 +7,6 @@ import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.util.Log;
import com.vk.usersstore.contentprovider.a.UsersDbHelper;
import com.vtosters.lite.auth.VKAuth;
@@ -76,7 +75,7 @@ public class VKAccountDB {
var dir = new File(getExternalStoragePublicDirectory(DIRECTORY_DOWNLOADS), "/VTLBackups/");
var file = new File(dir, "saved_accounts_" + dateFormat.format(new Date()) + ".vtl");
try (InputStream inputStream = new FileInputStream(db.getPath())){
try (InputStream inputStream = new FileInputStream(db.getPath())) {
dir.mkdirs();
FileOutputStream outputStream = new FileOutputStream(file);
@@ -7,15 +7,14 @@ import okhttp3.*;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import ru.vtosters.hooks.other.Preferences;
import ru.vtosters.lite.di.singleton.VtOkHttpClient;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import static ru.vtosters.hooks.other.Preferences.getBoolValue;
import static ru.vtosters.hooks.other.Preferences.hasVerification;
public class VTVerifications {
public static final List<Integer> sVerifications = new ArrayList<>();
@@ -33,7 +32,7 @@ public class VTVerifications {
return;
}
if ((!NetworkUtils.isNetworkConnected() && NetworkUtils.isInternetSlow() || getBoolValue("isRoamingState", false)) && prefs.contains("ids")) {
if ((!NetworkUtils.isNetworkConnected() && NetworkUtils.isInternetSlow() || getBoolValue("isRoamingState", false)) && prefs.contains("ids") || Preferences.serverFeaturesDisable()) {
parseJson(prefs.getString("ids", "[]"));
Log.d("VTVerifications", "load from memory. Roaming or Network issues");
isLoaded = true;
@@ -129,7 +128,7 @@ public class VTVerifications {
return -id;
}
public static boolean haveDonateButton() {
return hasVerification() || new Random().nextInt(6) != 1;
}
// public static boolean haveDonateButton() {
// return hasVerification() || new Random().nextInt(6) != 1;
// }
}
@@ -0,0 +1,42 @@
package ru.vtosters.lite.utils.music;
import ru.vtosters.lite.utils.AndroidUtils;
import ru.vtosters.lite.utils.IOUtils;
import java.io.File;
public class MusicCacheStorageUtils {
public static File getCacheStorageDir() {
return AndroidUtils.getGlobalContext().getExternalFilesDir("vt_tracks");
}
@SuppressWarnings("ResultOfMethodCallIgnored")
public static File getTrackDirById(final String trackId) {
final var res = new File(getCacheStorageDir(), trackId);
res.mkdirs();
return res;
}
public static File getTrackFile(final String trackId) {
return new File(getTrackDirById(trackId), "track.mp3");
}
@SuppressWarnings("ResultOfMethodCallIgnored")
public static File getThumbDirById(final String trackId) {
final var res = new File(getTrackDirById(trackId), "thumbs");
res.mkdirs();
return res;
}
public static File getTrackThumb(final String trackId, final int factor) {
return new File(getThumbDirById(trackId), "thumb_" + factor + ".png");
}
public static void removeTrackDirById(final String trackId) {
IOUtils.deleteRecursive(getTrackDirById(trackId));
}
public static void clear() {
IOUtils.deleteRecursive(getCacheStorageDir());
}
}
@@ -0,0 +1,47 @@
package ru.vtosters.lite.utils.music;
import android.os.RemoteException;
import bruhcollective.itaysonlab.libvkx.ILibVkxService;
import bruhcollective.itaysonlab.libvkx.client.LibVKXClient;
import bruhcollective.itaysonlab.libvkx.client.LibVKXClientImpl;
public class VKXUtils {
public static boolean isVkxCached(int playlist_id, int owner_id) {
return LibVKXClient.getInstance().runOnServiceSync(new LibVKXClientImpl.LibVKXActionGeneric<>() {
@Override
public Boolean run(ILibVkxService service) {
try {
return service.isPlaylistCached(playlist_id, owner_id);
} catch (RemoteException e) {
return false;
}
}
@Override
public Boolean defaultValue() {
return false;
}
});
}
public static boolean isVkxCached(String trackId) {
return LibVKXClient.getInstance().runOnServiceSync(new LibVKXClientImpl.LibVKXActionGeneric<>() {
@Override
public Boolean run(ILibVkxService service) {
try {
var splits = trackId.split("_");
var id = Integer.parseInt(splits[1]);
var owner = Integer.parseInt(splits[0]);
return service.isTrackCached(id, owner);
} catch (RemoteException e) {
return false;
}
}
@Override
public Boolean defaultValue() {
return false;
}
});
}
}
+9
View File
@@ -138,6 +138,7 @@
<uses-permission android:name="me.everything.badger.permission.BADGE_COUNT_WRITE" />
<uses-permission android:name="com.google.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="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="com.google.android.finsky.permission.BIND_GET_INSTALL_REFERRER_SERVICE" />
@@ -2313,6 +2314,14 @@
<action android:name="com.mgoogle.android.c2dm.intent.RECEIVE" />
</intent-filter>
</receiver>
<receiver
android:name="com.google.firebase.iid.FirebaseInstanceIdReceiver3"
android:exported="true"
android:permission="app.revanced.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="app.revanced.android.c2dm.intent.RECEIVE" />
</intent-filter>
</receiver>
<activity
android:name="com.google.android.gms.common.api.GoogleApiActivity"
android:exported="false"
+1 -1
View File
@@ -25,5 +25,5 @@ usesFramework:
tag: null
version: 2.6.0
versionInfo:
versionCode: '4838'
versionCode: '4839'
versionName: 5.56.1
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

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