From 9d3e7c53de9fe1ef4087071030c6f47211693465 Mon Sep 17 00:00:00 2001 From: gdlbo <41114720+gdlbo@users.noreply.github.com> Date: Thu, 28 Jul 2022 00:15:18 +0300 Subject: [PATCH] lastfm test --- .../libvkx/client/LibVKXClient.java | 21 +-- .../lite/hooks/StartFragmentHook.java | 4 +- .../ru/vtosters/lite/music/Scrobbler.java | 126 ++++++++++++++++++ .../ui/components/DockBarEditorManager.java | 4 +- .../lite/ui/fragments/MediaFragment.java | 57 +++++++- .../ru/vtosters/lite/utils/AndroidUtils.java | 22 +++ .../ru/vtosters/lite/utils/Preferences.java | 4 +- smali/res/xml/preferences_media.xml | 14 +- .../com/vtosters/lite/MainActivity.smali | 2 +- 9 files changed, 231 insertions(+), 23 deletions(-) create mode 100644 app/src/main/java/ru/vtosters/lite/music/Scrobbler.java diff --git a/app/src/main/java/bruhcollective/itaysonlab/libvkx/client/LibVKXClient.java b/app/src/main/java/bruhcollective/itaysonlab/libvkx/client/LibVKXClient.java index 3c2623517..5be09b2e8 100644 --- a/app/src/main/java/bruhcollective/itaysonlab/libvkx/client/LibVKXClient.java +++ b/app/src/main/java/bruhcollective/itaysonlab/libvkx/client/LibVKXClient.java @@ -1,8 +1,10 @@ package bruhcollective.itaysonlab.libvkx.client; +import static ru.vtosters.lite.music.Scrobbler.scrobbleTrack; import static ru.vtosters.lite.utils.AndroidUtils.getGlobalContext; import static ru.vtosters.lite.utils.Preferences.getBoolValue; +import android.annotation.SuppressLint; import android.content.Context; import android.os.RemoteException; @@ -14,22 +16,23 @@ import java.util.List; import bruhcollective.itaysonlab.libvkx.ILibVkxService; -public class LibVKXClient { +public class LibVKXClient{ + @SuppressLint("StaticFieldLeak") private static LibVKXClientImpl impl; - public static LibVKXClientImpl getInstance(Context context) { + public static LibVKXClientImpl getInstance(Context context){ if (impl == null) { impl = new LibVKXClientImpl(context); } return impl; } - public static LibVKXClientImpl getInstance() { + public static LibVKXClientImpl getInstance(){ LibVKXClientImpl libVKXClientImpl = impl; return libVKXClientImpl == null ? getInstance(getGlobalContext()) : libVKXClientImpl; } - public static boolean isVkxInstalled() { + public static boolean isVkxInstalled(){ try { getGlobalContext().getPackageManager().getPackageInfo("ua.itaysonlab.vkx", 0); return true; @@ -38,11 +41,13 @@ public class LibVKXClient { } } // VKX check - public static boolean isIntegrationEnabled() { + public static boolean isIntegrationEnabled(){ return getBoolValue("libvkx_integration", false) && isVkxInstalled(); } - public static boolean play(MusicTrack musicTrack, List list, MusicPlaybackLaunchContext playerRefer) { + public static boolean play(MusicTrack musicTrack, List list, MusicPlaybackLaunchContext playerRefer){ + scrobbleTrack(musicTrack.h, musicTrack.C, musicTrack.f, musicTrack.y1()); + if (!isIntegrationEnabled()) { return false; } @@ -52,7 +57,7 @@ public class LibVKXClient { return getInstance().runOnService(action); } - public static void lambdaplay(List list, MusicTrack musicTrack, ILibVkxService iLibVkxService) { + public static void lambdaplay(List list, MusicTrack musicTrack, ILibVkxService iLibVkxService){ int indexOf = list.indexOf(musicTrack); var ids = new ArrayList(); for (var track : list) { @@ -69,7 +74,7 @@ public class LibVKXClient { } } - private static String asId(MusicTrack musicTrack) { + private static String asId(MusicTrack musicTrack){ String str = musicTrack.e + "_" + musicTrack.d; if (musicTrack.O == null || musicTrack.O.equals("")) { return str; diff --git a/app/src/main/java/ru/vtosters/lite/hooks/StartFragmentHook.java b/app/src/main/java/ru/vtosters/lite/hooks/StartFragmentHook.java index 40f9976ea..46b035576 100644 --- a/app/src/main/java/ru/vtosters/lite/hooks/StartFragmentHook.java +++ b/app/src/main/java/ru/vtosters/lite/hooks/StartFragmentHook.java @@ -6,7 +6,7 @@ import static ru.vtosters.lite.utils.Preferences.getBoolValue; import static ru.vtosters.lite.utils.Preferences.milkshake; import static ru.vtosters.lite.utils.Preferences.superapp; import static ru.vtosters.lite.utils.Preferences.useNewSettings; -import static ru.vtosters.lite.utils.Preferences.videoewcatalog; +import static ru.vtosters.lite.utils.Preferences.videonewcatalog; import static ru.vtosters.lite.utils.Preferences.vkme; import com.vk.apps.AppsFragment; @@ -53,7 +53,7 @@ public class StartFragmentHook { case "photos": return PhotosFragment.class; case "videos": - return videoewcatalog() ? VideoCatalogFragment.class : VideosFragment.class; + return videonewcatalog() ? VideoCatalogFragment.class : VideosFragment.class; case "settings": return useNewSettings(); case "apps": diff --git a/app/src/main/java/ru/vtosters/lite/music/Scrobbler.java b/app/src/main/java/ru/vtosters/lite/music/Scrobbler.java new file mode 100644 index 000000000..b3a8c70dd --- /dev/null +++ b/app/src/main/java/ru/vtosters/lite/music/Scrobbler.java @@ -0,0 +1,126 @@ +package ru.vtosters.lite.music; +import static java.lang.String.valueOf; +import static ru.vtosters.lite.utils.AndroidUtils.MD5; +import static ru.vtosters.lite.utils.AndroidUtils.edit; +import static ru.vtosters.lite.utils.AndroidUtils.getDefaultPrefs; +import static ru.vtosters.lite.utils.AndroidUtils.getPreferences; +import static ru.vtosters.lite.utils.AndroidUtils.sendToast; + +import android.util.Log; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Map; +import java.util.TreeMap; + +import ru.vtosters.lite.net.NetCall; +import ru.vtosters.lite.net.NetCallback; +import ru.vtosters.lite.net.NetClient; +import ru.vtosters.lite.net.NetRequest; +import ru.vtosters.lite.net.NetResponse; +import ru.vtosters.lite.utils.Preferences; + +public class Scrobbler{ + public static final String key = "5965d63402414776c54c266db0211746"; + public static final String keysecret = "d982180eed13275bb948e41cf225f88f"; + public static final String url = "https://ws.audioscrobbler.com/2.0/"; + private static final NetClient client = new NetClient.Builder().build(); + public static ArrayList scrobbled = new ArrayList<>(); + + public static void scrobbleTrack(long duration, String artist, String title, String uid){ + var string = getDefaultPrefs().getString("sessionKey", null); + var timestamp = System.currentTimeMillis() / 1000; + + Log.d("Scrobbler", "scrobbleTrack: " + title + " - " + artist + " - " + duration + " - " + uid); + + if (string == null || !scrobbled.contains(uid) && !getPreferences().getBoolean("lastfm_enabled", false)) { + return; + } + + scrobbled.add(uid); + + TreeMap treeMap = new TreeMap<>(); + treeMap.put("method", "track.scrobble"); + treeMap.put("sk", string); + treeMap.put("artist[0]", artist); + treeMap.put("track[0]", title); + treeMap.put("timestamp[0]", valueOf(timestamp)); + treeMap.put("api_key", key); + treeMap.put("duration[0]", String.valueOf(duration)); + treeMap.put("api_sig", signMD5(treeMap)); + treeMap.put("format", "json"); + + client.newCall(new NetRequest.Builder().url(url).post().params(treeMap).build()).enqueue(new NetCallback(){ + @Override + public void onFailure(NetCall call, IOException e){ + Log.d("Scrobbler", "Error: " + e.getMessage()); + } + + @Override + public void onResponse(NetCall call, NetResponse response){ + Log.d("Scrobbler", "Scrobbled: " + artist + " - " + title); + if (Preferences.dev()) Log.d("Scrobbler", "Response scrobble: " + response.getDataString()); + } + }); + } + + private static String signMD5(Map map){ + var sb = new StringBuilder(); + + for (Map.Entry entry : map.entrySet()) { + sb.append(entry.getKey()); + sb.append(entry.getValue()); + } + sb.append(keysecret); + + return MD5(sb.toString()); + } + + public static void auth(String login, String pass){ + TreeMap treeMap = new TreeMap<>(); + treeMap.put("method", "auth.getMobileSession"); + treeMap.put("api_key", key); + treeMap.put("username", login); + treeMap.put("password", pass); + treeMap.put("api_sig", signMD5(treeMap)); + treeMap.put("format", "json"); + + client.newCall(new NetRequest.Builder().url(url).post().params(treeMap).build()).enqueue(new NetCallback(){ + @Override + public void onFailure(NetCall call, IOException e){ + Log.d("Scrobbler", "Auth failed", e); + sendToast("Ошибка входа"); + } + + @Override + public void onResponse(NetCall call, NetResponse response){ + try { + Log.d("Scrobbler", "Auth " + response.getDataString()); + JSONObject jSONObject = new JSONObject(response.getDataString()).getJSONObject("session"); + String string = jSONObject.getString("name"); + getDefaultPrefs().edit().putString("username", string).putString("sessionKey", jSONObject.getString("key")).apply(); + sendToast("Успешно вошли как " + string); + } catch (JSONException e) { + e.printStackTrace(); + } + } + }); + } + + public static void reset(){ + edit().remove("username").apply(); + edit().remove("sessionKey").apply(); + sendToast("Вы успешно вышли из учетной записи"); + } + + private static String getSessionKey(){ + return getDefaultPrefs().getString("sessionKey", null); + } + + public static String getUserName(){ + return !getSessionKey().isEmpty() ? getDefaultPrefs().getString("username", null) : null; + } +} diff --git a/app/src/main/java/ru/vtosters/lite/ui/components/DockBarEditorManager.java b/app/src/main/java/ru/vtosters/lite/ui/components/DockBarEditorManager.java index 4711395fc..36df13409 100644 --- a/app/src/main/java/ru/vtosters/lite/ui/components/DockBarEditorManager.java +++ b/app/src/main/java/ru/vtosters/lite/ui/components/DockBarEditorManager.java @@ -6,7 +6,7 @@ import static ru.vtosters.lite.utils.Preferences.getBoolValue; import static ru.vtosters.lite.utils.Preferences.milkshake; import static ru.vtosters.lite.utils.Preferences.superapp; import static ru.vtosters.lite.utils.Preferences.useNewSettings; -import static ru.vtosters.lite.utils.Preferences.videoewcatalog; +import static ru.vtosters.lite.utils.Preferences.videonewcatalog; import static ru.vtosters.lite.utils.Preferences.vkme; import static ru.vtosters.lite.utils.Preferences.vkme_notifs; @@ -197,7 +197,7 @@ public class DockBarEditorManager { R.drawable.ic_video_outline_28, R.string.videos, R.id.menu_videos, - videoewcatalog() ? VideoCatalogFragment.class : VideosFragment.class); + videonewcatalog() ? VideoCatalogFragment.class : VideosFragment.class); case "tab_lives": return DockBarTab.valuesOf( "tab_lives", diff --git a/app/src/main/java/ru/vtosters/lite/ui/fragments/MediaFragment.java b/app/src/main/java/ru/vtosters/lite/ui/fragments/MediaFragment.java index b8b0f2986..5e656445d 100644 --- a/app/src/main/java/ru/vtosters/lite/ui/fragments/MediaFragment.java +++ b/app/src/main/java/ru/vtosters/lite/ui/fragments/MediaFragment.java @@ -1,5 +1,7 @@ package ru.vtosters.lite.ui.fragments; +import static ru.vtosters.lite.ui.PreferencesUtil.getSTextColor; +import static ru.vtosters.lite.utils.AndroidUtils.dp2px; import static ru.vtosters.lite.utils.AndroidUtils.edit; import static ru.vtosters.lite.utils.AndroidUtils.getIdentifier; import static ru.vtosters.lite.utils.AndroidUtils.getPreferences; @@ -13,6 +15,7 @@ import android.content.DialogInterface; import android.content.res.ColorStateList; import android.os.Bundle; import android.view.LayoutInflater; +import android.view.ViewGroup; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.ListView; @@ -26,6 +29,7 @@ import com.vtosters.lite.general.fragments.MaterialPreferenceToolbarFragment; import java.util.Arrays; import ru.vtosters.lite.downloaders.VideoDownloader; +import ru.vtosters.lite.music.Scrobbler; import ru.vtosters.lite.ui.adapters.ImagineArrayAdapter; public class MediaFragment extends MaterialPreferenceToolbarFragment { @@ -37,21 +41,52 @@ public class MediaFragment extends MaterialPreferenceToolbarFragment { LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT); - input.setLayoutParams(lp); - input.setTextColor(getTextAttr()); - input.setBackgroundTintList(ColorStateList.valueOf(getAccentColor())); - alertDialog.setView(input); - alertDialog.setPositiveButton("Скачать", (dialog, which) -> VideoDownloader.parseVideoLink(input.getText().toString(), ctx)); - var alert = alertDialog.create(); - alert.show(); + alert.getButton(DialogInterface.BUTTON_POSITIVE).setTextColor(getAccentColor()); + } + public static void lastfmAuth(Context ctx) { + LinearLayout linearLayout = new LinearLayout(ctx); + linearLayout.setOrientation(LinearLayout.VERTICAL); + + final EditText fn = new EditText(ctx); + fn.setHint("Логин"); + fn.setTextColor(getTextAttr()); + fn.setHintTextColor(getSTextColor(ctx)); + fn.setBackgroundTintList(ColorStateList.valueOf(getAccentColor())); + linearLayout.addView(fn); + fn.getLayoutParams().width = ViewGroup.LayoutParams.MATCH_PARENT; + ViewGroup.MarginLayoutParams margin = ((ViewGroup.MarginLayoutParams) fn.getLayoutParams()); + margin.setMargins(dp2px(20f), 0, dp2px(20f), 0); + fn.setLayoutParams(margin); + + final EditText ln = new EditText(ctx); + ln.setHint("Пароль"); + ln.setTextColor(getTextAttr()); + ln.setHintTextColor(getSTextColor(ctx)); + ln.setBackgroundTintList(ColorStateList.valueOf(getAccentColor())); + linearLayout.addView(ln); + ln.getLayoutParams().width = ViewGroup.LayoutParams.MATCH_PARENT; + ln.setLayoutParams(margin); + + AlertDialog.Builder alertDialog = new AlertDialog.Builder(ctx, getAlertStyle()); + alertDialog.setTitle("Введите логин и пароль"); + + alertDialog.setView(linearLayout); + alertDialog.setPositiveButton("Войти", (dialog, which) -> { + String login = fn.getText().toString(); + String pass = ln.getText().toString(); + + Scrobbler.auth(login, pass); + }); + var alert = alertDialog.create(); + alert.show(); alert.getButton(DialogInterface.BUTTON_POSITIVE).setTextColor(getAccentColor()); } @@ -65,6 +100,14 @@ public class MediaFragment extends MaterialPreferenceToolbarFragment { private void prefs() { findPreference("download_video").setOnPreferenceClickListener(new MediaFragment.download()); findPreference("dateformat").setOnPreferenceChangeListener(new MediaFragment.restart()); + findPreference("lastfm_auth").setOnPreferenceClickListener(preference -> { + lastfmAuth(getContext()); + return true; + }); + findPreference("lastfm_reset").setOnPreferenceClickListener(preference -> { + Scrobbler.reset(); + return true; + }); findPreference("select_photo_search_engine").setOnPreferenceClickListener(preference -> { var items = Arrays.asList( new ImagineArrayAdapter.ImagineArrayAdapterItem(getIdentifier("yandex", "drawable"), "Yandex"), diff --git a/app/src/main/java/ru/vtosters/lite/utils/AndroidUtils.java b/app/src/main/java/ru/vtosters/lite/utils/AndroidUtils.java index 3523ca688..982b3042d 100644 --- a/app/src/main/java/ru/vtosters/lite/utils/AndroidUtils.java +++ b/app/src/main/java/ru/vtosters/lite/utils/AndroidUtils.java @@ -15,6 +15,8 @@ import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.StringRes; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.util.Locale; public class AndroidUtils { @@ -115,4 +117,24 @@ public class AndroidUtils { public static void sendToast(String text) { Toast.makeText(getGlobalContext(), text, Toast.LENGTH_SHORT).show(); } + + public static String MD5(String s){ + try { + var messageDigest = MessageDigest.getInstance("MD5"); + var sb = new StringBuilder(); + messageDigest.update(s.getBytes()); + byte[] digest = messageDigest.digest(); + for (byte b : digest) { + String hexString = Integer.toHexString(b & 255); + while(hexString.length() < 2) { + hexString = "0" + hexString; + } + sb.append(hexString); + } + return sb.toString(); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + return null; + } + } } \ No newline at end of file diff --git a/app/src/main/java/ru/vtosters/lite/utils/Preferences.java b/app/src/main/java/ru/vtosters/lite/utils/Preferences.java index 68ecf8566..12a90477f 100644 --- a/app/src/main/java/ru/vtosters/lite/utils/Preferences.java +++ b/app/src/main/java/ru/vtosters/lite/utils/Preferences.java @@ -211,8 +211,8 @@ public class Preferences { return getBoolValue("musicnewcatalog", true); } - public static boolean videoewcatalog() { - return getBoolValue("videoewcatalog", true); + public static boolean videonewcatalog() { + return getBoolValue("videonewcatalog", true); } public static boolean postsredesign() { diff --git a/smali/res/xml/preferences_media.xml b/smali/res/xml/preferences_media.xml index 52d2e7bd9..0721d6dff 100644 --- a/smali/res/xml/preferences_media.xml +++ b/smali/res/xml/preferences_media.xml @@ -36,6 +36,18 @@ android:summary="Отключение значительно улучшит качество загружаемых видео" android:title="@string/sett_compress_videos" /> + + + + + onCreate(Landroid/os/Bundle;)V - invoke-static {p0}, Lru/vtosters/lite/ui/components/Crashreporter;->init(Landroid/app/Activity;)V + invoke-static {p0}, Lru/vtosters/lite/ui/components/CrashReporter;->init(Landroid/app/Activity;)V invoke-static {p0}, Lru/vtosters/lite/utils/VTVerifications;->load(Landroid/content/Context;)V