diff --git a/app/libs/classes-dex2jar.jar b/app/libs/classes-dex2jar.jar new file mode 100644 index 000000000..886991ee9 Binary files /dev/null and b/app/libs/classes-dex2jar.jar differ diff --git a/app/libs/classes2-dex2jar.jar b/app/libs/classes2-dex2jar.jar new file mode 100644 index 000000000..ea0425fab Binary files /dev/null and b/app/libs/classes2-dex2jar.jar differ diff --git a/app/libs/classes2-dex2jar.jar.REMOVED.git-id b/app/libs/classes2-dex2jar.jar.REMOVED.git-id deleted file mode 100644 index 500874678..000000000 --- a/app/libs/classes2-dex2jar.jar.REMOVED.git-id +++ /dev/null @@ -1 +0,0 @@ -ea0425fab4379ae5f7e9db05a22d4b7afd709560 \ No newline at end of file diff --git a/app/libs/classes3-dex2jar.jar b/app/libs/classes3-dex2jar.jar new file mode 100644 index 000000000..3b92d8876 Binary files /dev/null and b/app/libs/classes3-dex2jar.jar differ diff --git a/app/libs/classes3-dex2jar.jar.REMOVED.git-id b/app/libs/classes3-dex2jar.jar.REMOVED.git-id deleted file mode 100644 index a064f8665..000000000 --- a/app/libs/classes3-dex2jar.jar.REMOVED.git-id +++ /dev/null @@ -1 +0,0 @@ -3b92d8876dd59d5afc60a53bb23b103cede6a194 \ No newline at end of file diff --git a/app/libs/classes4-dex2jar.jar b/app/libs/classes4-dex2jar.jar new file mode 100644 index 000000000..2ee7dbd87 Binary files /dev/null and b/app/libs/classes4-dex2jar.jar differ diff --git a/app/libs/classes5-dex2jar.jar b/app/libs/classes5-dex2jar.jar new file mode 100644 index 000000000..198831243 Binary files /dev/null and b/app/libs/classes5-dex2jar.jar differ diff --git a/app/src/main/java/ru/vtosters/lite/downloaders/AudioDownloader.java b/app/src/main/java/ru/vtosters/lite/downloaders/AudioDownloader.java index bc9f5d154..fdf3a7b33 100644 --- a/app/src/main/java/ru/vtosters/lite/downloaders/AudioDownloader.java +++ b/app/src/main/java/ru/vtosters/lite/downloaders/AudioDownloader.java @@ -19,7 +19,7 @@ import ru.vtosters.lite.music.MP3Downloader; import ru.vtosters.lite.utils.AndroidUtils; public class AudioDownloader { - private static NotificationManagerCompat notificationManager = NotificationManagerCompat.from(AndroidUtils.getGlobalContext()); + private static NotificationManagerCompat notificationManager = NotificationChannels.getNotificationManager(); public static void downloadAudio(MusicTrack track) { if (AndroidUtils.getDefaultPrefs().getBoolean("new_music_downloading_way", false)) { @@ -93,7 +93,7 @@ public class AudioDownloader { } private static void downloadMP3(MusicTrack track) { - MP3Downloader.execute(track); + MP3Downloader.downloadMP3(track); } private static NotificationCompat.Builder buildDownloadNotification(MusicTrack track, int id) { @@ -103,8 +103,8 @@ public class AudioDownloader { .setContentText(track.toString()) .setPriority(NotificationCompat.PRIORITY_LOW) .setCategory(NotificationCompat.CATEGORY_PROGRESS) - .setAutoCancel(true); - notificationBuilder.setProgress(100, 0, false); + .setAutoCancel(true) + .setProgress(100, 0, false); notificationManager.notify(id, notificationBuilder.build()); return notificationBuilder; } diff --git a/app/src/main/java/ru/vtosters/lite/downloaders/notifications/NotificationChannels.java b/app/src/main/java/ru/vtosters/lite/downloaders/notifications/NotificationChannels.java index b7937baf9..146379963 100644 --- a/app/src/main/java/ru/vtosters/lite/downloaders/notifications/NotificationChannels.java +++ b/app/src/main/java/ru/vtosters/lite/downloaders/notifications/NotificationChannels.java @@ -13,8 +13,11 @@ import ru.vtosters.lite.utils.AndroidUtils; public class NotificationChannels { public static final String MUSIC_DOWNLOAD_CHANNEL_ID = "music_download_channel"; public static final String MUSIC_DOWNLOAD_CHANNEL_NAME = "Загрузка музыки"; + public static final String MUSIC_CACHING_NAME = "Кеширование трека / Обложки"; public static final String MUSIC_DOWNLOAD_CHANNEL_DESCRIPTION = "Прогресс загрузки музыки"; + private static NotificationManagerCompat notificationManagerCompat; + @RequiresApi(api = Build.VERSION_CODES.O) public static void createChannels() { createMusicDownloadChannel(); @@ -29,4 +32,10 @@ public class NotificationChannels { var notificationManager = NotificationManagerCompat.from(AndroidUtils.getGlobalContext()); notificationManager.createNotificationChannel(musicDownloadChannel); } + + public static NotificationManagerCompat getNotificationManager() { + if (notificationManagerCompat == null) + notificationManagerCompat = NotificationManagerCompat.from(AndroidUtils.getGlobalContext()); + return notificationManagerCompat; + } } diff --git a/app/src/main/java/ru/vtosters/lite/music/ArtworkDownloader.java b/app/src/main/java/ru/vtosters/lite/music/ArtworkDownloader.java new file mode 100644 index 000000000..4201465ad --- /dev/null +++ b/app/src/main/java/ru/vtosters/lite/music/ArtworkDownloader.java @@ -0,0 +1,61 @@ +package ru.vtosters.lite.music; + +import android.app.Notification; +import android.content.Context; +import android.os.Build; +import android.util.Log; + +import com.vk.dto.music.MusicTrack; + +import org.json.JSONObject; + +import java.io.IOException; +import java.net.URL; + +import ru.vtosters.lite.downloaders.notifications.NotificationChannels; +import ru.vtosters.lite.music.cache.FileCacheImplementation; +import ru.vtosters.lite.utils.IOUtils; + +public class ArtworkDownloader { + + public static void downloadArtworks(MusicTrack track) throws IOException { + var obj = track.J(); + + if (!obj.has("album")) return; + JSONObject album = obj.optJSONObject("album"); + + if (!album.has("thumb")) return; + JSONObject thumb = album.optJSONObject("thumb"); + + var trackId = track.y1(); + if (thumb.has("photo_300")) + downloadArtwork(thumb.optString("photo_300"), 300, trackId.hashCode(), trackId); + if (thumb.has("photo_600")) + downloadArtwork(thumb.optString("photo_600"), 600, trackId.hashCode(), trackId); + if (thumb.has("photo_1200")) + downloadArtwork(thumb.optString("photo_1200"), 1200, trackId.hashCode(), trackId); + } + + private static void downloadArtwork(String url, int res, int hash, String trackId) throws IOException { + var content = IOUtils.readAllBytes(new URL(url).openStream()); + IOUtils.writeToFile(FileCacheImplementation.getTrackArtwork(trackId, res), content); + Log.d("ArtworkDownloader", "Downloading " + url); + //notifyArtwork(AndroidUtils.getGlobalContext(), hash, res); + } + + private static void notifyArtwork(Context ctx, int hash, int resolution) { + Notification.Builder builder; + + if (Build.VERSION.SDK_INT >= 26) { + builder = new Notification.Builder(ctx, NotificationChannels.MUSIC_DOWNLOAD_CHANNEL_ID); + builder.setVisibility(Notification.VISIBILITY_PUBLIC); + } else builder = new Notification.Builder(ctx); + + builder.setSmallIcon(com.vtosters.lite.R.drawable.ic_download_outline_28) + .setShowWhen(false) + .setContentTitle(NotificationChannels.MUSIC_CACHING_NAME) + .setContentText("Разрешение обложки: " + resolution + "x" + resolution); + + NotificationChannels.getNotificationManager().notify(hash, builder.build()); + } +} diff --git a/app/src/main/java/ru/vtosters/lite/music/MP3Downloader.java b/app/src/main/java/ru/vtosters/lite/music/MP3Downloader.java index 27e33ba77..ddff202ed 100644 --- a/app/src/main/java/ru/vtosters/lite/music/MP3Downloader.java +++ b/app/src/main/java/ru/vtosters/lite/music/MP3Downloader.java @@ -1,17 +1,23 @@ package ru.vtosters.lite.music; import android.app.DownloadManager; +import android.app.Notification; import android.net.Uri; +import android.os.Build; import android.os.Environment; +import android.text.TextUtils; import android.util.Log; import android.widget.Toast; import com.vk.dto.music.MusicTrack; +import com.vk.dto.music.Playlist; import org.json.JSONException; import org.json.JSONObject; import java.io.IOException; +import java.net.URL; +import java.util.stream.Collectors; import okhttp3.Call; import okhttp3.Callback; @@ -19,19 +25,86 @@ import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; import ru.vtosters.lite.downloaders.AudioDownloader; +import ru.vtosters.lite.downloaders.notifications.NotificationChannels; +import ru.vtosters.lite.music.cache.CacheDatabaseDelegate; +import ru.vtosters.lite.music.cache.FileCacheImplementation; import ru.vtosters.lite.utils.AndroidUtils; +import ru.vtosters.lite.utils.IOUtils; public class MP3Downloader { + public final static int CACHE_STRATEGY = 0; + public final static int EXTERNAL_STORAGE_STRATEGY = 1; + private static final String URL_FORMAT = "https://api.vk.com/method/audio.getById?v=5.131&access_token=%s&audios=%s©right=https://vtosters.app/"; private static final OkHttpClient client = new OkHttpClient(); - public static void execute(MusicTrack track) { + private static int getSelectedStrategy() { + return AndroidUtils.getPreferences().getInt("music_storing_strategy", CACHE_STRATEGY); + } + + public static void downloadPlaylist(Playlist playlist) { var token = AndroidUtils.getPreferences().getString("vk_admin_token", ""); if (token.isEmpty()) { Toast.makeText(AndroidUtils.getGlobalContext(), "Обновите токен VK Admin в настройках", Toast.LENGTH_SHORT).show(); return; } + + String trackIds = playlist.R + .stream() + .map(MusicTrack::y1) + .collect(Collectors.joining(",")); + + var request = new Request.a() + .b(String.format(URL_FORMAT, token, trackIds)) + .a(); + client.a(request).a(new Callback() { + + @Override + public void a(Call call, IOException e) { + Log.d("MP3Downloader", e + ""); + } + + @Override + public void a(Call call, Response response) throws IOException { + var payload = response.a().g(); + Log.d("MP3Downloader", payload); + try { + JSONObject jsonObj = new JSONObject(payload); + if (!jsonObj.has("response")) + return; + var tracks = new JSONObject(payload).getJSONArray("response"); + for (int i = 0; i < tracks.length(); i++) { + JSONObject responseNode = tracks.getJSONObject(i); + if (!responseNode.has("url")) + continue; + var url = responseNode.optString("url"); + Log.d("MP3Downloader", url); + if (TextUtils.isEmpty(url)) + return; + var track = playlist.R.get(i); + switch (getSelectedStrategy()) { + case CACHE_STRATEGY: + saveToCache(track, url); + break; + case EXTERNAL_STORAGE_STRATEGY: + saveToDownloads(track.toString(), url); + } + } + } catch (JSONException e) { + Log.d("MP3Downloader", e + ""); + } + } + }); + } + + public static void downloadMP3(MusicTrack track) { + var token = AndroidUtils.getPreferences().getString("vk_admin_token", ""); + if (token.isEmpty()) { + Toast.makeText(AndroidUtils.getGlobalContext(), "Обновите токен VK Admin в настройках", Toast.LENGTH_SHORT).show(); + return; + } + var request = new Request.a() .b(String.format(URL_FORMAT, token, track.y1())) .a(); @@ -47,20 +120,28 @@ public class MP3Downloader { Log.d("MP3Downloader", payload); try { JSONObject jsonObj = new JSONObject(payload); - if (jsonObj.has("response")) { - JSONObject responseNode = new JSONObject(payload).getJSONArray("response") - .getJSONObject(0); - if (responseNode.has("url")) { - var url = responseNode.optString("url"); - Log.d("MP3Downloader", url); - if (url != null) { - if (url.endsWith(".mp3")) { - downloadMP3(track.toString(), url); - } else if (VKM3UParser.isM3U8(url)) { - AudioDownloader.downloadM3U8(track); - } - } - } + if (!jsonObj.has("response")) + return; + JSONObject responseNode = new JSONObject(payload).getJSONArray("response") + .getJSONObject(0); + if (!responseNode.has("url")) + return; + var url = responseNode.optString("url"); + Log.d("MP3Downloader", url); + if (TextUtils.isEmpty(url)) + return; + if (VKM3UParser.isM3U8(url)) { + AudioDownloader.downloadM3U8(track); + return; + } + switch (getSelectedStrategy()) { + case CACHE_STRATEGY: + ArtworkDownloader.downloadArtworks(track); + saveToCache(track, url); + break; + case EXTERNAL_STORAGE_STRATEGY: + saveToDownloads(track.toString(), url); + break; } } catch (JSONException e) { Log.d("MP3Downloader", e + ""); @@ -69,7 +150,37 @@ public class MP3Downloader { }); } - private static void downloadMP3(String title, String url) { + private static void saveToCache(MusicTrack track, String url) throws IOException { + var trackId = track.y1(); + if (CacheDatabaseDelegate.isCached(trackId)) + return; + + var content = IOUtils.readAllBytes(new URL(url).openStream()); + var trackFile = FileCacheImplementation.getTrackFile(trackId); + if (!trackFile.exists()) + trackFile.getParentFile().mkdirs(); + IOUtils.writeToFile(trackFile, content); + notifySavingToCache(track); + CacheDatabaseDelegate.insertTrack(track); + NotificationChannels.getNotificationManager().cancel(trackId.hashCode()); + } + + private static void notifySavingToCache(MusicTrack track) { + Notification.Builder builder; + + if (Build.VERSION.SDK_INT >= 26) { + builder = new Notification.Builder(AndroidUtils.getGlobalContext(), NotificationChannels.MUSIC_DOWNLOAD_CHANNEL_ID); + builder.setVisibility(Notification.VISIBILITY_PUBLIC); + } else builder = new Notification.Builder(AndroidUtils.getGlobalContext()); + + builder.setSmallIcon(com.vtosters.lite.R.drawable.ic_download_outline_28) + .setShowWhen(false) + .setContentTitle("Закешированный трек: " + track.toString()); + + NotificationChannels.getNotificationManager().notify(track.y1().hashCode(), builder.build()); + } + + private static void saveToDownloads(String title, String url) { var downloadManager = AndroidUtils.getGlobalContext().getSystemService(DownloadManager.class); var request = new DownloadManager.Request(Uri.parse(url)) .setAllowedOverRoaming(true) diff --git a/app/src/main/java/ru/vtosters/lite/music/cache/CacheDatabaseDelegate.java b/app/src/main/java/ru/vtosters/lite/music/cache/CacheDatabaseDelegate.java new file mode 100644 index 000000000..8d1ec02ca --- /dev/null +++ b/app/src/main/java/ru/vtosters/lite/music/cache/CacheDatabaseDelegate.java @@ -0,0 +1,346 @@ +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 com.vk.core.util.ToastUtils; +import com.vk.dto.music.MusicTrack; + +import org.json.JSONObject; + +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.List; + +import bruhcollective.itaysonlab.libvkx.ILibVkxService; +import bruhcollective.itaysonlab.libvkx.client.LibVKXClient; +import bruhcollective.itaysonlab.libvkx.client.LibVKXClientImpl; +import ru.vtosters.lite.utils.AndroidUtils; +import ru.vtosters.lite.utils.IOUtils; + +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(AndroidUtils.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("Ошибка при записи трека в БД! [" + e.getMessage() + "]", true); + } + } + + public static void clear() { + getHelper().getReadableDatabase().execSQL(String.format("DELETE FROM %s", TABLE_NAME)); + } + + @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[0]); + var ownerId = Integer.parseInt(splits[1]); + + 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.getTrackArtwork(trackId, 300).exists() + && !FileCacheImplementation.getTrackArtwork(trackId, 600).exists() + && !FileCacheImplementation.getTrackArtwork(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.getTrackArtwork(trackId, 300))) + .putOpt("photo_600", FileCacheImplementation.getFileUri(FileCacheImplementation.getTrackArtwork(trackId, 600))) + .putOpt("photo_1200", FileCacheImplementation.getFileUri(FileCacheImplementation.getTrackArtwork(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 LibVKXClient.getInstance().runOnServiceSync(new LibVKXClientImpl.LibVKXActionGeneric() { + @Override + public Boolean run(ILibVkxService service) { + try { + return service.getCache().size() > 0; + } catch (RemoteException e) { + return false; + } + } + + @Override + public Boolean defaultValue() { + return false; + } + }); + } + + 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 getTracks() { + return getTracks(String.format( + "SELECT * FROM %s", + TABLE_NAME + )); + } + + public static List 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 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 getTracksByAlbumOne(String id) { + return getTracks(String.format( + "SELECT * FROM %s WHERE %s = %s LIMIT 1", + TABLE_NAME, + COLUMN_ALBUM_ID, + id + )); + } + + public static List getTracks(String query) { + List 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) { + if (!LibVKXClient.isIntegrationEnabled()) { + 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; + } + + return LibVKXClient.getInstance().runOnServiceSync(new LibVKXClientImpl.LibVKXActionGeneric() { + @Override + public Boolean run(ILibVkxService service) { + try { + var splits = trackId.split("_"); + var id = Integer.parseInt(splits[0]); + var owner = Integer.parseInt(splits[1]); + return service.isTrackCached(id, owner); + } catch (RemoteException e) { + return false; + } + } + + @Override + public Boolean defaultValue() { + return false; + } + }); + } + + public static void removeTrackFromCache(String trackId) { + if (LibVKXClient.isIntegrationEnabled()) { + LibVKXClient.getInstance().runOnService((service -> { + try { + var splits = trackId.split("_"); + var id = Integer.parseInt(splits[0]); + var owner = Integer.parseInt(splits[1]); + service.deleteTrackFromCache(id, owner); + } catch (RemoteException e) { + e.printStackTrace(); + } + })); + return; + } + + IOUtils.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() { + @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) { + + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/vtosters/lite/music/cache/FileCacheImplementation.java b/app/src/main/java/ru/vtosters/lite/music/cache/FileCacheImplementation.java new file mode 100644 index 000000000..d238736e6 --- /dev/null +++ b/app/src/main/java/ru/vtosters/lite/music/cache/FileCacheImplementation.java @@ -0,0 +1,48 @@ +package ru.vtosters.lite.music.cache; + +import android.content.Context; +import android.net.Uri; + +import com.vk.dto.music.MusicTrack; + +import java.io.File; + +import ru.vtosters.lite.utils.AndroidUtils; +import ru.vtosters.lite.utils.IOUtils; + +public class FileCacheImplementation { + + public static void clear() { + CacheDatabaseDelegate.clear(); + IOUtils.deleteRecursive(getCacheDir(), false); + } + + public static File getCacheDir() { + return AndroidUtils.getGlobalContext().getDir("vt_tracks", Context.MODE_PRIVATE); + } + + public static File getTrackFolder(String trackId) { + return new File(getCacheDir(), trackId); + } + + public static File getTrackFile(String trackId) { + return new File(getTrackFolder(trackId), "track.mp3"); + } + + public static File getTrackFile(MusicTrack track) { + return getTrackFile(track.y1()); + } + + public static File getTrackArtwork(String trackId, int factor) { + return new File(getTrackFolder(trackId), "artwork_" + 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"; + } +} diff --git a/app/src/main/java/ru/vtosters/lite/music/cache/VKListHelper.java b/app/src/main/java/ru/vtosters/lite/music/cache/VKListHelper.java new file mode 100644 index 000000000..ef65fc9d4 --- /dev/null +++ b/app/src/main/java/ru/vtosters/lite/music/cache/VKListHelper.java @@ -0,0 +1,27 @@ +package ru.vtosters.lite.music.cache; + +import com.vk.dto.common.data.VKList; + +import java.lang.reflect.InvocationTargetException; +import java.util.Collection; + +import ru.vtosters.lite.utils.ReflectionUtils; + +public class VKListHelper { + + public static void addToVKList(VKList vkList, Object addition) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + ReflectionUtils.invokeStaticMethod(VKList.class, "add", new Class[]{Object.class}, addition); + } + + public static void addToVKList(VKList vkList, int index, Object addition) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + ReflectionUtils.invokeStaticMethod(VKList.class, "add", new Class[]{int.class, Object.class}, index, addition); + } + + public static void addAllToVKList(VKList list, Collection addition) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + ReflectionUtils.invokeStaticMethod(VKList.class, "addAll", new Class[]{Collection.class}, addition); + } + + public static void addAllToVKList(VKList list, int index, Collection addition) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + ReflectionUtils.invokeStaticMethod(VKList.class, "addAll", new Class[]{int.class, Collection.class}, index, addition); + } +} diff --git a/app/src/main/java/ru/vtosters/lite/music/cache/helpers/PlaylistHelper.java b/app/src/main/java/ru/vtosters/lite/music/cache/helpers/PlaylistHelper.java new file mode 100644 index 000000000..143d86093 --- /dev/null +++ b/app/src/main/java/ru/vtosters/lite/music/cache/helpers/PlaylistHelper.java @@ -0,0 +1,92 @@ +package ru.vtosters.lite.music.cache.helpers; + +import com.vk.dto.music.Playlist; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.List; + +import ru.vtosters.lite.music.cache.CacheDatabaseDelegate; +import ru.vtosters.lite.music.cache.FileCacheImplementation; +import ru.vtosters.lite.utils.AccountManagerUtils; + +public class PlaylistHelper { + + public static Playlist createCachedPlaylistMetadata() { + try { + var obj = new JSONObject() + .put("id", -1) + .put("owner_id", AccountManagerUtils.getUserId()) + .put("type", 0) + .put("album_type", "playlist") + .put("title", "Кешированные треки") + .put("description", "Это - список всех кешированных треков. Данный плейлист НЕ СУЩЕСТВУЕТ в вашем профиле.\n\n" + + "При отсутствии соединения с Интернетом данный плейлист превратится в обычный список треков.") + .put("count", 0); + return new Playlist(obj); + } catch (JSONException e) { + e.printStackTrace(); + return null; + } + } + + public static Playlist createCachedPlaylistMetadata(String id) { + var track = TracklistHelper.getTracksOne(id).get(0); + var albumLink = track.I; + var thumb = albumLink.u1(); + return createAlbum( + thumb != null ? thumb.h(600) : "", + albumLink.getTitle(), + track.C, + track.y1(), + albumLink.getId() + ); + } + + public static Playlist createAlbum(String photo, String title, String artist, String trackId, int albumId) { + try { + var jPhoto = new JSONObject(); + if (!photo.isEmpty()) + jPhoto.put("height", 600) + .put("width", 600) + .putOpt("photo_300", FileCacheImplementation.getFileUri(FileCacheImplementation.getTrackArtwork(trackId, 300))) + .putOpt("photo_600", FileCacheImplementation.getFileUri(FileCacheImplementation.getTrackArtwork(trackId, 600))); + var obj = new JSONObject() + .put("id", -2) + .put("owner_id", albumId) + .put("type", 1) + .put("album_type", "main_only") + .put("title", title) + .put("description", albumId + "/ Это кешированный альбом!") + .put("main_artists", new JSONArray() + .put(new JSONObject().put("name", artist) + .put("id", "-1") + .put("domain", "-1"))) + .putOpt("photo", jPhoto) + .put("count", 0); + return new Playlist(obj); + } catch (JSONException e) { + e.printStackTrace(); + return null; + } + } + + public static List getAlbumPlaylists() { + List list = new ArrayList<>(); + CacheDatabaseDelegate.getTracksAsPlaylist().forEach(track -> { + var albumLink = track.I; + var thumb = albumLink.u1(); + list.add(createAlbum( + thumb != null ? thumb.h(600) : "", + albumLink.getTitle(), + track.C, + track.y1(), + albumLink.getId() + )); + }); + return list; + } +} diff --git a/app/src/main/java/ru/vtosters/lite/music/cache/helpers/TracklistHelper.java b/app/src/main/java/ru/vtosters/lite/music/cache/helpers/TracklistHelper.java new file mode 100644 index 000000000..6a654da55 --- /dev/null +++ b/app/src/main/java/ru/vtosters/lite/music/cache/helpers/TracklistHelper.java @@ -0,0 +1,40 @@ +package ru.vtosters.lite.music.cache.helpers; + +import com.vk.dto.music.MusicTrack; + +import org.json.JSONArray; + +import java.util.List; + +import ru.vtosters.lite.music.cache.CacheDatabaseDelegate; + +public class TracklistHelper { + + public static List getTracks() { + return CacheDatabaseDelegate.getTracks(); + } + + public static List getTracks(String id) { + return CacheDatabaseDelegate.getTracksByAlbum(id); + } + + public static List getTracksOne(String id) { + return CacheDatabaseDelegate.getTracksByAlbumOne(id); + } + + public static JSONArray tracksToIds(List tracks) { + var arr = new JSONArray(); + for (MusicTrack track : tracks) { + arr.put(track.y1()); + } + return arr; + } + + public static JSONArray tracksToJsons(List tracks) { + var arr = new JSONArray(); + for (MusicTrack track : tracks) { + arr.put(track.J()); + } + return arr; + } +} diff --git a/app/src/main/java/ru/vtosters/lite/music/cache/injectors/PlaylistInjector.java b/app/src/main/java/ru/vtosters/lite/music/cache/injectors/PlaylistInjector.java new file mode 100644 index 000000000..5165285de --- /dev/null +++ b/app/src/main/java/ru/vtosters/lite/music/cache/injectors/PlaylistInjector.java @@ -0,0 +1,80 @@ +package ru.vtosters.lite.music.cache.injectors; + +import android.os.RemoteException; +import android.text.TextUtils; + +import com.vk.api.audio.AudioGetPlaylist; +import com.vk.dto.music.MusicTrack; +import com.vk.dto.music.Playlist; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.List; + +import bruhcollective.itaysonlab.libvkx.client.LibVKXClient; +import io.reactivex.Observable; +import io.reactivex.ObservableOnSubscribe; +import ru.vtosters.lite.music.MP3Downloader; +import ru.vtosters.lite.music.cache.CacheDatabaseDelegate; +import ru.vtosters.lite.music.cache.helpers.PlaylistHelper; +import ru.vtosters.lite.music.cache.helpers.TracklistHelper; +import ru.vtosters.lite.utils.NetworkUtils; + +public class PlaylistInjector { + + public final static String CHANNEL_NAME = "VTCH"; + + public static void injectDownloadPlaylist(Playlist playlist) { + MP3Downloader.downloadPlaylist(playlist); + } + + public static boolean eligibleForOfflineCaching() { + return !NetworkUtils.isNetworkConnected() && CacheDatabaseDelegate.hasTracks(); + } + + public static Observable injectGetPlaylist(AudioGetPlaylist audioGetPlaylist) { + try { + if (!eligibleForOfflineCaching()) + return null; + var requestArgs = audioGetPlaylist.b(); + var id = requestArgs.get("id"); + if (TextUtils.isEmpty(id) || !id.equals("-1") && !id.equals("-2")) + return null; + + return Observable.a((ObservableOnSubscribe) observableEmitter -> { + AudioGetPlaylist.c response = new AudioGetPlaylist.c(); + if (LibVKXClient.isIntegrationEnabled()) { + response.b = PlaylistHelper.createCachedPlaylistMetadata(); + LibVKXClient.getInstance().runOnService((service) -> { + try { + List cache = service.getCache(); + ArrayList tracks = new ArrayList<>(); + for (String json : cache) { + tracks.add(new MusicTrack(new JSONObject(json))); + } + response.c = tracks; + } catch (RemoteException | JSONException e) { + e.printStackTrace(); + response.c = new ArrayList<>(); + } + observableEmitter.b(response); + }); + return; + } + if (id.equals("-1")) { + response.c = (ArrayList) TracklistHelper.getTracks(); + response.b = PlaylistHelper.createCachedPlaylistMetadata(); + } else { + response.c = (ArrayList) TracklistHelper.getTracks(requestArgs.get("owner_id")); + response.b = PlaylistHelper.createCachedPlaylistMetadata(requestArgs.get("owner_id")); + } + observableEmitter.b(response); + }); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } +} diff --git a/app/src/main/java/ru/vtosters/lite/music/cache/injectors/TracklistInjector.java b/app/src/main/java/ru/vtosters/lite/music/cache/injectors/TracklistInjector.java new file mode 100644 index 000000000..0a30c3449 --- /dev/null +++ b/app/src/main/java/ru/vtosters/lite/music/cache/injectors/TracklistInjector.java @@ -0,0 +1,197 @@ +package ru.vtosters.lite.music.cache.injectors; + +import com.vk.catalog2.core.CatalogParser; +import com.vk.catalog2.core.api.dto.CatalogResponse; +import com.vk.dto.music.MusicTrack; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import bruhcollective.itaysonlab.libvkx.client.LibVKXClient; +import io.reactivex.Observable; +import io.reactivex.ObservableOnSubscribe; +import ru.vtosters.lite.music.cache.CacheDatabaseDelegate; +import ru.vtosters.lite.music.cache.helpers.PlaylistHelper; +import ru.vtosters.lite.music.cache.helpers.TracklistHelper; +import ru.vtosters.lite.utils.AccountManagerUtils; +import ru.vtosters.lite.utils.NetworkUtils; + +public class TracklistInjector { + + public static boolean eligibleForOfflineCaching() { + return !NetworkUtils.isNetworkConnected() && CacheDatabaseDelegate.hasTracks(); + } + + public static Observable createOfflineRx(CatalogParser parser) { + return Observable.a((ObservableOnSubscribe) observableEmitter -> { + if (LibVKXClient.isIntegrationEnabled()) { + LibVKXClient.getInstance().runOnService((service) -> { + try { + List tracks = new ArrayList<>(); + List cache = service.getCache(); + + for (String json : cache) { + tracks.add(new MusicTrack(new JSONObject(json))); + } + + observableEmitter.b(parser.c(createVirtualCatalog(tracks))); + } catch (Exception e) { + e.printStackTrace(); + } + }); + } else { + observableEmitter.b(parser.c(createVirtualCatalog(TracklistHelper.getTracks()))); + } + }); + } + + public static void injectIntoExistingCatalog(JSONObject catalogNode) { + if (!CacheDatabaseDelegate.hasTracks()) return; + try { + var sectionsNode = catalogNode.getJSONObject("catalog") + .getJSONArray("sections"); + var firstSection = sectionsNode.getJSONObject(0); + if (!firstSection.getString("url").equals("https://vk.com/audios" + AccountManagerUtils.getUserId() + "?section=all")) + return; + + var foundPlaylistBlock = false; + var suitableIndex = 0; + + var blocks = firstSection.getJSONArray("blocks"); + for (int i = 0; i < blocks.length(); i++) { + var block = blocks.getJSONObject(i); + var dataType = block.getString("data_type"); + if ("music_audios".equals(dataType)) { + suitableIndex = i - 1; + continue; + } + + if (!"music_playlists".equals(dataType)) + continue; + + var playlistIds = block.getJSONArray("playlists_ids"); + var newPlaylistIds = new JSONArray() + .put(AccountManagerUtils.getUserId() + "_-1"); + + for (int j = 0; j < playlistIds.length(); j++) + newPlaylistIds.put(playlistIds.get(j)); + + block.put("playlists_ids", newPlaylistIds); + + // need test + blocks.remove(i); + blocks.put(i, block); + + foundPlaylistBlock = true; + } + + if (foundPlaylistBlock) { + var playlists = catalogNode.getJSONArray("playlists") + .put(0, PlaylistHelper.createCachedPlaylistMetadata().J()); + catalogNode.put("playlists", playlists); + return; + } + + var ids = new JSONArray() + .put(0, AccountManagerUtils.getUserId() + "_-1"); + + var playlistsBlock = new JSONObject() + .put("id", getRandomId()) + .put("data_type", "music_playlists") + .put("url", "synth:cache:playlists") + .put("playlists_ids", ids); + + var layout = new JSONObject() + .put("name", "large_slider") + .put("owner_id", AccountManagerUtils.getUserId()); + + playlistsBlock.put("layout", layout); + + blocks.put(suitableIndex, playlistsBlock); + + firstSection.put("blocks", blocks); + + var playlists = new JSONArray() + .put(0, PlaylistHelper.createCachedPlaylistMetadata().J()); + catalogNode.put("playlists", playlists); + } catch (JSONException e) { + e.printStackTrace(); + } + } + + public static JSONObject createVirtualCatalog(List tracks) throws JSONException { + var clickAction = new JSONObject() + .put("type", "open_url") + .put("target", "internal") + .put("url", "https://vkx.app/index.html?app=vt_ctg"); + + var catalogBanner = new JSONObject() + .put("id", 1234) + .put("image_mode", "cover") + .put("title", "Включите интеграцию VK X!") + .put("text", "Получите доступ к хранилищу треков и возможности включать/кешировать треки из VTosters в VK X.\n\n" + + "Подробнее - в Настройки > Настройки VTosters > Кешированные треки.") + .put("click_action", new JSONObject().put("action", clickAction)); + var catalogBanners = new JSONArray() + .put(catalogBanner); + + var audiosBlock = new JSONObject() + .put("id", getRandomId()) + .put("data_type", "music_audios") + .put("url", "synth:cache:list") + .put("audios_ids", TracklistHelper.tracksToIds(tracks)); + + var layout = new JSONObject() + .put("name", "list") + .put("owner_id", AccountManagerUtils.getUserId()); + + audiosBlock.put("layout", layout); + + var blocks = new JSONArray() + .put(audiosBlock); + +// var vkxPostirony = new JSONObject() +// .put("id", getRandomId()) +// .put("data_type", "catalog_banners") +// .put("url", "synth:cache:vkx") +// .put("catalog_banner_ids", new JSONArray().put(1234)); +// +// var vkxlayout = new JSONObject() +// .put("name", "large_list") +// .put("owner_id", Globals.getUserId()) +// .put("infinite_repeat", true); +// +// vkxPostirony.put("layout", vkxlayout); +// +// if (eligibleForVkxAd()) +// blocks.put(vkxPostirony); + var randomId = getRandomId(); + + var section = new JSONObject() + .put("id", randomId) + .put("title", "Сохраненные треки") + .put("url", "synth:cache") + .put("blocks", blocks); + + var sections = new JSONArray() + .put(section); + + var catalog = new JSONObject() + .put("sections", sections) + .put("default_section", randomId); + + return new JSONObject() + .put("catalog", catalog) + .put("audios", TracklistHelper.tracksToJsons(tracks)) + .put("catalog_banners", catalogBanners); + } + + private static String getRandomId() { + return UUID.randomUUID().toString(); + } +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 000000000..7454180f2 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/scripts/apksigner.jar b/scripts/apksigner.jar new file mode 100644 index 000000000..2c1593765 Binary files /dev/null and b/scripts/apksigner.jar differ diff --git a/scripts/apktool.jar b/scripts/apktool.jar new file mode 100644 index 000000000..2966f4277 Binary files /dev/null and b/scripts/apktool.jar differ diff --git a/scripts/apktool.jar.REMOVED.git-id b/scripts/apktool.jar.REMOVED.git-id deleted file mode 100644 index 2b4d468c9..000000000 --- a/scripts/apktool.jar.REMOVED.git-id +++ /dev/null @@ -1 +0,0 @@ -2966f42770fa9480b905416ecea04a6c4b401193 \ No newline at end of file diff --git a/scripts/framework/1.apk b/scripts/framework/1.apk new file mode 100755 index 000000000..0840244ca Binary files /dev/null and b/scripts/framework/1.apk differ diff --git a/scripts/framework/1.apk.REMOVED.git-id b/scripts/framework/1.apk.REMOVED.git-id deleted file mode 100644 index a6621eba5..000000000 --- a/scripts/framework/1.apk.REMOVED.git-id +++ /dev/null @@ -1 +0,0 @@ -0840244ca18d071426e1a4cf350bdb0d446ca68f \ No newline at end of file diff --git a/smali/lib/armeabi-v7a/libvoip_armv7-a-neon.so b/smali/lib/armeabi-v7a/libvoip_armv7-a-neon.so new file mode 100644 index 000000000..6278c6512 Binary files /dev/null and b/smali/lib/armeabi-v7a/libvoip_armv7-a-neon.so differ diff --git a/smali/lib/armeabi-v7a/libvoip_armv7-a-neon.so.REMOVED.git-id b/smali/lib/armeabi-v7a/libvoip_armv7-a-neon.so.REMOVED.git-id deleted file mode 100644 index 5c673ecff..000000000 --- a/smali/lib/armeabi-v7a/libvoip_armv7-a-neon.so.REMOVED.git-id +++ /dev/null @@ -1 +0,0 @@ -6278c6512808b17c9470c0b231c40ab1d01ef877 \ No newline at end of file diff --git a/smali/smali_classes2/com/vk/catalog2/core/api/music/CatalogGetAudio.smali b/smali/smali_classes2/com/vk/catalog2/core/api/music/CatalogGetAudio.smali index d76a16fd7..b316ba534 100644 --- a/smali/smali_classes2/com/vk/catalog2/core/api/music/CatalogGetAudio.smali +++ b/smali/smali_classes2/com/vk/catalog2/core/api/music/CatalogGetAudio.smali @@ -89,6 +89,8 @@ invoke-static {p1, v1}, Lkotlin/jvm/internal/Intrinsics;->a(Ljava/lang/Object;Ljava/lang/String;)V + invoke-static/range {p1 .. p1}, Lru/vtosters/lite/music/cache/injectors/TracklistInjector;->injectIntoExistingCatalog(Lorg/json/JSONObject;)V + invoke-virtual {v0, p1}, Lcom/vk/catalog2/core/CatalogParser;->c(Lorg/json/JSONObject;)Lcom/vk/catalog2/core/api/dto/CatalogResponse; move-result-object p1 diff --git a/smali/smali_classes3/com/vk/music/bottomsheets/playlist/PlaylistBottomSheetClickListener.smali b/smali/smali_classes3/com/vk/music/bottomsheets/playlist/PlaylistBottomSheetClickListener.smali index 3b77aec8a..94894ecc0 100644 --- a/smali/smali_classes3/com/vk/music/bottomsheets/playlist/PlaylistBottomSheetClickListener.smali +++ b/smali/smali_classes3/com/vk/music/bottomsheets/playlist/PlaylistBottomSheetClickListener.smali @@ -153,7 +153,7 @@ iget-object v0, p0, Lcom/vk/music/bottomsheets/playlist/PlaylistBottomSheetClickListener;->e:Lcom/vk/dto/music/Playlist; - invoke-interface {p1, p2, v0}, Lcom/vk/music/bottomsheets/playlist/PlaylistBottomSheetModel;->a(Landroid/content/Context;Lcom/vk/dto/music/Playlist;)V + invoke-static/range {v0 .. v0}, Lru/vtosters/lite/music/cache/injectors/PlaylistInjector;->injectDownloadPlaylist(Lcom/vk/dto/music/Playlist;)V goto/16 :goto_0 diff --git a/smali/smali_classes3/com/vk/music/e/MusicCatalogConfiguration.smali b/smali/smali_classes3/com/vk/music/e/MusicCatalogConfiguration.smali index c5602966e..e8c1f541d 100644 --- a/smali/smali_classes3/com/vk/music/e/MusicCatalogConfiguration.smali +++ b/smali/smali_classes3/com/vk/music/e/MusicCatalogConfiguration.smali @@ -65,6 +65,23 @@ } .end annotation + invoke-static {}, Lru/vtosters/lite/music/cache/injectors/TracklistInjector;->eligibleForOfflineCaching()Z + + move-result v0 + + if-eqz v0, :cond_custom + + invoke-virtual {p0}, Lcom/vk/catalog2/core/VkCatalogConfiguration;->j()Lcom/vk/catalog2/core/CatalogParser; + + move-result-object v0 + + invoke-static {v0}, Lru/vtosters/lite/music/cache/injectors/TracklistInjector;->createOfflineRx(Lcom/vk/catalog2/core/CatalogParser;)Lio/reactivex/Observable; + + move-result-object v0 + + return-object v0 + + :cond_custom .line 1 new-instance v0, Lcom/vk/catalog2/core/api/music/CatalogGetAudio; diff --git a/smali/smali_classes3/com/vk/music/playlist/ModernPlaylistModel.smali b/smali/smali_classes3/com/vk/music/playlist/ModernPlaylistModel.smali index 1dc8f5ad7..2536b751f 100644 --- a/smali/smali_classes3/com/vk/music/playlist/ModernPlaylistModel.smali +++ b/smali/smali_classes3/com/vk/music/playlist/ModernPlaylistModel.smali @@ -239,11 +239,23 @@ const/4 v1, 0x1 - .line 14 + #invoke-static {p1}, Lru/vtosters/lite/music/cache/injectors/PlaylistInjector;->injectGetPlaylist(Lcom/vk/api/audio/AudioGetPlaylist;)Lio/reactivex/Observable; + + #move-result-object v2 + + #if-eqz v2, :cond_custom + + #move-object p1, v2 + + #goto :goto_20 + + #:cond_custom + invoke-static {p1, v0, v1, v0}, Lcom/vk/api/base/ApiRequest;->d(Lcom/vk/api/base/ApiRequest;Lcom/vk/api/base/ApiThreadHolder;ILjava/lang/Object;)Lio/reactivex/Observable; move-result-object p1 + :goto_20 .line 15 new-instance v6, Lcom/vk/music/playlist/ModernPlaylistModel$d; diff --git a/smali/smali_classes3/com/vk/music/playlist/modern/MusicPlaylistContract4.smali b/smali/smali_classes3/com/vk/music/playlist/modern/MusicPlaylistContract4.smali index 02bb4e10d..c317eaef5 100644 --- a/smali/smali_classes3/com/vk/music/playlist/modern/MusicPlaylistContract4.smali +++ b/smali/smali_classes3/com/vk/music/playlist/modern/MusicPlaylistContract4.smali @@ -593,6 +593,10 @@ move-result-object v0 + invoke-static/range {v0 .. v0}, Lru/vtosters/lite/music/cache/injectors/PlaylistInjector;->injectDownloadPlaylist(Lcom/vk/dto/music/Playlist;)V + + return-void + if-eqz v0, :cond_1 .line 3 diff --git a/smali/smali_classes4/com/vtosters/lite/audio/player/CacheUtils.smali b/smali/smali_classes4/com/vtosters/lite/audio/player/CacheUtils.smali index b272e291f..d79b7b845 100644 --- a/smali/smali_classes4/com/vtosters/lite/audio/player/CacheUtils.smali +++ b/smali/smali_classes4/com/vtosters/lite/audio/player/CacheUtils.smali @@ -8,6 +8,12 @@ .locals 10 .line 5 + invoke-static/range {p1 .. p1}, Lru/vtosters/lite/music/cache/FileCacheImplementation;->getTrackFile(Ljava/lang/String;)Ljava/io/File; + + move-result-object v0 + + return-object v0 + invoke-static {p0}, Landroidx/core/content/ContextCompat;->getExternalCacheDirs(Landroid/content/Context;)[Ljava/io/File; move-result-object v0 diff --git a/smali/smali_classes4/com/vtosters/lite/audio/utils/Utils.smali b/smali/smali_classes4/com/vtosters/lite/audio/utils/Utils.smali index e361cdfc3..6ae3c2f6a 100644 --- a/smali/smali_classes4/com/vtosters/lite/audio/utils/Utils.smali +++ b/smali/smali_classes4/com/vtosters/lite/audio/utils/Utils.smali @@ -452,6 +452,8 @@ .method public static a([BIIJ)V .locals 10 + return-void + move-wide v0, p3 move p3, p1