Compare commits

...
441 Commits
Author SHA1 Message Date
gdlbo efb909ce64 fix artists catalog 2023-02-12 14:24:17 +03:00
Saoqqandsaoq 62c11b90dc Forward calls from msg to off VK apps (#1094)
Co-authored-by: saoq <wfmfe@ya.ru>
2023-02-12 02:18:27 +03:00
gdlbo a15cd9bd0b fixed compressing jpegs 2023-02-08 18:32:58 +03:00
gdlbo 6d9f4a23c8 added newsfeed update dialog 2023-02-08 18:00:36 +03:00
gdlbo 0677f77fde Update BackupManager.java 2023-02-08 17:55:26 +03:00
gdlbo 1b274fdb91 Update DockBarEditorManager.java 2023-02-08 17:08:22 +03:00
gdlbo c4bf9f8f88 some switch fixes 2023-02-08 16:41:56 +03:00
gdlbo e9a98afae9 Update ThemesCore.java 2023-02-07 01:35:36 +03:00
gdlbo 856bd63b43 updated VTSettings.java 2023-02-07 00:48:35 +03:00
gdlbo 606c908b90 some color fixes 2023-02-07 00:09:52 +03:00
gdlbo 0adc9ca3fd updated switch cases 2023-02-06 21:47:33 +03:00
gdlbo 3345382383 jdk upped to 14 2023-02-06 21:35:48 +03:00
gdlbo 7e7f8d0efa Update InterfaceFragment.java 2023-02-06 20:00:17 +03:00
gdlbo 84e0601b76 refactoring 2023-02-06 02:51:32 +03:00
gdlbo bc43b788d8 Update ThemesCore.java 2023-02-06 02:19:36 +03:00
gdlbo 22bd830353 fix #1062 2023-02-06 01:03:31 +03:00
gdlbo c2ff38044c fixed проксi moment 2023-02-05 23:53:28 +03:00
Pavel af312540df Escape character in values-be (#1088) 2023-02-04 21:44:00 +03:00
mysh228 1cee582dbf Apostrophes fixes in arrays 2023-02-04 00:52:05 +03:00
mysh228 664ea02dd0 Apostrophes fix 2023-02-04 00:52:05 +03:00
mysh228 4fe85d264d Belarusian translation 2023-02-04 00:52:05 +03:00
gdlbo e8a1d814c1 fixed false injection 2023-02-03 16:07:42 +03:00
gdlbo 76d0dd2ad6 disabled vigo 2023-02-03 15:56:17 +03:00
gdlbo 4658338fbb Update strings.xml 2023-02-03 15:25:07 +03:00
awers24 745ccd6e1d ukrainian translate (#1083)
* ukrainian translation
2023-02-02 20:52:24 +03:00
k1zn da76da8e35 Update strings.xml 2023-02-01 20:25:08 +05:00
gdlbo 249d1de1a1 Update R.java 2023-01-31 14:50:54 +03:00
gdlbo 1a82b40a7f Update RandomProxy.java 2023-01-31 14:43:01 +03:00
k1zn 83093be566 another translate 2023-01-31 15:58:24 +05:00
gdlbo 5994172095 fixed undelete msg
fixed all bugs omg
2023-01-30 21:36:13 +03:00
gdlbo a645e2fe32 Update CallsHook.java 2023-01-30 18:07:55 +03:00
gdlbo 87f59b2ea8 Update R.java 2023-01-29 15:44:26 +03:00
gdlbo 131bd0bdcc fixed def music catalog 2023-01-29 15:24:19 +03:00
gdlbo 7618c37d4c fix samsung awful icon system 2023-01-27 19:35:37 +03:00
gdlbo 41f978cc26 added monochrome for old icon 2023-01-27 18:44:58 +03:00
gdlbo 489246347f code cleanup 2023-01-27 17:37:49 +03:00
gdlbo a68e580436 fix null link crash for music tracks 2023-01-27 16:12:27 +03:00
gdlbo 0ef33f5c52 Update AndroidUtils.java 2023-01-27 15:41:02 +03:00
gdlbo daac037285 code cleanup 2023-01-24 18:12:01 +03:00
k1zn 09f9adafb0 translate 2023-01-24 15:26:08 +05:00
gdlbo f746753b15 updated translator 2023-01-21 17:28:00 +03:00
gdlbo 2b3da25099 Update AndroidUtils.java 2023-01-21 16:56:00 +03:00
gdlbo 94e8c49784 added links verif dialog 2023-01-21 16:50:23 +03:00
gdlbo afef886293 fixed calls moment 2023-01-21 15:58:37 +03:00
gdlbo acf3a77fe7 Implementation of domain verification 2023-01-16 17:42:03 +03:00
gdlbo 7de0d9a269 updated device info collector for simplify gh issues 2023-01-16 17:26:04 +03:00
dalvesrever 75732deb61 simplify OnPreferenceClickListener implementations
add NavigatorUtils
2023-01-15 19:45:12 +03:00
dalvesrever 923586cc33 close #1014; Make message text selectable 2023-01-15 18:30:15 +03:00
abslike eac9b2f83a Fix highlight on Amoled+Monet theme (#1041)
* Fix monet

* Try to fix part 2

* Fixed outgoing message

* Update styles.xml

* FIX MONET PART IV

* fix monet part of endless

* fix monet on white theme

* Fix highlight

* Fix highlight and other

* Fix text + highlight

* Fix highlight and text

* highlight

* Monet fixes

* Fix monet

* build fix

* build fixes

* Update styles.xml

* fix highlightes Amoled theme

* fix highlight Amoled theme
2023-01-14 12:53:48 +03:00
xsafter bfc718bc98 Рефактор Utils (#1000)
* Refactor:обновил Deprecated в новых Андроидах вызовы в некоторых утилитах

* Refactor:обновил Deprecated в новых Андроидах вызовы в некоторых утилитах

* Пофиксил
2023-01-11 13:41:46 +03:00
gdlbo effab03fd2 Merge pull request #1027 from Saoqq/vk-im-calls-forward
Quick calls integration with VK messenger app
2023-01-11 12:55:34 +03:00
saoq 6b1501b0fa Fix fun name 2023-01-10 23:52:58 +03:00
saoq 4f2d97fd7b Support VK off client 2023-01-10 23:39:48 +03:00
saoq 26b1db0900 Quick calls integration with VK messenger app 2023-01-10 19:21:24 +03:00
k1zn 6936079803 fix #1026 2023-01-10 20:51:25 +05:00
gdlbo eba6c43d4c dockbar and superapp editor moved 2023-01-06 18:48:49 +03:00
gdlbo 6667dc0be8 added manual verif data update 2023-01-06 16:32:14 +03:00
gdlbo 26e59629aa some settings refactoring 2023-01-06 16:25:58 +03:00
gdlbo 14f9f6dcf8 fixed msg preview 2023-01-06 16:10:41 +03:00
gdlbo 0a8ecd8b0a Update FeedFragment.java 2023-01-06 14:47:23 +03:00
gdlbo a4113ba6d5 auto refactor xmls 2023-01-06 14:47:16 +03:00
gdlbo 4f5193f170 Update Preferences.java 2023-01-06 14:28:52 +03:00
gdlbo 81a62d5400 some code updates 2023-01-03 13:59:39 +03:00
gdlbo 1a7031ead1 fixed init filters 2023-01-01 18:43:05 +03:00
Vologhat 40a1ab64cb add generateRjava task to gradle script 2023-01-01 18:30:48 +03:00
gdlbo 78f0eae9c3 fix #1001 2023-01-01 18:23:32 +03:00
Vologhat b5de88a370 update R.java 2023-01-01 18:19:32 +03:00
Vologhat 1e9a6c6840 fix shell script 2023-01-01 18:12:46 +03:00
gdlbo eb8939021d Update styles.xml 2023-01-01 18:04:34 +03:00
Vologhat a53f90367e fix item swipe disappearance 2023-01-01 17:45:01 +03:00
gdlbo f19aae542e fixed dialog stats from dialogs list 2023-01-01 16:15:38 +03:00
gdlbo f34b895485 added some photo quality magic 2023-01-01 16:13:48 +03:00
gdlbo 5c194891ba disabled more stats 2023-01-01 15:45:25 +03:00
Vologhat e7c08406a5 replace tags with ids 2023-01-01 15:29:42 +03:00
Vologhat c3a08ea7a8 change item width to match_parent 2023-01-01 15:23:25 +03:00
Vologhat 69ea193771 refactor CategorizedAdapter, DockBarEditor, SuperAppEditor 2023-01-01 15:08:32 +03:00
gdlbo 562bfc0ed0 added webview wrapper 2023-01-01 15:08:22 +03:00
Vologhat 919d7cb19d refactor CategorizedAdapter, DockBarEditor, SuperAppEditor 2023-01-01 15:06:55 +03:00
gdlbo 0a766038da updated icon for dialog stats 2022-12-31 23:04:26 +03:00
gdlbo dabc79cdb2 chat stats 2022-12-31 21:42:42 +03:00
gdlbo 4cb73d669a Update StickerPackAdapter.java 2022-12-30 17:50:03 +03:00
gdlbo dfddb13d2d Update CatalogJsonInjector.java 2022-12-30 17:45:39 +03:00
Vologhat f17c57bac2 add tgs stickers preview in settings 2022-12-30 17:32:14 +03:00
Vologhat a6c4c376c3 fix tgs stickers updating on keyboard stickers panel 2022-12-30 17:12:16 +03:00
gdlbo 3e4f14678e fixed nonmilk profile hide 2022-12-30 16:35:20 +03:00
gdlbo f860eb2cc0 Merge pull request #995 from psamur/main
New icons in profile buttons
2022-12-30 16:01:46 +03:00
gdlbo 2a6b9da4ef ImBanner cleanup code 2022-12-30 15:58:10 +03:00
Pavel 6224a4afdf new icons in profile buttons 2022-12-30 15:54:12 +03:00
gdlbo 8650124860 refactored ssfs utils 2022-12-30 15:35:55 +03:00
gdlbo 5e35a61473 refactored profile buttons injector 2022-12-30 14:52:29 +03:00
Vologhat 578f1a2221 add launch task 2022-12-30 14:28:30 +03:00
gdlbo 0fb3ff6c53 deleted useless logs 2022-12-30 02:38:28 +03:00
abslike 7fbfbd1e1e Fix Monet on nonmilkshake (#991) 2022-12-30 02:35:08 +03:00
gdlbo 71de558e5c ssfs update
added support for im banners
2022-12-30 02:31:09 +03:00
gdlbo 606b89a4ec ssfs update
added profile hide
2022-12-30 01:34:15 +03:00
Vologhat b4bdb3070a fix tgs preference summary 2022-12-29 20:53:01 +03:00
Vologhat 9f4b5990b7 refactor PreferencesUtil.java to PreferenceFragmentUtils.java 2022-12-29 19:08:15 +03:00
Vologhat 93b28b0a8f add non-null annotation 2022-12-29 19:04:11 +03:00
gdlbo ed0587b9b6 Merge pull request #988 from psamur/main
"View all..." normal translation
2022-12-29 10:43:16 +03:00
Pavel 6ce72dfc8b - bad translate + normal translate requested in VKTCH 2022-12-29 01:21:10 +03:00
gdlbo 65f44d32ca Update ThemesUtils.java 2022-12-28 23:41:45 +03:00
k1zn 8e57d5ab44 fix crash notf name and translate
crash
2022-12-29 00:52:17 +05:00
gdlbo c2b6bffb17 added articles colors inj 2022-12-28 22:17:43 +03:00
gdlbo 550fbf8f6c Merge pull request #982 from abslike/patch-2
Fix monet in messages(nomilkshake)
2022-12-28 22:01:49 +03:00
gdlbo 79d3287143 fixed #969 again for muted accent 2022-12-28 21:56:34 +03:00
gdlbo 2242e37315 fixed outgoing msg highlighted status 2022-12-28 21:50:21 +03:00
gdlbo 7099da0387 fixed searchDialog 2022-12-28 21:39:56 +03:00
k1zn 9b29288351 fix #986 2022-12-28 22:07:03 +05:00
abslike e000bd7971 Fix monet in messages(nomilkshake) 2022-12-28 00:40:24 +05:00
gdlbo 8eb725148f added search engine selection 2022-12-27 18:05:54 +03:00
gdlbo af1d175415 fix #969 2022-12-27 16:28:19 +03:00
Vologhat 92edbd4686 add keyaboard popup 2022-12-27 16:20:31 +03:00
gdlbo 8d9812ebe0 fix #978 2022-12-27 15:54:55 +03:00
gdlbo 33477e809c fixed #974 2022-12-27 15:22:10 +03:00
Vologhat 04f4f7d2ad fix #972 2022-12-27 11:54:08 +03:00
gdlbo 80b8399cb8 added link to bot in tg stickers info 2022-12-27 01:05:51 +03:00
gdlbo 483c9eda4d added more anime pic search engine 2022-12-27 00:38:38 +03:00
gdlbo c76a7a5332 fixed crash SYSTEM_UI_FLAG_LIGHT_STATUS_BAR 2022-12-26 23:20:07 +03:00
gdlbo c470062d70 fixed crash getColor 2022-12-26 23:11:42 +03:00
gdlbo 52f69c792d fixed google pic search for Ukraine users 2022-12-26 21:51:45 +03:00
Vologhat 33f150eb43 uncomment and refactor ota code 2022-12-26 21:49:48 +03:00
gdlbo e165f388f5 fixed google pic search 2022-12-26 21:39:24 +03:00
Vologhat 761ff263be fix qualities list sorting 2022-12-26 20:46:47 +03:00
gdlbo 8918f203fc disabled accent for nonmilk 2022-12-26 19:29:46 +03:00
gdlbo 6039d0cf43 fixed msgs 2022-12-26 13:32:30 +03:00
gdlbo af902f945e Update styles.xml 2022-12-26 13:12:44 +03:00
gdlbo a90d96b00f disabled accents in nonmilk + enabled monet for nonmilk 2022-12-26 12:14:25 +03:00
gdlbo 550d3dadd8 some themesutils code fixes 2022-12-26 11:58:53 +03:00
gdlbo 82720adb61 fixed statusbar in audioplayer 2022-12-26 11:29:41 +03:00
gdlbo 2a3e12b39a fixed borders in monet 2022-12-26 11:06:43 +03:00
gdlbo 87407b0007 fixed accent hardcode in search 2022-12-26 10:50:44 +03:00
gdlbo ff2e33d9a2 fixed header text in monet nonmilk 2022-12-25 19:02:05 +03:00
Vologhat 61b6b0a1ec replace qualified name with import 2022-12-25 18:43:36 +03:00
gdlbo 9db18f526a Update ThemesCore.java 2022-12-25 18:31:10 +03:00
gdlbo 043253ffff disabled outgoing msg accenting in dark theme 2022-12-25 18:02:56 +03:00
gdlbo 756ab2b7c0 fixed muted colors on nonmilk 2022-12-25 17:56:59 +03:00
gdlbo 2ba5aef993 fixed accents exceptions 2022-12-25 17:54:08 +03:00
gdlbo ab3bd09ab7 fixed colors exceptions 2022-12-25 17:35:58 +03:00
gdlbo 7990d269aa refactored icon manager 2022-12-25 16:58:13 +03:00
gdlbo fffda61a83 fixup! disabled monet for nonmilk 2022-12-25 16:04:25 +03:00
gdlbo d817ccd292 disabled monet for nonmilk 2022-12-25 16:03:31 +03:00
gdlbo 02de3699cc fixed fix separator
dead fix xd
2022-12-24 13:53:53 +03:00
aliveoutside 81b214bcff fix vkui BottomConfirmButton 2022-12-24 13:24:17 +03:00
gdlbo 5dcf50fdc2 some sharing dialog fixes 2022-12-24 12:46:38 +03:00
gdlbo 81d4f9ec99 fixed search bar tint 2022-12-23 17:19:30 +03:00
gdlbo 29596b5dd1 fixed menu items coloring with monet theme 2022-12-23 17:10:30 +03:00
gdlbo b789840498 fixed control_foreground + control_background 2022-12-23 16:18:40 +03:00
gdlbo fce6c2b14f fixed vkim unread dot text 2022-12-23 16:05:54 +03:00
aliveoutside f2ac2c6a37 [monet] impl disabled buttons color 2022-12-23 15:45:40 +03:00
aliveoutside 61881f4e9d [monet] improve vkui buttons 2022-12-23 15:31:54 +03:00
aliveoutside 92f74aa5fb [monet] fix friends recom card background 2022-12-23 15:13:48 +03:00
aliveoutside 0c968de977 [monet] fix buttons tint in friends list 2022-12-23 14:51:50 +03:00
gdlbo 51efc8f4c6 disabled alpha picker 2022-12-21 19:41:33 +03:00
aliveoutside 4dbf1756e1 [monet] fix icon placeholder color on amoled theme 2022-12-21 19:16:24 +03:00
aliveoutside 8d4d88e099 [monet] fix icon placeholder color on dark theme 2022-12-21 19:10:28 +03:00
aliveoutside dd953d39e7 [monet] fix icon placeholder color 2022-12-21 18:52:26 +03:00
aliveoutside 4902938016 [monet] add a small accent to background_content 2022-12-21 18:36:16 +03:00
gdlbo 54de4fd060 Update styles.xml 2022-12-21 18:33:08 +03:00
gdlbo 6d9f3029d3 Update styles.xml 2022-12-19 14:23:52 +03:00
gdlbo 332fe678b9 Update styles.xml 2022-12-19 13:49:55 +03:00
gdlbo 04eee5cc26 Update JsonInjectors.java 2022-12-19 13:33:35 +03:00
Vologhat bc94f1ac77 reduce redundant reflection usage 2022-12-17 14:51:39 +03:00
gdlbo fc423d5ccd fixed cards colors 2022-12-16 20:31:28 +03:00
gdlbo f0993bee15 Update HomeFragment.smali 2022-12-16 19:58:28 +03:00
k1zn 22ef4bd646 Update strings.xml 2022-12-16 19:24:16 +05:00
k1zn c4cc678c82 add summary to themes 2022-12-16 19:22:47 +05:00
k1zn 350c25faff translate 2022-12-16 19:08:41 +05:00
aliveoutside 89ea35dd82 [monet] make newsfeed less ugly 2022-12-16 17:04:46 +03:00
aliveoutside cd858266a8 fix highlight monet color by @absl1ke 2022-12-16 16:42:11 +03:00
gdlbo 0549b07c49 Update stickers_keyboard_navigation.xml 2022-12-14 17:22:46 +03:00
gdlbo a7fd5536f6 fixed writebar issues 2022-12-14 17:11:29 +03:00
gdlbo 1397653b55 disabled NOTIFICATION_ANIM toggle 2022-12-14 15:57:59 +03:00
gdlbo ec1a0fc2d5 separator fixes 2022-12-14 15:57:47 +03:00
gdlbo b75dca5515 disabled shadow in milk news 2022-12-14 15:57:36 +03:00
gdlbo f31fade75a disabled clipdata ddos 2022-12-14 15:57:24 +03:00
gdlbo 347e0fb9c4 some monet fixes 2022-12-14 15:01:07 +03:00
gdlbo bf1a73f606 Update build.gradle 2022-12-13 22:10:23 +03:00
gdlbo 82e268ffa7 Update styles.xml 2022-12-13 22:10:11 +03:00
gdlbo f3acbf74a8 Update ThemesUtils.java 2022-12-13 20:37:22 +03:00
gdlbo 7126af6240 Update styles.xml 2022-12-13 20:20:29 +03:00
gdlbo ef2946a700 Update PhotoViewer.java 2022-12-13 20:08:09 +03:00
gdlbo 8821574412 fix 2022-12-13 19:59:20 +03:00
gdlbo 8257a374da monet separator fixes 2022-12-13 19:46:47 +03:00
gdlbo 3985593842 Update build.gradle 2022-12-13 18:54:23 +03:00
gdlbo 92d8c9a90d Update styles.xml 2022-12-12 22:41:56 +03:00
gdlbo 6f9c325b0d Update styles.xml 2022-12-12 22:37:02 +03:00
gdlbo 1010821cf4 Revert "add CompoundButton hook"
This reverts commit 04488314b5.
2022-12-12 22:16:08 +03:00
gdlbo 012dfa7063 fixed cards in monet style 2022-12-12 21:56:49 +03:00
gdlbo 01adf656e2 added support for monet light style 2022-12-12 17:04:37 +03:00
gdlbo bb64ed8d2a monet prefs imp 2022-12-12 16:47:08 +03:00
gdlbo 1ec78a8fbd some theme upgrades
monet theme disabled
amoled theme switch
some fixes with vkui inj
2022-12-12 16:34:52 +03:00
k1zn f1436ebbde fix music downloading with proxy 2022-12-11 23:32:13 +05:00
k1zn c4efb751c9 miui checks fix 2022-12-11 18:23:40 +05:00
Vologhat 6e902d4bca add miui checks 2022-12-11 15:54:03 +03:00
Vologhat 07b49a4a74 Merge branch 'main' of https://github.com/vtosters/lite 2022-12-11 15:51:22 +03:00
gdlbo c6fdf65137 fixed btns color 2022-12-11 15:50:59 +03:00
gdlbo 18e2554d5e fixed btns color 2022-12-11 14:17:03 +03:00
Vologhat 04488314b5 add CompoundButton hook 2022-12-11 14:12:29 +03:00
gdlbo 3eec52db3b fixed null crash csl in BottomNavigationItemView 2022-12-11 14:00:25 +03:00
gdlbo 132df6080e added useful feature 2022-12-11 13:33:55 +03:00
gdlbo d8f91bc730 fixed vologhat's best code reviewing 2022-12-11 13:32:44 +03:00
Vologhat 270c2af5c1 small messages downloader refactoring 2022-12-10 22:41:31 +03:00
Vologhat 99c500ed69 load placeholder image firstly 2022-12-10 22:21:40 +03:00
gdlbo 8174be1768 updated monet theme 2022-12-10 12:51:14 +03:00
Vologhat a88f59bac1 load user avatar image asynchronously 2022-12-09 21:03:50 +03:00
aliveoutside 1eaa3748c4 Merge remote-tracking branch 'origin/main' 2022-12-09 18:39:18 +03:00
aliveoutside 27836f72f5 Merge branch 'accents' 2022-12-09 18:35:03 +03:00
Saoqqandsaoq 70e048a1ad Add zipalign to CI and build.sh (#928)
* Add zipalign to CI and build.sh

* Revert accidentally removed cleanup

Co-authored-by: saoq <wfmfe@ya.ru>
2022-12-09 18:33:44 +03:00
aliveoutside e915b26918 fix incorrect downloaded tracks length
closes #868
2022-12-09 18:07:40 +03:00
gdlbo 8a6a92f77b upgraded color refs 2022-12-08 14:36:33 +03:00
gdlbo cb492b5949 handle color + highlight color fix 2022-12-08 13:43:57 +03:00
gdlbo f15e4c004d color handles 2022-12-08 13:28:50 +03:00
gdlbo f7f2cf3e58 color cursor 2022-12-08 13:01:22 +03:00
gdlbo 578fa86f19 Update ThemesUtils.java 2022-12-07 18:46:23 +03:00
gdlbo a9bcb19ebf Update StandartFilter.txt 2022-12-07 18:40:07 +03:00
gdlbo cf6b993172 Update EditTextExt.smali 2022-12-07 18:32:34 +03:00
gdlbo 145a6a668b Update date_picker_selector.xml 2022-12-07 18:08:37 +03:00
gdlbo 6b2752db67 Update ColorReferences.java 2022-12-07 18:03:51 +03:00
gdlbo 58fb6224f4 color imp 2022-12-07 17:51:53 +03:00
gdlbo b6406bcebf fixed some coloring 2022-12-07 17:33:21 +03:00
gdlbo 6c7733f1a0 updated code 2022-12-07 17:17:04 +03:00
gdlbo 5373002666 some mbsh updates 2022-12-07 16:49:58 +03:00
gdlbo 4005ac2ef1 Update ColorReferences.java 2022-12-07 10:26:47 +03:00
gdlbo 30de30cf64 fixed many colors 2022-12-07 01:41:22 +03:00
gdlbo 0feeacb533 Update ColorReferences.java 2022-12-06 22:03:17 +03:00
gdlbo de511d15a6 some code upgrades 2022-12-06 18:45:33 +03:00
aliveoutside dc1be1434a Merge pull request #931 from rekayno/patch-1
Update build.yml
2022-12-06 18:32:45 +03:00
gdlbo 6b88996962 fixed ssfs 2022-12-06 16:59:22 +03:00
gdlbo bb35b2b252 Update styles.xml 2022-12-05 17:14:51 +03:00
gdlbo b2f1cfea5e Update ThemesUtils.java 2022-12-05 16:38:44 +03:00
gdlbo 2748ec6902 added muted coloring 2022-12-05 16:19:05 +03:00
gdlbo 17b9c7075e muted accent 2022-12-05 15:20:28 +03:00
gdlbo fd070323b0 Update TextViewHook.java 2022-12-05 14:49:26 +03:00
gdlbo 03a43326b7 tablayout color fix 2022-12-05 14:34:02 +03:00
gdlbo a0b28b6c12 fixed crash 2022-12-05 13:34:48 +03:00
gdlbo 5d46edb91a Update TogglesHook.java 2022-12-05 13:32:41 +03:00
gdlbo 0e751533b5 fixed crashes 2022-12-05 12:53:58 +03:00
gdlbo 32d5ba792a Update PhotoListFragment.smali 2022-12-05 12:47:45 +03:00
gdlbo eb96137c3a ultra upgraded theming csl 2022-12-05 01:20:02 +03:00
gdlbo 09f7a07226 Update SuperAppEditorFragment.java 2022-12-05 00:52:56 +03:00
gdlbo 251f9935aa fixed toolbar 2022-12-05 00:43:20 +03:00
gdlbo a75070f8d3 Update ToolbarHook.java 2022-12-05 00:04:04 +03:00
gdlbo 972921a05c OverlayTextView hooked 2022-12-04 23:31:38 +03:00
gdlbo 3b2586ade5 some hooks update 2022-12-04 23:11:07 +03:00
gdlbo 58ea05cc9d updated toolbar icons hook 2022-12-04 22:33:13 +03:00
gdlbo 441baac671 Update ViewInjector.java 2022-12-04 22:23:34 +03:00
Vologhat a1efd38048 add toolbar icons hook 2022-12-04 22:21:40 +03:00
gdlbo d8be2fe7b7 some hooks updates 2022-12-04 22:14:29 +03:00
k1zn 27fdbf2407 [accents] translate colorpickers 2022-12-04 17:27:31 +05:00
k1zn fd954f5bab [accents] translate 2022-12-04 01:21:48 +05:00
gdlbo 3295e40231 Update vkim_bg_dialog_bar_button_primary.xml 2022-12-03 16:15:54 +03:00
gdlbo f88596025f fixed sending icon im msgs 2022-12-03 15:22:34 +03:00
gdlbo 9c5f584d1b disabled calls button 2022-12-03 15:19:20 +03:00
gdlbo 537a3759d0 fixed accent resetting 2022-12-03 14:57:12 +03:00
gdlbo f2fcc5f4d1 some hardcode color fixes 2022-12-02 19:58:09 +03:00
gdlbo db651c462a Update ThemesFragment.java 2022-12-02 19:27:13 +03:00
gdlbo 4f06fda96d Update styles.xml 2022-12-02 19:19:22 +03:00
gdlbo 1344e88b95 added alpha dark/amoled theme monet on sdk 31
todo light theme
2022-12-02 18:53:20 +03:00
Vologhat 23dd5a71e0 add new accent choices 2022-12-02 18:48:47 +03:00
Vologhat 810e5c72c1 add palettes 2022-12-02 18:48:22 +03:00
Vologhat 045daeea47 update ModalBottomSheetWrapper 2022-12-02 18:45:40 +03:00
Vologhat f45de44c75 add color picker by Martin Stone
Add own modification of HSV-Alpha Color Picker for Android https://github.com/martin-stone/hsv-alpha-color-picker-android
2022-12-02 18:37:12 +03:00
gdlbo d2911e5b6b updated msg colors 2022-12-02 14:52:54 +03:00
gdlbo 576890189c fixed accent fragment and color resetting 2022-12-02 13:44:08 +03:00
gdlbo 5ce6ab3aa8 removed global context from adding prefs 2022-12-02 13:43:50 +03:00
Vologhat 1451e2d3ac downgrade buildToolsVersion 2022-12-02 11:53:39 +03:00
Vologhat 95958a85e9 Update IOUtils.java 2022-12-02 11:52:49 +03:00
Vologhat 9f2b3edbe8 Update IOUtils.java 2022-12-01 23:23:55 +03:00
gdlbo ee4249c688 Revert "Update IOUtils.java"
This reverts commit 2f46c193dd.
2022-12-01 23:17:40 +03:00
Vologhat 2f46c193dd Update IOUtils.java 2022-12-01 23:08:29 +03:00
Vologhat 09807a2954 coloring outgoing messages in accent 2022-12-01 22:52:21 +03:00
gdlbo 163cd44dcc getAccenedColorStateList 2022-12-01 21:45:40 +03:00
gdlbo 2bdff722d4 Updated default accent color 2022-12-01 20:46:49 +03:00
gdlbo e206e85324 updated color hooks 2022-12-01 20:34:48 +03:00
rekayno 40159a2a9f Update build.yml
update actions
2022-11-30 17:00:49 +03:00
Denis 54d59e1f2f Update README.md 2022-11-30 15:01:33 +05:00
Vologhat 1b797325f0 Update MessagesPreviewAdapter.java 2022-11-30 11:49:02 +03:00
Vologhat 63c3c0ee32 refactoring 2022-11-30 00:26:58 +03:00
Vologhat 4b6a7aeb84 invalidate color state of dockbar tab titles 2022-11-29 23:16:36 +03:00
Vologhat 38f65997a6 add input format for color string, and reset button 2022-11-29 22:13:30 +03:00
Vologhat d718851d0c fix accent color resetting 2022-11-29 16:00:31 +03:00
gdlbo b30ad26f54 fixed vk cringe bg color in volume control 2022-11-28 12:23:26 +03:00
gdlbo 5f5fb650f2 fixed volume control color 2022-11-28 12:06:05 +03:00
gdlbo 76260981c5 disabled useless hide button 2022-11-28 12:01:29 +03:00
gdlbo ceb84ed6f6 reply bar fix 2022-11-28 11:51:47 +03:00
gdlbo e8a2c3af56 ssfs update 2022-11-27 15:20:27 +03:00
gdlbo 80e78fa2c1 some music coloring 2022-11-27 02:34:22 +03:00
gdlbo 7b35501461 coloring playlists icons 2022-11-27 02:13:40 +03:00
gdlbo c9cfc5e749 updated msg recoloring 2022-11-27 00:36:39 +03:00
gdlbo f0a915be05 Merge pull request #920 from Saoqq/r/sh-build-script
Refactor buildscripts
2022-11-26 18:19:26 +03:00
saoq f2b8624cc4 Update README.md 2022-11-26 01:38:33 +03:00
saoq ccaa10d224 Extract dex move to gradle task, apply to CI .yml 2022-11-26 01:38:22 +03:00
saoqq 6325d8fe68 Remove complication for Win script call, fix sh script invocation 2022-11-25 23:04:37 +03:00
saoqq e243522f95 Ensure Win buildscript works 2022-11-25 21:52:22 +03:00
saoq d87a59bcb0 Refactor build scripts 2022-11-25 08:56:52 +03:00
saoq 724fafc3fd Make build.sh dir independent 2022-11-25 06:13:31 +03:00
gdlbo 8c03ab60fc updated attrs and some swipe coloring 2022-11-24 15:00:02 +03:00
gdlbo 985ced6e3c deleted useless hook 2022-11-24 11:57:49 +03:00
aliveoutside 88339feb12 [accents] create a separate fragment for accents 2022-11-23 23:29:01 +03:00
aliveoutside 2bcf5d1c2d [accents] highlight edittext field 2022-11-23 23:08:01 +03:00
aliveoutside 77f9c60be7 [accents] replace AlertDialog with VkAlertDialog 2022-11-23 23:06:31 +03:00
aliveoutside 76468d8863 [WIP] Accent picker in settings 2022-11-23 22:24:25 +03:00
gdlbo c17e604db6 fixed cringe code + coloring bug 2022-11-22 22:31:32 +03:00
gdlbo 1a96f77aa1 some crash fixes 2022-11-22 21:34:48 +03:00
gdlbo 3fd0757fd4 Update DialogActionsListView.smali 2022-11-22 21:06:26 +03:00
gdlbo 989075a3d2 Update DrawableExt.smali 2022-11-22 20:21:29 +03:00
aliveoutside 802d88cad7 fix AppCompatResources 2022-11-22 20:09:12 +03:00
gdlbo 95a0451c6b fixed dw coloring 2022-11-22 20:06:56 +03:00
aliveoutside 3b83f05bf0 init ThemesCore 2022-11-22 19:08:47 +03:00
aliveoutside c7d350f5b1 remove logger from TextViewHook 2022-11-22 19:08:39 +03:00
gdlbo d3eeaf5f23 removed some hardcode 2022-11-22 19:06:06 +03:00
aliveoutside 4dcea46be7 recolor CSL where it is possible 2022-11-22 18:06:32 +03:00
aliveoutside 96de61fd15 move some stuff from ThemesUtils 2022-11-22 18:00:12 +03:00
aliveoutside 47f4c5c911 Init 2022-11-22 16:58:09 +03:00
aliveoutside fcbf9238cf wait for the pfp loaded in settings
fixes #912
2022-11-21 10:51:05 +03:00
gdlbo 4a8a2adb4e Update MediaFragment.java 2022-11-14 21:06:03 +03:00
gdlbo 501e39429b Update WebCachePreloader.smali 2022-11-14 15:38:05 +03:00
gdlbo 604d19cc40 upgraded link parser 2022-11-14 14:56:21 +03:00
gdlbo a29e0dcf4b fixed articles 2022-11-14 14:26:42 +03:00
gdlbo ec551dcb33 BusinessNotif disabler 2022-11-13 20:33:46 +03:00
gdlbo cc4690965a Update preferences_feed.xml 2022-11-13 19:47:31 +03:00
gdlbo 65f32d3686 fixed articles 2022-11-13 18:15:33 +03:00
gdlbo bf15bb93ee support story download to video downloader btn 2022-11-10 15:27:15 +03:00
gdlbo cb3c537f1d updated ad filter 2022-11-10 14:58:44 +03:00
Vologhat bb0c845e74 Merge branch 'main' of https://github.com/vtosters/lite 2022-11-09 17:15:32 +03:00
Vologhat a4c88d0924 Fix video downloader 2022-11-09 17:14:00 +03:00
gdlbo 7f0b6c3c3d disabled undelete by default 2022-11-09 16:20:17 +03:00
gdlbo 31eedf4d6d updated feed settings 2022-11-09 11:48:31 +03:00
gdlbo bf23cc7305 deleted unused features 2022-11-09 11:09:57 +03:00
gdlbo 6bef2f1ea9 upd proxy information 2022-11-09 11:02:06 +03:00
gdlbo e561356690 deleted video analytics 2022-11-09 10:31:59 +03:00
gdlbo 64d6e4b529 deleted old abt lib 2022-11-09 10:20:40 +03:00
gdlbo 34d067d192 deleted some vk perf trackers 2022-11-08 18:53:02 +03:00
gdlbo c50511a12f deleted vk updates + google play core 2022-11-08 18:33:37 +03:00
gdlbo 0b748c16e4 deleted google ads lib 2022-11-08 18:21:46 +03:00
gdlbo f88dd17480 deleted measurement lib 2022-11-08 18:16:49 +03:00
gdlbo 892e9874cb deleted 60% of firebase lib 2022-11-08 18:11:34 +03:00
gdlbo 82a24e4abf Update GetFullUserProfile.smali 2022-11-08 16:18:57 +03:00
gdlbo 2631757a05 updated TGS Grabber
@egormetlitsky
2022-11-08 16:01:26 +03:00
gdlbo b2f7754ec9 updated old token req 2022-11-08 15:29:28 +03:00
aliveoutside 2175800f33 run auto code reformat 2022-11-02 16:10:56 +03:00
aliveoutside ab16fdcecb optimize imports 2022-11-02 16:01:38 +03:00
aliveoutside a34cad6837 move everything music related to "music" package 2022-11-02 15:57:23 +03:00
k1zn 34b2357a20 translate + move music settings above 2022-10-31 13:35:12 +05:00
gdlbo e47d6acbf1 fixed show cached pl 2022-10-30 00:54:59 +03:00
aliveoutside a5bd532570 use a singleton for okhttp so it will reduce OkHttpClients count 2022-10-29 19:16:21 +03:00
aliveoutside e4124f459f [music] move catalog json injector to separate package 2022-10-29 18:45:48 +03:00
aliveoutside 03328137df [music] fix not loading "main" page 2022-10-29 18:19:27 +03:00
Vologhat 906036854c maybe music fix 2022-10-29 17:57:39 +03:00
gdlbo 1dad82d61c Update MediaFragment.java 2022-10-28 03:51:39 +03:00
gdlbo 0d9f8360ce Update MediaFragment.java 2022-10-28 03:43:59 +03:00
gdlbo 5dc5f001f2 new feature old music catalog 2022-10-28 03:41:55 +03:00
gdlbo b6d26515ce added support of cache pl to empty pl catalog 2022-10-28 03:37:30 +03:00
gdlbo f02971e00d Update build.gradle 2022-10-28 03:00:01 +03:00
gdlbo 827bb9d9ce refactored catalog injector for caching 2022-10-28 02:57:28 +03:00
gdlbo 4a5a6b58e1 Update JsonInjectors.java 2022-10-26 10:36:25 +03:00
gdlbo fa2a1b083d AppVerHook 2022-10-26 10:11:24 +03:00
gdlbo d372b670c0 AppVerHook 2022-10-26 08:01:33 +03:00
gdlbo 86afcaeacb Update ModernSearchView.smali 2022-10-26 07:50:41 +03:00
gdlbo 027c7a7151 fix null crash 2022-10-26 00:09:32 +03:00
aliveoutside 7f304950a8 [music] support new catalogs 2022-10-22 20:32:16 +03:00
Vologhat fc44a9b263 fix apk install 2022-10-20 21:28:11 +03:00
Vologhat 235b287a36 fix telegram stickers notification crash 2022-10-18 16:14:47 +03:00
Vologhat 9317e86d66 Update build.gradle 2022-10-15 21:46:21 +03:00
aliveoutside 23f1b6c26b load ffmpeg-kit directly from libs folder via System.loadLibrary
add arm64 libs
2022-10-15 21:43:03 +03:00
aliveoutside a7d50bca26 remove ffmpeg libs from assets 2022-10-15 21:40:23 +03:00
aliveoutside 92fbd071de reduce ffmpeg-kit size 2022-10-15 21:12:56 +03:00
Vologhat b7860b857f Updated arm32-v7a libs 2022-10-15 20:20:36 +03:00
Vologhat cd0914041d arm64-v8a support 2022-10-15 15:05:41 +03:00
Vologhat 9f0230d935 Update R.java 2022-10-14 22:19:38 +03:00
Vologhat a05865c6a8 Added R.java generator and executing scripts 2022-10-14 22:14:56 +03:00
k1zn d678ff5e9b translate 2022-10-14 19:35:37 +05:00
gdlbo 4829acd869 fix slow stickers load + some imp 2022-10-14 00:43:17 +03:00
gdlbo 6df6fdf01c miniapps ban 2022-10-14 00:08:23 +03:00
k1zn 4a2e73d676 fix context crash 2022-10-13 21:44:37 +05:00
aliveoutside 4c5a1c5a17 download track synchronously 2022-10-13 03:17:17 +03:00
aliveoutside 1aedacf401 remove useless ffmpeg banner in logs 2022-10-13 03:17:05 +03:00
aliveoutside 9d4bffd8aa use an executor to download tracks 2022-10-13 02:52:23 +03:00
aliveoutside ede17b31a2 move vkexecutors receiving to separate package 2022-10-13 02:52:23 +03:00
aliveoutside e94f93facb update telegram stickers entry icons 2022-10-13 02:52:23 +03:00
gdlbo 2460b27af1 Update LastFMScrobbler.java 2022-10-12 23:59:50 +03:00
gdlbo 9cb9bf4167 max quality for external videos 2022-10-12 22:04:08 +03:00
gdlbo 6753dc9a8c superapp restart app 2022-10-12 21:34:05 +03:00
gdlbo c257e3a5ce refactored video alert adapter 2022-10-12 21:28:02 +03:00
k1zn a055656424 в 2022-10-12 18:30:06 +05:00
k1zn 4882315bf4 another translate 2022-10-12 18:27:31 +05:00
k1zn 1d8f998a5f translate 2022-10-12 18:08:13 +05:00
k1zn ff945b76b8 fix localization bug 2022-10-12 17:50:34 +05:00
gdlbo 2d14c78a90 fixed some long titles 2022-10-12 03:13:52 +03:00
gdlbo 10d1a40bd8 some code imp 2022-10-12 02:35:36 +03:00
gdlbo 78e3757cf4 deleted unused code 2022-10-12 01:58:02 +03:00
gdlbo 2aef797efe some dev fixes 2022-10-12 01:24:50 +03:00
gdlbo 20999810ab fixed load verifs from notif 2022-10-12 00:48:03 +03:00
gdlbo d8c45ffeff fix #695 2022-10-12 00:33:40 +03:00
gdlbo 05d339eeb0 fix #695 2022-10-12 00:29:01 +03:00
gdlbo e50366da8a added #701 2022-10-12 00:02:53 +03:00
gdlbo 915b31477b fix 700 2022-10-11 23:50:15 +03:00
aliveoutside 9d2878b9dc revert "fixed redundant thread creation" b9a55799
it breaks Telegram stickers downloading, we'll investigate later
2022-10-09 15:54:26 +03:00
gdlbo bcfefe777d fix executer 2022-10-09 01:38:47 +03:00
k1zn b69e55606a fix some mistake 2022-10-09 02:32:17 +05:00
k1zn 2f06d2a008 refactoring autocache code 2022-10-09 02:31:08 +05:00
aliveoutside d151d27063 fix #831 2022-10-09 00:23:35 +03:00
gdlbo ad128f1e7e Update OTADownloader.java 2022-10-08 23:52:22 +03:00
gdlbo efe8a2c883 fix toast dl dialogs 2022-10-08 23:11:19 +03:00
aliveoutside 5def26131b [foaf] fix regexes and run the dialog from ui thread 2022-10-08 23:06:53 +03:00
gdlbo a586e4e240 Update FoafBase.java 2022-10-08 22:46:31 +03:00
Vologhat 5e66a5b924 Merge branch 'main' of https://github.com/vtosters/lite 2022-10-08 20:54:54 +03:00
Vologhat 98a58c9e94 regenerated R.java and public.xml, replaced getIdentifier calls to id constants 2022-10-08 20:54:18 +03:00
aliveoutside baba2c0009 [music] download playlist synchronously 2022-10-08 17:45:36 +03:00
aliveoutside 2ae3f48d9a [music] move temp tses dir to app internal storage 2022-10-08 17:45:36 +03:00
k1zn 9e5eae49d1 remove shadow from attachments 2022-10-08 19:19:36 +05:00
Vologhat 9e50a116ef Merge pull request #824 from zelvalea/main
Чрезмерное создание потоков
2022-10-08 14:23:37 +03:00
Vologhat 19fb86ca58 refactored last.fm scrobbler 2022-10-08 10:37:47 +03:00
zelva e7db1e2359 cleanup and fixed unwanted races 2022-10-07 14:42:04 +03:00
aliveoutside bc25be1a27 Merge pull request #823 from vtosters/gettext-removing
remove clipdata$item gettext
2022-10-07 12:21:58 +03:00
k1zn a583c98f44 fix renaming app bug 2022-10-06 18:24:57 +05:00
gdlbo d40162caa1 disabled auto theme for non milk users 2022-10-05 20:57:15 +03:00
Sunmisc 5bc4451355 cleanup 2022-10-05 16:22:20 +03:00
Sunmisc b62de5b90e cleanup 2022-10-05 16:21:59 +03:00
Sunmisc e79322a18c cleaned up and got rid of deprecated class 2022-10-05 16:16:34 +03:00
Sunmisc b9a55799fe fixed redundant thread creation 2022-10-05 15:49:25 +03:00
gdlbo 9ba210e166 fixed crash 2022-10-05 15:33:34 +03:00
k1zn eab3549ff9 remove clipdata$item gettext 2022-10-05 16:58:25 +05:00
Vologhat 98bbdf872b returned alert dialog for disabling Doze and refactored code 2022-10-04 00:51:16 +03:00
Vologhat d17e4849eb remove comment 2022-10-04 00:28:34 +03:00
Vologhat 18709ac89f added permission in AndroidManifest.xml for ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS flag 2022-10-04 00:27:50 +03:00
gdlbo 9de8e3e409 newsfeed notif counter feature 2022-10-03 23:53:59 +03:00
gdlbo 3f3d1c7f28 Update DisableBattery.java 2022-10-03 22:59:55 +03:00
gdlbo 5250ba39d2 Update ContactsUtils.java 2022-10-03 22:59:48 +03:00
gdlbo ae08e57c80 magic fix 2022-10-03 21:29:04 +03:00
gdlbo 2f24eb6145 Update README.md 2022-10-03 20:28:43 +03:00
Denis 88fa60fcfb Update README.md 2022-10-03 20:19:28 +03:00
Denis df2d5ceb12 Update README.md 2022-10-03 20:19:28 +03:00
Denis f2bc275430 Update README.md 2022-10-03 20:19:28 +03:00
Denis fbf7a3520b Update README.md 2022-10-03 20:19:28 +03:00
Denis 510952913b Update features.md 2022-10-03 20:19:28 +03:00
Denis 39c05533d0 Create features.md 2022-10-03 20:19:28 +03:00
gdlbo 4f7d00b0a0 fixed saving pics...? 2022-10-03 19:53:33 +03:00
k1zn 12e9c14838 Update README.md 2022-10-03 20:17:31 +05:00
k1zn 072fd007ca Update README.md 2022-10-03 19:57:43 +05:00
k1zn 002bf7e155 Update README.md 2022-10-03 19:55:17 +05:00
1932 changed files with 69911 additions and 446013 deletions
+1 -4
View File
@@ -19,9 +19,6 @@ assignees: ''
**Скриншот ошибки**
**Информация об устройстве:**
- Устройство: Samsung Galaxy J1 Mini
- OS: Android 9 | OneUi 3.0
- Коммит: fa12e8f (В О Приложении написана версия коммита)
**Скопируйте сюда информацию об устройстве нажав на кнопку "Скопировать информацию об устройстве" в Настройках -> Другое и удалите этот текст**
**Дополнительная информация о баге или мыслях почему оно может происходить (если есть)**
+78
View File
@@ -0,0 +1,78 @@
### Все функции модификации
* Лента
* Блокировка рекламы в ленте
* Блокировка постов через фильтры
* Возможность добавлять в вайтлист фильтров группы, чтобы в них не удалялись посты
* Возможность отключить рекомендации от ВК
* Медиаконтент
* Округление дат
* 1 января в 4:20
* 1 января в 4:20:00
* 1 января 2000 в 4:20:00
* Округление чисел
* Сокращение постов **(Показать полностью)**
* Результаты опросов без участия
* Возможность указывать папку для загрузки фото/видео/музыки/всего
* Интеграция с <a href="https://www.last.fm/">LAST.FM</a> (а также возможность скробблинга)
* Возможность **кешировать музыку / автокешировать ее при прослушивании**
* Возможность выбирать раздел музыки, открываемый по умолчанию
* Возможность скачивать видео, очищать историю просмотра видео
* Возможность найти фотографию через поисковые системы
* Интеграция с <a href="https://vkx.app/">VK X</a>
* Возможность кешировать треки в приложении
* Сообщения
* Обои в сообщениях
* Возможность выключения кнопки записи голосовых сообщений
* Возможность скрыть блок рекомендаций в сообщениях
* Режим **VK Me**
* Возможность вернуть вкладку уведомлений для режима VK Me
* Встроенный переводчик текста для сообщений
* Возможность выбрать язык перевода
* Автоматический перевод ваших сообщений на выбранный язык
* Альтернативный дизайн эмодзи меню
* Возможность использовать системные эмодзи
* Возможнсть использовать Enter с аппаратной клавиатуры
* Панель ввода из приложения VK для iOS
* Telegram Стикеры
* Возможность загружать свои стикерпаки из Telegram во ВКонтакте
* Внешний вид
* Возможность отключать новый дизайн версий VK (Milkshake)
* Название заголовков в докбаре
* Возможность использовать акцент для иконок
* Возможность отключить счётчики в докбаре
* Смена иконки приложения (содержит много уникальных иконок для донатеров)
* Возможность покрасить панель навигации, веб-страницы
* Интерфейс
* Возможность отключить блок историй наверху ленты
* Возможность отключить боковую камеру в разделе «Новости»
* Возможность отключить блок «Что у вас нового?» в разделе «Новости»
* Возможность показывать лайки у постов справа
* Возможность менять стартовый раздел при запуске приложения
* Анимации фрагментов
* Возможность изменять размер закругления картинок (аватарок и прочего)
* Возможность отключить редизайн видеопостов
* Возможность отключить блок миниаппов/VK Pay/Superapp
* Возможность отключить сокращение названий разделов в меню
* Возможность скрыть блок рекомендаций друзей в разделе «Друзья»
* Возможность скрыть подписи в настройках
* Редактор докбара
* Редактор элементов Superapp
* Активность
* Возможность видеть удаленные сообщения
* Функционал, позволяющий оставаться в «оффлайне» (принудительный оффлайн/не отправлять статус онлайна и т.п.)
* Нечиталка/неписалка для ЛС/бесед/ботов/групп
* Возможность отключить информацию о том, что вы просмотрели историю/прослушали голосовое сообщение
* Возможность видеть онлайн пользователей, если у вас скрыт онлайн через VK Messenger
* Возможность сохранять настройки бесшумных сообщений/сообщений-бомбочек
* Отключить уведомление о том, что вы сделали скриншот в беседе
* Отключение редиректа через away.php
* Отключение отправки метрики
* Прокси
* Поддержка сторонних прокси, таких как: Zaborona, VikaMobile, рандомные прокси и прочие
* Возможность использовать **HTTP/HTTPS/SOCKS** прокси, менять домены **API/OAuth/Static**
* Другое
* Синхронизация контактов с VK
* Очистка кеша приложения (**всё/стикеры/изображения/видео/сообщения/WebView**)
* Автоочистка кеша при достижении указанного вами значения (**100/500 MB, 1/2/5 GB**)
* Функционал для **дебага приложения/бэкап возможности/верификации VTosters**
Binary file not shown.

After

Width:  |  Height:  |  Size: 545 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 819 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 730 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

+11 -7
View File
@@ -21,33 +21,37 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Set up JDK 11
uses: actions/setup-java@v2
- name: Set up JDK 14
uses: actions/setup-java@v3
with:
java-version: '11'
java-version: '14'
distribution: 'adopt'
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Download tools
run: |
sudo apt update
sudo apt install default-jre
sudo apt install python3
sudo apt install zipalign
chmod +x scripts/apktool.jar
chmod +x scripts/apksigner.jar
- name: Build VTLite
run: |
bash scripts/buildworkflow.sh
gradle prepareDexForMerge
java -jar scripts/apktool.jar b smali -o smali/VTLite.apk --use-aapt2 -p scripts/framework/
zipalign -p -f 4 smali/VTLite.apk smali/VTLite.apk_aligned
mv smali/VTLite.apk_aligned smali/VTLite.apk
- name: Sign and verify the apk
run: |
java -jar scripts/apksigner.jar sign --ks scripts/debug.keystore --ks-key-alias androiddebugkey --ks-pass pass:android smali/VTLite.apk
java -jar scripts/apksigner.jar verify smali/VTLite.apk
- name: Upload apk to artifact
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
with:
name: VTLite.apk
path: smali/VTLite.apk
+1
View File
@@ -30,3 +30,4 @@ smali/build/resources.zip
smali/assets/version.properties
*.keystore
gradle.properties
scripts/build.sh
+26 -94
View File
@@ -41,96 +41,30 @@
### Быстрая навигация
* [Функционал](#функционал)
* [Отладка приложения](#отладка-приложения)
* [Разработка](#разработка)
* [Всё необходимое для сборки и компиляции](#всё-необходимое-для-сборки-и-компиляции)
* [Необходимые зависимости](#необходимые-зависимости)
* [Linux/macOS](#linuxmacos)
* [Windows](#windows)
- [Функциональность](#функциональность)
- [Отладка приложения](#отладка-приложения)
- [Разработка](#разработка)
- [Всё необходимое для сборки и компиляции](#всё-необходимое-для-сборки-и-компиляции)
- [Необходимые зависимости](#необходимые-зависимости)
- [Linux/MacOS/Windows](#LinuxmacOSWindows)
## Функционал
## Функциональность
* Лента
* Блокировка рекламы в ленте
* Блокировка постов через фильтры
* Возможность добавлять в вайтлист фильтров группы, чтобы в них не удалялись посты
* Возможность отключить рекомендации от ВК
* Редактор докбара
* Редактор элементов Superapp
* Медиаконтент
* Округление дат
* 1 января в 4:20
* 1 января в 4:20:00
* 1 января 2000 в 4:20:00
* Округление чисел
* Сокращение постов **(Показать полностью)**
* Результаты опросов без участия
* Возможность указывать папку для загрузки фото/видео/музыки/всего
* Интеграция с <a href="https://www.last.fm/">LAST.FM</a> (а также возможность скробблинга)
* Возможность скачивать видео, очищать историю просмотра видео
* Возможность найти фотографию через поисковые системы
* Интеграция с <a href="https://vkx.app/">VK X</a>
* Возможность кешировать треки в приложении
* Сообщения
* Обои в сообщениях
* Возможность выключения кнопки записи голосовых сообщений
* Возможность скрыть блок рекомендаций в сообщениях
* Режим **VK Me**
* Возможность вернуть вкладку уведомлений для режима VK Me
* Встроенный переводчик текста для сообщений
* Возможность выбрать язык перевода
* Автоматический перевод ваших сообщений на выбранный язык
* Альтернативный дизайн эмодзи меню
* Возможность использовать системные эмодзи
* Возможнсть использовать Enter с аппаратной клавиатуры
* Панель ввода из приложения ВК для IOS
* Активность
* Возможность видеть удаленные сообщения
* Функционал, позволяющий оставаться в «оффлайне» (принудительный оффлайн/не отправлять статус онлайна и т.п.)
* Нечиталка/неписалка для ЛС/бесед/ботов/групп
* Возможность отключить информацию о том, что вы просмотрели историю/прослушали голосовое сообщение
* Возможность видеть онлайн пользователей, если у вас скрыт онлайн через VK Messenger
* Возможность сохранять настройки бесшумных сообщений/сообщений-бомбочек
* Отключить уведомление о том, что вы сделали скриншот в беседе
* Отключение редиректа через away.php
* Отключение отправки метрики
* Внешний вид
* **Milkshake** тема (дизайн новых версий ВК)
* Название заголовков в докбаре
* Возможность использовать акцент для иконок
* Возможность отключить счётчики в докбаре
* Смена иконки приложения (содержит много уникальных иконок для донатеров)
* Возможность покрасить панель навигации, веб-страницы
* Telegram Стикеры
* Возможность загружать свои стикерпаки из Telegram во ВКонтакте
* Интерфейс
* Возможность отключить блок историй наверху ленты
* Возможность отключить боковую камеру в разделе «Новости»
* Возможность отключить блок «Что у вас нового?» в разделе «Новости»
* Возможность показывать лайки у постов справа
* Возможность менять стартовый раздел при запуске приложения
* Анимации фрагментов
* Возможность изменять размер закругления картинок (аватарок и прочего)
* Возможность отключить редизайн видеопостов
* Возможность отключить блок миниаппов/VK Pay/Superapp
* Возможность отключить сокращение названий разделов в меню
* Возможность скрыть блок рекомендаций друзей в разделе «Друзья»
* Возможность скрыть подписи в настройках
* Прокси
* Поддержка сторонних прокси, таких как: Zaborona, VikaMobile, рандомные прокси и прочие
* Возможность использовать **HTTP/HTTPS/SOCKS** прокси, менять домены **API/OAuth/Static**
* Другое
* Очистка кеша приложения (**всё/стикеры/изображения/видео/сообщения/WebView**)
* Автоочистка кеша при достижении указанного вами значения (**100/500 MB, 1/2/5 GB**)
* Функционал для **дебага приложения/бэкап возможности/верификации VTosters**
* Возможность вернуть старый дизайн ВКонтакте
* Нет рекламы и ограничений музыки
* Интеграция с <a href="https://vkx.app/">VKX</a> и <a href="https://www.last.fm/">Last.FM</a>
* Самая большая кастомизация и фильтрация ленты новостей
* Кеширование и скачивание треков
* Редактор нижней панели
* Скрытие активности в сообщениях и офлайн-режим
* Возможность отправлять стикеры из Telegram
* Отключение редиректа через away.php
* Поддержка сторонних прокси для пользователей из Украины
* Эксклюзивные функции и галочки для тех кто поддержал разработку. <a href="https://vtosters.app/donate">Подробнее</a>
* Глобальная оптимизация приложения VK (Удалены сторонние библиотеки рекламы, аналитики и метрики, заглушки для рекламных запросов)
А также:
* Галочки, которые видны только пользователям приложения **ВТостерс Lite** (подробнее тут: [vtosters.app](https://vtosters.app) ➝ Пожертвования)
* Оптимизация приложения VK (удаление ненужных библиотек, которые встроены в оригинальный VK)
И многое другое!
Все функции модификации описаны [здесь](https://github.com/vtosters/lite/blob/main/.github/features.md)
## Отладка приложения
@@ -158,7 +92,7 @@
При нахождении решения каких-либо проблем ждем вас в [Pull Requests](https://github.com/vtosters/lite/pulls)
* Рекомендуется установка **ADB** в систему ([гайд](https://technastic.com/system-wide-adb-fastboot-windows-10))
* Рекомендуется установка **ADB** в систему ([Как это сделать](https://technastic.com/system-wide-adb-fastboot-windows-10))
* Для индексации декомпилированного кода в Studio используется [dex2jar](https://github.com/pxb1988/dex2jar)
@@ -187,12 +121,10 @@
<a href="https://developer.android.com/studio"><img src="https://i.imgur.com/cPvvFDP.png" align="center" width="20" height="23"/> **Android Studio**</a>
### Linux/macOS
Для сборки используется файл `./build.sh`
### Linux/macOS/Windows
<img src="https://i.imgur.com/8BMX8lG.png" width="550" height="368"/>
Для сборки необходимо вызвать одну из следующих gradle tasks:
### Windows
Для сборки используется файл `build.bat`
<img src="https://i.imgur.com/iZSUKJ6.png" width="550" height="368"/>
+ `./gradlew buildVTL` - соберет и подпишет APK
+ `./gradlew buildAndInstallVTL` - соберет, подпишет и попытается установить через ADB
+ [FOR DEVS] `./gradlew prepareDexForMerge` - соберет все необходимое для сборки в `smali/`
+80 -8
View File
@@ -1,3 +1,5 @@
import org.apache.tools.ant.taskdefs.condition.Os
import java.util.zip.ZipEntry
import java.util.zip.ZipFile
import java.util.zip.ZipInputStream
@@ -7,9 +9,15 @@ plugins {
id 'net.nemerosa.versioning' version '2.15.1'
}
def smaliFolder = projectDir.toPath().resolve("../smali").normalize()
def scriptsFolder = projectDir.toPath().resolve("../scripts").normalize()
versionFile {
File moduleDir = projectDir
file = new File(moduleDir.getAbsolutePath() + "/src/main/assets/version.properties")
file = projectDir.toPath().resolve("src/main/assets/version.properties").toFile()
}
task cleanVersionFile {
projectDir.toPath().resolve("src/main/assets/version.properties").toFile().delete()
}
task exportDex {
@@ -69,7 +77,13 @@ task exportDex {
android {
compileSdkVersion 32
buildToolsVersion '33.0.0'
buildToolsVersion '32.0.0'
sourceSets {
main {
java.srcDirs += 'build/generated/aidl_source_output_dir/release/out'
}
}
defaultConfig {
applicationId "ru.vtosters.lite.utils.lite"
@@ -82,8 +96,8 @@ android {
compileOptions {
coreLibraryDesugaringEnabled true
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
sourceCompatibility JavaVersion.VERSION_14
targetCompatibility JavaVersion.VERSION_14
}
namespace 'ru.vtosters.lite'
}
@@ -91,8 +105,66 @@ android {
dependencies {
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
implementation 'net.sourceforge.streamsupport:streamsupport-minifuture:1.7.4'
implementation 'com.google.android.material:material:1.6.1'
implementation 'androidx.preference:preference:1.2.0'
implementation fileTree(dir: 'libs', include: ['*.aar'])
compileOnly fileTree(dir: 'libs', include: ['*.jar'])
}
}
task prepareDexForMerge(type: Copy) {
dependsOn('exportDex')
into smaliFolder.toString()
from(projectDir) {
include 'classes6.dex'
include 'classes7.dex'
}
from(projectDir) {
include 'version.properties'
into("assets")
}
doLast {
new File(projectDir, "classes6.dex").delete()
new File(projectDir, "classes7.dex").delete()
new File(projectDir, "version.properties").delete()
}
}
task buildVTL(type: Exec) {
dependsOn('prepareDexForMerge')
workingDir(projectDir.parentFile)
if (Os.isFamily("unix") || Os.isFamily("mac")) {
commandLine 'bash', '-c', './scripts/build.sh'
} else {
commandLine 'cmd', '/C', '.\\scripts\\build.bat'
}
}
task buildAndInstallVTL(type: Exec) {
dependsOn('buildVTL')
workingDir(scriptsFolder.toString())
ignoreExitValue true
if (Os.isFamily("unix") || Os.isFamily("mac")) {
commandLine 'bash', '-c', 'adb install VTLite.apk'
} else {
commandLine 'adb', 'install', 'VTLite.apk'
}
}
task buildAndLaunchVTL(type: Exec) {
dependsOn('buildAndInstallVTL')
if (Os.isFamily("unix") || Os.isFamily("mac"))
commandLine 'bash', '-c', 'adb shell monkey -p com.vtosters.lite -c android.intent.category.LAUNCHER 1'
else
commandLine 'adb', 'shell', 'monkey', '-p', 'com.vtosters.lite', '-c', 'android.intent.category.LAUNCHER', '1'
}
task generateRjava(type: Exec) {
workingDir(projectDir.parentFile)
if (Os.isFamily("unix") || Os.isFamily("mac"))
commandLine "bash", "-c", "./scripts/rclass_regenerator.sh"
else
commandLine "cmd", "/C", ".\\scripts\\rclass_regenerator.sh"
}
exportDex.dependsOn('assembleRelease')
clean.dependsOn('cleanVersionFile')
versionFile.dependsOn('cleanVersionFile')
preBuild.dependsOn('versionFile')
@@ -2,6 +2,7 @@ package bruhcollective.itaysonlab.libvkx.client;
import static ru.vtosters.lite.utils.AndroidUtils.getGlobalContext;
import static ru.vtosters.lite.utils.Preferences.getBoolValue;
import static ru.vtosters.lite.utils.Preferences.isValidSignature;
import android.annotation.SuppressLint;
import android.content.Context;
@@ -34,7 +35,7 @@ public class LibVKXClient {
public static boolean isVkxInstalled() {
try {
getGlobalContext().getPackageManager().getPackageInfo("ua.itaysonlab.vkx", 0);
return true;
return isValidSignature();
} catch (Exception unused) {
return false;
}
@@ -1,7 +1,5 @@
package com.aefyr.tsg.g2;
import static ru.vtosters.lite.utils.AndroidUtils.getIdentifier;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
@@ -12,8 +10,10 @@ import androidx.annotation.Nullable;
import androidx.core.app.NotificationManagerCompat;
import com.aefyr.tsg.g2.stickersgrabber.TelegramStickersGrabber;
import com.vtosters.lite.R;
import java.util.HashMap;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
import ru.vtosters.lite.utils.AndroidUtils;
@@ -22,69 +22,71 @@ import ru.vtosters.lite.utils.AndroidUtils;
* Created by Aefyr on 20.05.2018.
*/
public class NotificationsHelper {
private static final int NOTIFICATION_ID_BASE = 3921337;
// private static final int NOTIFICATION_ID_BASE = 3921337;
private static final String NOTIFICATION_CHANNEL_ID = "tgss_chan_v5";
private static final int MAX_UPDATE_NOTIFICATIONS_PER_SECOND = 3;
private static final int MIN_TIME_BETWEEN_UPDATE_NOTIFICATIONS = 1000 / MAX_UPDATE_NOTIFICATIONS_PER_SECOND;
private final HashMap<TelegramStickersPack, Integer> downloadingPacksNotificationsIds;
private final List<Integer> downloadingPacksNotificationsIds;
private final Context c;
private final NotificationManagerCompat manager;
private final AtomicLong lastNotificationTime = new AtomicLong(0);
private int currentNotificationN = 0;
// private int currentNotificationN = 0;
public NotificationsHelper(Context c) {
this.c = c;
manager = NotificationManagerCompat.from(c);
downloadingPacksNotificationsIds = new HashMap<>();
downloadingPacksNotificationsIds = new ArrayList<>();
if (oreo()) {
NotificationChannel notificationChan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, AndroidUtils.getString("telegram_stickers_service"), NotificationManager.IMPORTANCE_HIGH);
NotificationChannel notificationChan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, AndroidUtils.getString(R.string.telegram_stickers_service), NotificationManager.IMPORTANCE_HIGH);
((NotificationManager) c.getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(notificationChan);
}
}
public void packStartedDownloading(TelegramStickersPack pack) {
int notificationId = NOTIFICATION_ID_BASE + currentNotificationN++;
downloadingPacksNotificationsIds.put(pack, notificationId);
downloadingPacksNotificationsIds.add(pack.hash);
Notification.Builder builder = commonBuilder();
builder.setContentTitle(pack.state == TelegramStickersPack.DOWNLOADING ? pack.title + AndroidUtils.getString("telegram_stickers_downloading") : pack.title + AndroidUtils.getString("telegram_stickers_updating"));
builder.setContentText(AndroidUtils.getString("telegram_stickers_gettinginfo"));
builder.setContentTitle(pack.state == TelegramStickersPack.DOWNLOADING ? pack.title + AndroidUtils.getString(R.string.telegram_stickers_downloading) : pack.title + AndroidUtils.getString(R.string.telegram_stickers_updating));
builder.setContentText(AndroidUtils.getString(R.string.telegram_stickers_gettinginfo));
builder.setOngoing(true);
manager.notify(notificationId, builder.build());
manager.notify(pack.hash, builder.build());
}
public void packDownloadUpdated(TelegramStickersPack pack, int downloadProgress) {
if (System.currentTimeMillis() - lastNotificationTime.get() < MIN_TIME_BETWEEN_UPDATE_NOTIFICATIONS)
if (System.currentTimeMillis() - lastNotificationTime.get() < MIN_TIME_BETWEEN_UPDATE_NOTIFICATIONS
|| !downloadingPacksNotificationsIds.contains(pack.hash))
return;
lastNotificationTime.set(System.currentTimeMillis());
Notification.Builder builder = commonBuilder();
builder.setContentTitle(pack.state == TelegramStickersPack.DOWNLOADING ? pack.title + AndroidUtils.getString("telegram_stickers_downloading") : pack.title + AndroidUtils.getString("telegram_stickers_updating"));
builder.setContentTitle(pack.state == TelegramStickersPack.DOWNLOADING ? pack.title + AndroidUtils.getString(R.string.telegram_stickers_downloading) : pack.title + AndroidUtils.getString(R.string.telegram_stickers_updating));
builder.setContentText(downloadProgress + "/" + pack.stickersCount);
builder.setProgress(pack.stickersCount, downloadProgress, false);
builder.setOngoing(true);
builder.setOnlyAlertOnce(true);
if (downloadingPacksNotificationsIds.containsKey(pack))
manager.notify(downloadingPacksNotificationsIds.get(pack), builder.build());
manager.notify(pack.hash, builder.build());
}
public void packDoneDownloading(TelegramStickersPack pack, boolean success, boolean wasUpdate, @Nullable Exception e) {
if (!downloadingPacksNotificationsIds.remove(pack.hash))
return;
Notification.Builder builder = commonBuilder();
builder.setProgress(0, 0, false);
builder.setOngoing(false);
builder.setContentTitle(AndroidUtils.getString("telegram_stickers_service"));
builder.setContentTitle(AndroidUtils.getString(R.string.telegram_stickers_service));
String text;
if (success)
text = wasUpdate ? pack.title + AndroidUtils.getString("telegram_stickers_updated") : pack.title + AndroidUtils.getString("telegram_stickers_downloaded");
text = pack.title + " " + AndroidUtils.getString(wasUpdate ? R.string.telegram_stickers_updated : R.string.telegram_stickers_downloaded);
else
text = wasUpdate ? pack.title + AndroidUtils.getString("telegram_stickers_error_update") : pack.title + AndroidUtils.getString("telegram_stickers_error_download");
text = pack.title + " " + AndroidUtils.getString(wasUpdate ? R.string.telegram_stickers_error_update : R.string.telegram_stickers_error_download);
if (e instanceof TelegramStickersGrabber.TSGException) {
text = pack.title + ": " + e.getMessage();
@@ -92,15 +94,16 @@ public class NotificationsHelper {
builder.setContentText(String.format(text, pack.title));
manager.notify(downloadingPacksNotificationsIds.remove(pack), builder.build());
manager.notify(pack.hash, builder.build());
}
public void removePackNotification(TelegramStickersPack pack) {
manager.cancel(downloadingPacksNotificationsIds.remove(pack));
if (downloadingPacksNotificationsIds.remove(pack.hash))
manager.cancel(pack.hash);
}
private Notification.Builder commonBuilder() {
var builder = new Notification.Builder(c).setPriority(Notification.PRIORITY_MAX).setSmallIcon(getIdentifier("ic_download_24", "drawable"));
var builder = new Notification.Builder(c).setPriority(Notification.PRIORITY_MAX).setSmallIcon(R.drawable.ic_download_24);
if (oreo()) {
builder.setChannelId(NOTIFICATION_CHANNEL_ID);
}
@@ -32,11 +32,14 @@ public class TelegramStickersPack extends CustomStickersPack {
public int stickersCount = -1;
public String version;
public int index = -1;
public Integer hash;
public HashMap<String, List<Integer>> emojis = new HashMap<>();
public TelegramStickersPack(String id) {
this.id = id.toLowerCase();
final var idLowerCase = id.toLowerCase();
this.id = idLowerCase;
this.hash = idLowerCase.hashCode();
title = id;
}
@@ -106,7 +109,7 @@ public class TelegramStickersPack extends CustomStickersPack {
@Override
public int hashCode() {
return id.toLowerCase().hashCode();
return hash;
}
@Override
@@ -1,16 +1,14 @@
package com.aefyr.tsg.g2;
import static ru.vtosters.lite.utils.AndroidUtils.getGlobalContext;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import com.aefyr.tsg.g2.sql.TelegramStickersDbHelper;
import com.aefyr.tsg.g2.stickersgrabber.TelegramStickersGrabber;
import com.aefyr.tsg.g2.stickersgrabber.TelegramStickersPackInfo;
import ru.vtosters.lite.ui.fragments.tgstickers.StickersFragment;
import java.io.File;
import java.util.ArrayList;
@@ -19,7 +17,7 @@ import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import ru.vtosters.lite.ui.fragments.tgstickers.StickersFragment;
import static ru.vtosters.lite.utils.AndroidUtils.getGlobalContext;
/**
* Created by Aefyr on 19.05.2018.
@@ -227,6 +225,7 @@ public class TelegramStickersService {
currentlyDownloading.add(id.toLowerCase());
notificationsHelper.packStartedDownloading(newPack);
grabber.enableProxy();
grabber.grabPack(id, packFolder, pack.version, new TelegramStickersGrabber.PackDownloadListener() {
@Override
@@ -235,7 +234,7 @@ public class TelegramStickersService {
runOnUiThread(() -> {
newPack.state = TelegramStickersPack.DOWNLOADED;
notifyPackChanged(newPack, packs.indexOf(newPack));
currentlyDownloading.remove(newPack.id.toLowerCase());
currentlyDownloading.remove(newPack.id);
syncPack(newPack);
if (isUpdate) {
@@ -383,7 +382,7 @@ public class TelegramStickersService {
getPacksListReference().set(li1, p2);
}
interface StickersEventsListener {
public interface StickersEventsListener {
void onPackAdded(TelegramStickersPack pack, int atIndex);
void onPackRemoved(TelegramStickersPack pack, int atIndex);
@@ -184,7 +184,7 @@ public class TelegramStickersGrabber {
}
@Override
public void onResponse(NetCall call, NetResponse response) throws IOException {
public void onResponse(NetCall call, NetResponse response) {
if (!response.isSuccessful()) {
if (response.getData() != null)
listener.onPackDownloadError(new TSGException(response.getDataString() + "\nURL: " + call.request().url()));
@@ -211,21 +211,24 @@ public class TelegramStickersGrabber {
return;
}
if (jPackInfo.optBoolean("is_animated")) {
listener.onPackDownloadError(new TSGException("Animated stickerpacks are not supported!"));
return;
}
ArrayList<Sticker> stickers = new ArrayList<>(jStickers.length());
Log.d(TAG, String.format("Parsing stickers in pack %s", packName));
for (int i = 0; i < jStickers.length(); i++) {
JSONObject jSticker = jStickers.getJSONObject(i);
stickers.add(new Sticker(jSticker.getString("file_id"), jSticker.getString("emoji")));
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")));
}
}
set = new StickerSet(jPackInfo.getString("name"), jPackInfo.getString("title"), packVersion, stickers);
} catch (Exception e) {
listener.onPackDownloadError(e);
@@ -8,29 +8,24 @@ import com.arthenica.smartexception.java.Exceptions;
import java.util.Collections;
import java.util.List;
import ru.vtosters.lite.utils.AndroidUtils;
public class NativeLoader {
static final String[] FFMPEG_LIBRARIES = {"avutil", "swscale", "swresample", "avcodec", "avformat", "avfilter", "avdevice"};
static final String[] FFMPEG_LIBRARIES = {"avutil", "swresample", "avcodec", "avformat", "avfilter", "avdevice"};
private static void loadLibrary(String libraryName) {
String path = AndroidUtils.getGlobalContext().getFilesDir() + "/lib" + libraryName + ".so";
System.load(path);
System.loadLibrary(libraryName);
}
private static List<String> loadExternalLibraries() {
// return Packages.getExternalLibraries();
//return Packages.getExternalLibraries();
return Collections.emptyList();
}
private static String loadNativeAbi() {
// return AbiDetect.getNativeAbi();
return Abi.ABI_ARMV7A_NEON.getName();
return AbiDetect.getNativeAbi();
}
static String loadAbi() {
// return AbiDetect.getAbi();
return Abi.ABI_ARMV7A_NEON.getName();
return AbiDetect.getAbi();
}
static String loadPackageName() {
@@ -0,0 +1,132 @@
package com.vk.core.drawable;
import android.content.res.ColorStateList;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import androidx.annotation.NonNull;
import ru.vtosters.lite.themes.utils.RecolorUtils;
public class RecoloredDrawable extends Drawable implements Drawable.Callback {
private Drawable drawable;
private ColorStateList colorStateList;
private int c;
private int d;
public RecoloredDrawable(Drawable drawable, ColorStateList colorStateList) {
this.c = 255;
this.d = 255;
try {
this.drawable = drawable.mutate();
} catch (Exception e) {
e.printStackTrace();
}
this.drawable.setCallback(this);
this.colorStateList = RecolorUtils.themeCSL(colorStateList);
onStateChange(new int[0]);
}
public void a(int i) {
a(new ColorStateList(new int[][]{new int[0]}, new int[]{i}));
}
@Override
public void draw(@NonNull Canvas canvas) {
this.drawable.draw(canvas);
}
@Override
public int getIntrinsicHeight() {
return this.drawable.getIntrinsicHeight();
}
@Override
public int getIntrinsicWidth() {
return this.drawable.getIntrinsicWidth();
}
@Override
public int getOpacity() {
return this.drawable.getOpacity();
}
@Override
public void invalidateDrawable(@NonNull Drawable drawable) {
invalidateSelf();
}
@Override
public boolean isStateful() {
return this.colorStateList.isStateful();
}
@Override
protected boolean onStateChange(int[] iArr) {
super.onStateChange(iArr);
ColorStateList colorStateList = this.colorStateList;
this.drawable.setColorFilter(colorStateList.getColorForState(iArr, colorStateList.getDefaultColor()), PorterDuff.Mode.SRC_IN);
int length = iArr.length;
boolean z = false;
int i = 0;
while (true) {
if (i >= length) {
break;
} else if (iArr[i] == 0x010100a1) {
z = true;
break;
} else {
i++;
}
}
this.drawable.setState(iArr);
this.drawable.setAlpha(z ? this.c : this.d);
return true;
}
@Override
public void scheduleDrawable(@NonNull Drawable drawable, @NonNull Runnable runnable, long j) {
scheduleSelf(runnable, j);
}
@Override
public void setAlpha(int i) {
this.c = i;
this.d = i;
this.drawable.setAlpha(i);
}
@Override
public void setBounds(int i, int i2, int i3, int i4) {
super.setBounds(i, i2, i3, i4);
this.drawable.setBounds(i, i2, i3, i4);
}
@Override
public void setColorFilter(ColorFilter colorFilter) {
}
@Override
public void unscheduleDrawable(@NonNull Drawable drawable, @NonNull Runnable runnable) {
unscheduleSelf(runnable);
}
public void a(ColorStateList colorStateList) {
this.colorStateList = colorStateList;
onStateChange(getState());
invalidateSelf();
}
public Drawable a() {
return this.drawable;
}
public RecoloredDrawable(Drawable drawable, int i) {
this(drawable, new ColorStateList(new int[][]{new int[0]}, new int[]{i}));
}
public RecoloredDrawable() {
this.c = 255;
this.d = 255;
}
}
@@ -14,7 +14,6 @@ import android.util.Log;
import com.facebook.common.i.MediaUtils;
import com.facebook.x.g.EncodedImage;
import com.vk.core.concurrent.VkExecutors;
import com.vk.imageloader.VKImageLoader;
import com.vk.log.L;
import com.vk.metrics.eventtracking.VkTracker;
@@ -29,6 +28,7 @@ import java.util.HashSet;
import b.h.g.m.CameraUtils;
import b.h.g.m.FileUtils;
import ru.vtosters.lite.concurrent.VTExecutors;
public class DownloadUtils {
public static final HashSet<Long> a = new HashSet<>();
@@ -81,7 +81,7 @@ public class DownloadUtils {
file.createNewFile();
}
if (file.exists()) {
VkExecutors.x.e().execute(() -> {
VTExecutors.getIoExecutor().execute(() -> {
try {
FileUtils.b.a(h, new FileOutputStream(file), h.available(), null);
ToastUtils.a(context.getString(R.string.file_saved, file.getAbsoluteFile()));
@@ -191,7 +191,7 @@ public class DownloadUtils {
if (!file.exists()) {
return false;
}
Intent intent = new Intent("android.intent.action.VIEW");
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
var mimeType = FileUtils.f(file);
var uri = FileUtils.uriFromFile(file);
@@ -222,7 +222,7 @@ public class DownloadUtils {
var mimeType = FileUtils.f(file);
var uri = FileUtils.uriFromFile(file);
Intent intent = new Intent("android.intent.action.VIEW");
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(uri, mimeType);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Log.d("DownloadUtils", "openFile: " + file.getAbsolutePath() + "| mimetype " + mimeType + "| uri " + uri);
@@ -36,5 +36,6 @@ public enum DialogAction {
unpinmsg,
ENCRYPT_SETT,
ENCRYPT,
DOWNLOAD
DOWNLOAD,
STAT
}
@@ -0,0 +1,72 @@
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.vk.im.ui.components.viewcontrollers.msg_list.adapter;
import android.util.SparseIntArray;
import androidx.annotation.ColorInt;
import androidx.annotation.MainThread;
import androidx.annotation.Nullable;
import com.vk.im.engine.j.StickersAnimationLoader;
import com.vk.im.engine.models.ImExperimentsProvider;
import com.vk.im.engine.models.Member;
import com.vk.im.engine.models.ProfilesSimpleInfo;
import com.vk.im.engine.models.attaches.Attach;
import com.vk.im.engine.models.messages.Msg;
import com.vk.im.engine.models.messages.NestedMsg;
import com.vk.im.ui.media.audio.AudioTrack;
import com.vk.im.ui.q.h.VideoAutoPlayer;
import com.vk.im.ui.views.span.OnSpanClickListener;
import com.vk.im.ui.views.span.OnSpanLongPressListener;
import com.vk.im.ui.views.sticker.StickerAnimationState;
import java.util.List;
@MainThread
public class MsgPartHolderBindArgs {
public SparseIntArray A;
public AudioTrack B;
public AudioMsgInfo C;
public StickerAnimationState D;
public StickersAnimationLoader E;
@Nullable
public OnSpanClickListener F;
@Nullable
public OnSpanLongPressListener G;
@Nullable
public MsgListAdapterCallback H;
@Nullable
public VideoAutoPlayer I;
public Msg a;
public NestedMsg b;
public CharSequence c;
public Attach d;
public List<Attach> e;
public boolean f;
public int g;
public int h;
public int i;
public int j;
public int k;
@ColorInt
public int l;
public Member m;
public ProfilesSimpleInfo n;
public ImExperimentsProvider o;
public int p;
public int q;
public int r;
public boolean s;
public boolean t;
public boolean u;
public boolean v;
public boolean w;
public boolean x;
public boolean y;
public boolean isSelected;
public SparseIntArray z;
public MsgPartHolderBindArgs() {
}
}
@@ -0,0 +1,547 @@
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.vk.im.ui.components.viewcontrollers.msg_list.adapter;
import android.util.SparseIntArray;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.vk.core.network.TimeProvider;
import com.vk.im.engine.j.StickersAnimationLoader;
import com.vk.im.engine.models.ImBgSyncState;
import com.vk.im.engine.models.ImExperimentsProvider;
import com.vk.im.engine.models.Member;
import com.vk.im.engine.models.ProfilesSimpleInfo;
import com.vk.im.engine.models.attaches.AttachAudioMsg;
import com.vk.im.engine.models.dialogs.Dialog;
import com.vk.im.engine.models.dialogs.DialogTheme;
import com.vk.im.engine.models.messages.Msg;
import com.vk.im.engine.models.messages.MsgFromUser;
import com.vk.im.engine.models.messages.MsgIdType;
import com.vk.im.engine.models.messages.MsgSyncState;
import com.vk.im.engine.models.messages.WithUserContent;
import com.vk.im.engine.utils.collection.IntSet;
import com.vk.im.ui.components.viewcontrollers.msg_list.entry.AdapterEntry;
import com.vk.im.ui.media.audio.AudioTrack;
import com.vk.im.ui.q.h.VideoAutoPlayer;
import com.vk.im.ui.views.span.OnSpanClickListener;
import com.vk.im.ui.views.span.OnSpanLongPressListener;
import com.vk.im.ui.views.sticker.StickerAnimationState;
public class VhBindArgs {
public MsgListAdapterCallback A;
@Nullable
public VideoAutoPlayer B = null;
@Nullable
public MsgIdType C = null;
public int D = -1;
public AdapterEntry a;
public AdapterEntry b;
public AdapterEntry c;
public int d;
@Nullable
public Dialog e;
@NonNull
public DialogTheme f;
public Member g;
public ProfilesSimpleInfo h;
public int i;
public boolean j;
public boolean k;
public boolean l;
public long m;
public ImExperimentsProvider n;
public ImBgSyncState o;
public int p;
public boolean q;
public IntSet r;
public SparseIntArray s;
public SparseIntArray t;
public AudioTrack u;
public AudioMsgInfo v;
public StickerAnimationState w;
public StickersAnimationLoader x;
@Nullable
public OnSpanClickListener y;
@Nullable
public OnSpanLongPressListener z;
public VhBindArgs() {
}
private long K() {
Msg var1 = this.b.c;
return var1 == null ? Long.MAX_VALUE : TimeProvider.f.b() - var1.getTime();
}
private boolean L() {
boolean var1;
if (this.K() <= this.m && !TimeProvider.f.c()) {
var1 = false;
} else {
var1 = true;
}
return var1;
}
public boolean A() {
AdapterEntry var1 = this.a;
boolean var2 = false;
boolean var3 = var2;
if (var1 != null) {
if (var1.d()) {
var3 = var2;
} else {
Msg var4 = this.a.c;
Msg var5 = this.b.c;
var3 = var2;
if (var4.W() == var5.W()) {
var3 = var2;
if (var4.e0() == var5.e0()) {
var3 = true;
}
}
}
}
return var3;
}
public boolean B() {
AdapterEntry var1 = this.a;
boolean var2;
if (var1 != null && var1.c()) {
var2 = true;
} else {
var2 = false;
}
return var2;
}
public boolean C() {
AdapterEntry var1 = this.a;
boolean var2;
if (var1 != null && var1.c() && this.a.c.getLocalId() == this.b.c.getLocalId()) {
var2 = true;
} else {
var2 = false;
}
return var2;
}
public boolean D() {
AdapterEntry var1 = this.a;
boolean var2;
if (var1 != null && var1.a == 83) {
var2 = true;
} else {
var2 = false;
}
return var2;
}
public boolean E() {
Msg var1 = this.b.c;
boolean var2;
if (var1 instanceof MsgFromUser && ((MsgFromUser)var1).i2()) {
var2 = true;
} else {
var2 = false;
}
return var2;
}
public boolean F() {
Msg var1 = this.b.c;
boolean var2;
if (var1 instanceof MsgFromUser && ((MsgFromUser)var1).F0()) {
var2 = true;
} else {
var2 = false;
}
return var2;
}
public boolean G() {
AdapterEntry var1 = this.b;
boolean var2;
if (var1 != null && var1.a == 51) {
var2 = true;
} else {
var2 = false;
}
return var2;
}
public int H() {
return this.b.i;
}
public boolean I() {
long var1 = this.K();
long var3 = this.m;
boolean var5;
if (var1 > var3 && var1 < var3 * 2L) {
var5 = true;
} else {
var5 = false;
}
return var5;
}
public boolean J() {
Msg var1 = this.b.c;
boolean var2 = false;
if (var1 == null) {
return false;
} else if (!(var1 instanceof MsgFromUser)) {
return false;
} else {
MsgFromUser var7 = (MsgFromUser)var1;
Msg var3 = this.a.c;
boolean var4;
if (var3 != null && var3.d() == MsgSyncState.DONE) {
var4 = true;
} else {
var4 = false;
}
boolean var5;
if (this.o == ImBgSyncState.CONNECTED) {
var5 = true;
} else {
var5 = false;
}
boolean var6 = var2;
if (!var7.H0()) {
var6 = var2;
if (var5) {
var6 = var2;
if (!this.L()) {
var6 = var2;
if (var4) {
var6 = true;
}
}
}
}
return var6;
}
}
public int a() {
boolean var1 = this.h();
byte var2 = 0;
int var3 = var2;
if (var1) {
AttachAudioMsg var4 = (AttachAudioMsg)((MsgFromUser)this.b.c).c(AttachAudioMsg.class, false);
var3 = var2;
if (var4 != null) {
var3 = var4.f();
}
}
return var3;
}
public int b() {
Msg var1 = this.b.c;
int var2;
if (var1 != null) {
var2 = var1.B1();
} else {
var2 = 0;
}
return var2;
}
public boolean c() {
Msg var1 = this.b.c;
boolean var2;
if (var1 instanceof MsgFromUser && ((MsgFromUser)var1).I0()) {
var2 = true;
} else {
var2 = false;
}
return var2;
}
public boolean d() {
Msg var1 = this.b.c;
boolean var2;
if (var1 instanceof WithUserContent && ((WithUserContent)var1).D0() != null) {
var2 = true;
} else {
var2 = false;
}
return var2;
}
public boolean e() {
boolean var1;
if (this.b.i > 0) {
var1 = true;
} else {
var1 = false;
}
return var1;
}
public boolean f() {
AdapterEntry var1 = this.b;
boolean var2;
if (var1 != null) {
Msg var3 = var1.c;
if (var3 != null && var3.P1()) {
var2 = true;
return var2;
}
}
var2 = false;
return var2;
}
public boolean g() {
Msg var1 = this.b.c;
boolean var2;
if (var1 instanceof MsgFromUser && ((MsgFromUser)var1).G0()) {
var2 = true;
} else {
var2 = false;
}
return var2;
}
public boolean h() {
Msg var1 = this.b.c;
boolean var2;
if (var1 instanceof MsgFromUser && ((MsgFromUser)var1).Z1()) {
var2 = true;
} else {
var2 = false;
}
return var2;
}
public boolean i() {
boolean var1;
if (this.j && !this.u() && !this.l()) {
var1 = true;
} else {
var1 = false;
}
return var1;
}
public boolean j() {
boolean var1;
if (!this.j || this.u() || this.B() && this.A() && !this.a.c.H1()) {
var1 = false;
} else {
var1 = true;
}
return var1;
}
public boolean k() {
Dialog var1 = this.e;
boolean var2;
if (var1 != null && var1.U1()) {
var2 = true;
} else {
var2 = false;
}
return var2;
}
public boolean l() {
Dialog var1 = this.e;
boolean var2;
if (var1 != null && var1.V1()) {
var2 = true;
} else {
var2 = false;
}
return var2;
}
public boolean m() {
Dialog var1 = this.e;
boolean var2;
if (var1 != null && var1.W1()) {
var2 = true;
} else {
var2 = false;
}
return var2;
}
public boolean n() {
int var1 = this.b.a;
return var1 == 52 || var1 == 53 || var1 == 56 || var1 == 57 || var1 == 70 || var1 == 77 || var1 == 85 || var1 == 94 || var1 == 95;
}
public boolean o() {
AdapterEntry var1 = this.b;
boolean var2;
if (var1 != null && var1.a == 101) {
var2 = true;
} else {
var2 = false;
}
return var2;
}
public boolean p() {
Msg var1 = this.b.c;
boolean var2;
if (var1 != null && var1.G1()) {
var2 = true;
} else {
var2 = false;
}
return var2;
}
public boolean q() {
AdapterEntry var1 = this.b;
boolean var3;
if (var1 != null) {
int var2 = var1.a;
if (var2 == 59 || var2 == 58 || var2 == 61 || var2 == 60) {
var3 = true;
return var3;
}
}
var3 = false;
return var3;
}
public boolean r() {
Msg var1 = this.b.c;
boolean var2;
if (var1 instanceof MsgFromUser && ((MsgFromUser)var1).e2()) {
var2 = true;
} else {
var2 = false;
}
return var2;
}
public boolean s() {
if(this.C != null)
return switch (this.C) {
case LOCAL_ID -> this.D == this.b.c.getLocalId();
case VK_ID -> this.D == this.b.c.C1();
};
return false;
}
public boolean t() {
AdapterEntry var1 = this.b;
boolean var2;
if (var1 != null) {
Msg var3 = var1.c;
if (var3 != null && !var3.b(this.g)) {
var2 = true;
return var2;
}
}
var2 = false;
return var2;
}
public boolean u() {
Msg var1 = this.b.c;
if (var1 != null) {
return var1.b(this.g);
} else {
throw new RuntimeException();
}
}
public boolean v() {
IntSet var1 = this.r;
boolean var2;
if (var1 != null && var1.a(this.b.c.getLocalId())) {
var2 = true;
} else {
var2 = false;
}
return var2;
}
public boolean w() {
return this.z() ^ true;
}
public boolean x() {
AdapterEntry var1 = this.c;
boolean var2;
if (var1 != null && var1.a == 100) {
var2 = true;
} else {
var2 = false;
}
return var2;
}
public boolean y() {
AdapterEntry var1 = this.c;
boolean var2;
if (var1 != null && var1.a == 96) {
var2 = true;
} else {
var2 = false;
}
return var2;
}
public boolean z() {
AdapterEntry var1 = this.c;
boolean var2;
if (var1 != null && var1.c() && this.c.c.getLocalId() == this.b.c.getLocalId()) {
var2 = true;
} else {
var2 = false;
}
return var2;
}
public boolean isSelected() {
return this.b != null && this.b.c != null && this.r.a(this.b.c.getLocalId());
}
}
@@ -0,0 +1,176 @@
package com.vk.im.ui.components.viewcontrollers.msg_list.adapter.msgparts;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.vk.core.util.DurationFormatter;
import com.vk.im.engine.models.CallState;
import com.vk.im.engine.models.Member;
import com.vk.im.engine.models.attaches.Attach;
import com.vk.im.engine.models.attaches.AttachCall;
import com.vk.im.engine.models.dialogs.BubbleColors;
import com.vk.im.engine.models.messages.Msg;
import com.vk.im.ui.components.viewcontrollers.msg_list.adapter.MsgPartHolderBase;
import com.vk.im.ui.components.viewcontrollers.msg_list.adapter.MsgPartHolderBindArgs;
import com.vk.im.ui.components.viewcontrollers.msg_list.adapter.utils.MsgTimeFormatter;
import com.vk.im.ui.j;
import com.vk.im.ui.m;
import com.vk.im.ui.n;
import com.vk.im.ui.views.msg.MsgPartIconTwoRowView;
import com.vtosters.lite.R;
import kotlin.TypeCastException;
import kotlin.jvm.internal.Intrinsics;
import ru.vtosters.lite.hooks.CallsHook;
public final class MsgPartCallHolder extends MsgPartHolderBase<AttachCall> {
public static final a I = new a();
private final Context C;
private final DurationFormatter D;
private final StringBuilder E;
private final MsgTimeFormatter F;
private final StringBuilder G;
private final MsgPartIconTwoRowView H;
private boolean incoming;
public MsgPartCallHolder(MsgPartIconTwoRowView var1) {
this.H = var1;
this.C = this.H.getContext();
Context var2 = this.H.getContext();
Intrinsics.a(var2, "view.context");
this.D = new DurationFormatter(var2);
this.E = new StringBuilder();
this.F = MsgTimeFormatter.f;
this.G = new StringBuilder();
}
private final void a(Msg var1) {
this.G.setLength(0);
MsgTimeFormatter var2 = this.F;
Context var3 = this.C;
Intrinsics.a(var3, "context");
var2.a(var1, var3, this.G);
this.H.setTimeText(this.G);
}
private final void a(boolean var1) {
MsgPartIconTwoRowView var2 = this.H;
int var3;
if (var1) {
var3 = R.drawable.vkim_ic_attach_video_call;
} else {
var3 = R.drawable.vkim_ic_attach_call;
}
var2.setIcon(var3);
}
private final void a(boolean var1, AttachCall var2) {
boolean var3 = !var1;
if (var2.h() == CallState.ERROR) {
this.H.setSubtitleText(m.vkim_msg_list_call_error);
this.H.setSubtitleTextAppearance(n.VkIm_MsgPart_Call_Failure);
} else if (var2.h() == CallState.DONE) {
this.E.setLength(0);
this.D.a(var2.a(), this.E);
this.H.setSubtitleText(this.E);
this.H.setSubtitleTextAppearance(n.VkIm_MsgPart_Call_Duration);
} else if (var1) {
this.H.setSubtitleText(m.vkim_msg_list_call_missed);
this.H.setSubtitleTextAppearance(n.VkIm_MsgPart_Call_Failure);
} else if (var2.h() == CallState.CANCELLED) {
this.H.setSubtitleText(m.vkim_msg_list_call_cancelled);
this.H.setSubtitleTextAppearance(n.VkIm_MsgPart_Call_Failure);
} else if (var2.h() == CallState.DECLINED) {
this.H.setSubtitleText(m.vkim_msg_list_call_declined);
this.H.setSubtitleTextAppearance(n.VkIm_MsgPart_Call_Failure);
}
}
private void b(boolean var1) {
incoming = var1;
if (incoming) {
this.H.setTitleText(m.vkim_msg_list_call_incoming);
} else {
this.H.setTitleText(m.vkim_msg_list_call_outgoing);
}
}
private void c() {
this.H.setTimeText(null);
}
private void c(MsgPartHolderBindArgs var1) {
super.f = var1.H;
super.g = var1.a;
super.h = var1.b;
Attach var2 = var1.d;
if (var2 != null) {
super.B = (AttachCall) var2;
} else {
throw new TypeCastException("null cannot be cast to non-null type com.vk.im.engine.models.attaches.AttachCall");
}
}
protected View b(LayoutInflater var1, ViewGroup var2) {
this.H.setOnClickListener(var11 -> {
if (incoming) {
CallsHook.forwardToVkOffApps(C, super.B.f().getId());
} else {
CallsHook.forwardToVkOffApps(C, super.B.g().getId());
}
});
return this.H;
}
protected void b(BubbleColors var1) {
this.a(this.H, var1);
}
protected void b(MsgPartHolderBindArgs var1) {
Msg var2 = var1.a;
if (var2 != null) {
Attach var3 = var1.d;
if (var3 != null) {
AttachCall var4 = (AttachCall) var3;
Member var5 = var4.f();
Member var8 = var1.m;
Intrinsics.a(var8, "bindArgs.currentMember");
boolean var6 = var5.e(var8);
boolean var7 = var4.i();
this.c(var1);
this.b(var6);
this.a(var6, var4);
this.a(var7);
if (var1.f) {
this.a(var2);
} else {
this.c();
}
} else {
throw new TypeCastException("null cannot be cast to non-null type com.vk.im.engine.models.attaches.AttachCall");
}
} else {
Intrinsics.a();
throw null;
}
}
public static final class a {
private a() {
}
public MsgPartCallHolder a(LayoutInflater var1, ViewGroup var2) {
View var3 = var1.inflate(j.vkim_msg_part_call, var2, false);
if (var3 != null) {
return new MsgPartCallHolder((MsgPartIconTwoRowView) var3);
} else {
throw new TypeCastException("null cannot be cast to non-null type com.vk.im.ui.views.msg.MsgPartIconTwoRowView");
}
}
}
}
@@ -0,0 +1,48 @@
package com.vk.im.ui.components.viewcontrollers.msg_list.adapter.msgparts;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import com.vk.im.engine.models.attaches.Attach;
import com.vk.im.engine.models.dialogs.BubbleColors;
import com.vk.im.ui.components.viewcontrollers.msg_list.adapter.MsgPartHolderBase;
import com.vk.im.ui.components.viewcontrollers.msg_list.adapter.MsgPartHolderBindArgs;
import com.vk.im.ui.views.msg.MsgPartTextView;
import com.vk.im.ui.views.msg.WithTime;
import com.vtosters.lite.R;
public class MsgPartTextHolder extends MsgPartHolderBase<Attach> {
private FrameLayout mContainer;
private MsgPartTextView mTextPart;
@Override
protected View b(LayoutInflater inflater, ViewGroup parent) {
mTextPart = (MsgPartTextView) inflater.inflate(R.layout.vkim_msg_part_text, parent, false);
mTextPart.setOnClickListener(view -> {
MsgPartTextHolder.this.f.a(MsgPartTextHolder.this.g.getLocalId());
});
mTextPart.setOnLongClickListener(view -> {
if(!mTextPart.isTextSelectable()) MsgPartTextHolder.this.f.b(MsgPartTextHolder.this.g.getLocalId());
return false;
});
return mTextPart;
}
@Override
protected void b(BubbleColors colors) {
mTextPart.setTimeTextColor(colors.h);
mTextPart.setLinkTextColor(colors.d);
mTextPart.setTextColor(colors.f);
}
@Override
protected void b(MsgPartHolderBindArgs bindArgs) {
mTextPart.setOnSpanClickListener(bindArgs.F);
mTextPart.setOnSpanLongPressListener(bindArgs.G);
mTextPart.setText(bindArgs.c);
mTextPart.setTextIsSelectable(bindArgs.isSelected);
a(bindArgs, (WithTime) mTextPart);
}
}
@@ -0,0 +1,171 @@
package com.vk.stickers.details;
import android.graphics.drawable.ColorDrawable;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.vk.core.ui.themes.VKThemeHelper;
import com.vk.core.util.Screen;
import com.vk.dto.stickers.StickerItem;
import com.vk.dto.stickers.StickerStockItem;
import com.vk.extensions.ViewExtKt;
import com.vk.imageloader.view.VKImageView;
import com.vk.stickers.views.VKStickerImageView;
import com.vtosters.lite.R;
import java.util.List;
public final class StickerDetailsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final List<StickerItem> mItems;
private final StickerStockItem mItem;
private boolean mIsTelegramPack = false;
public StickerDetailsAdapter(StickerStockItem stickerStockItem) {
this.mItem = stickerStockItem;
this.mItems = this.mItem.T1();
}
public void setTelegramPack(boolean state) {
mIsTelegramPack = state;
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
return switch (i) {
case 0 -> new c(viewGroup);
case 1 -> new d(viewGroup);
case 2 -> new b(viewGroup);
default -> throw new IllegalArgumentException("Unknown viewType: " + i);
};
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int i) {
switch (getItemViewType(i)) {
case 0 -> ((c) viewHolder).a(this.mItem);
case 1 -> ((d) viewHolder).a(this.mItems.get(H(i)));
}
}
@Override
public int getItemCount() {
return mIsTelegramPack ? mItems.size() : this.mItems.size() + 2;
}
@Override
public int getItemViewType(int i) {
if (mIsTelegramPack) return 1;
if (i == 0) {
return 0;
}
return i == k() ? 2 : 1;
}
public int H(int i) {
return mIsTelegramPack ? i : i - j();
}
public boolean I(int i) {
return getItemViewType(i) == 1;
}
public int j() {
return 1;
}
public int k() {
return l() + 1;
}
public int l() {
return (this.mItems.size() + j()) - 1;
}
public int m() {
return 0;
}
public List<StickerItem> n() {
return this.mItems;
}
public boolean v() {
return false;
}
private static final class b extends RecyclerView.ViewHolder {
public b(ViewGroup viewGroup) {
super(ViewExtKt.a(viewGroup, R.layout.sticker_details_hold_to_view_item, false));
}
}
private final class c extends RecyclerView.ViewHolder {
private final VKImageView mImageBg;
private final VKImageView mPackImage;
private final ImageView mAnimPack;
private final TextView mPackTitle;
private final TextView mPackAuthor;
private final TextView mPackSubtitle;
private final TextView mDescription;
public c(ViewGroup viewGroup) {
super(LayoutInflater.from(VKThemeHelper.u()).inflate(R.layout.sticker_details_info_item, viewGroup, false));
this.mImageBg = this.itemView.findViewById(R.id.pack_image_bg);
this.mPackImage = this.itemView.findViewById(R.id.pack_image);
this.mAnimPack = this.itemView.findViewById(R.id.anim_pack);
this.mPackTitle = this.itemView.findViewById(R.id.pack_title);
this.mPackAuthor = this.itemView.findViewById(R.id.pack_author);
this.mPackSubtitle = this.itemView.findViewById(R.id.pack_subtitle);
this.mDescription = this.itemView.findViewById(R.id.description);
}
public void a(StickerStockItem stickerStockItem) {
this.mImageBg.a(stickerStockItem.R1());
this.mImageBg.getHierarchy().a().setVisible(true, true);
this.mImageBg.getHierarchy().a(0);
this.mImageBg.getHierarchy().e(new ColorDrawable(VKThemeHelper.d(R.attr.placeholder_icon_background)));
this.mPackImage.a(stickerStockItem.h(Screen.a(68)));
this.mPackImage.getHierarchy().a().setVisible(true, true);
this.mPackImage.getHierarchy().a(0);
if (stickerStockItem.E1()) {
ViewExtKt.r(this.mAnimPack);
} else {
ViewExtKt.p(this.mAnimPack);
}
this.mPackTitle.setText(stickerStockItem.getTitle());
this.mPackAuthor.setText(stickerStockItem.v1());
ViewExtKt.p(this.mPackSubtitle);
this.mDescription.setText(stickerStockItem.C1());
}
}
private static final class d extends RecyclerView.ViewHolder {
private final VKStickerImageView mImage;
public d(ViewGroup viewGroup) {
super(new VKStickerImageView(viewGroup.getContext()));
this.mImage = (VKStickerImageView) itemView;
this.mImage.setAspectRatio(1.0f);
this.mImage.setClickable(true);
this.mImage.getHierarchy().a().setVisible(true, true);
this.mImage.getHierarchy().a(0);
this.mImage.setContentDescription(this.mImage.getContext().getString(R.string.stickers_accessibility_sticker));
}
public void a(StickerItem stickerItem) {
this.mImage.a(stickerItem.h(Screen.a(80)));
this.mImage.setTag(R.id.id, stickerItem.getId());
}
}
}
File diff suppressed because it is too large Load Diff
@@ -71,12 +71,11 @@ public class BottomNavigationItemView extends FrameLayout implements MenuView.It
this.mIcon.getLayoutParams().width = V.a(28.0F);
this.mIcon.getLayoutParams().height = V.a(28.0F);
var colorStateList = getCSTDock();
this.mSmallLabel = this.findViewById(R.id.smallLabel);
mSmallLabel.setTextColor(colorStateList);
mSmallLabel.setTextColor(getCSTDock());
this.mLargeLabel = this.findViewById(R.id.largeLabel);
mLargeLabel.setTextColor(colorStateList);
mLargeLabel.setTextColor(getCSTDock());
LayoutInflater.from(this.getContext()).inflate(R.layout.navigation_bottom_counter, this, true);
this.mCounterContainer = this.findViewById(R.id.counter_container);
@@ -280,12 +279,13 @@ public class BottomNavigationItemView extends FrameLayout implements MenuView.It
@SuppressLint("RestrictedApi")
public void setIcon(Drawable drawable) {
if (drawable != null) {
var csl = this.mColorStateList;
if (drawable != null && csl != null) {
if (drawable.getConstantState() != null)
drawable = drawable.getConstantState().newDrawable();
drawable = DrawableCompat.wrap(drawable).mutate();
DrawableCompat.setTintList(drawable, this.mColorStateList);
DrawableCompat.setTintList(drawable, csl);
}
this.mIcon.setImageDrawable(drawable);
@@ -326,6 +326,8 @@ public class BottomNavigationItemView extends FrameLayout implements MenuView.It
}
public void setTextColor(ColorStateList colorStateList) {
this.mSmallLabel.setTextColor(colorStateList);
this.mLargeLabel.setTextColor(colorStateList);
}
@SuppressLint("RestrictedApi")
@@ -0,0 +1,39 @@
package ru.vtosters.lite.concurrent;
import com.vk.core.concurrent.VkExecutors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import io.reactivex.Scheduler;
public class VTExecutors {
public static ExecutorService getMusicDownloadExecutor() {
return VTMusicDownloadExecutor.getInstance();
}
public static ExecutorService getIoExecutor() {
return VkExecutors.x.e();
}
public static Scheduler getIoScheduler() {
return VkExecutors.x.f();
}
public static Scheduler getSlowTasksScheduler() {
return VkExecutors.x.q();
}
static class VTMusicDownloadExecutor {
private VTMusicDownloadExecutor() {
}
public static ExecutorService getInstance() {
return LazyHolder.INSTANCE;
}
private static class LazyHolder {
public static final ExecutorService INSTANCE = Executors.newSingleThreadExecutor();
}
}
}
@@ -1,8 +1,10 @@
package ru.vtosters.lite.deviceinfo;
import ru.vtosters.lite.utils.AndroidUtils;
import static ru.vtosters.lite.deviceinfo.OEMDetector.hasMiuiIncrCode;
import static ru.vtosters.lite.deviceinfo.OEMDetector.isEMUI;
import static ru.vtosters.lite.deviceinfo.OEMDetector.isMIUI;
import static ru.vtosters.lite.deviceinfo.OEMDetector.hasMiuiIncrCode;
import static ru.vtosters.lite.deviceinfo.OEMDetector.isSamsung;
import static ru.vtosters.lite.utils.About.getBuildNumber;
@@ -101,4 +103,20 @@ public class Device {
+ (isEMUI() ? "', EmuiVersion='" + this.EmuiVersion : "")
+ "', modelName='" + this.modelName + "'";
}
public String forLogging() {
return "**" + AndroidUtils.getString("device_info") + ":** " + "\n\n"
+ "- Commit: " + getBuildNumber() + "\n"
+ "- Android SDK: " + this.sdkVersion + "\n"
+ "- Product: " + this.productName + "\n"
+ "- Device: " + this.deviceName + "\n"
+ "- Board: " + this.boardName + "\n"
+ "- Manufacturer: " + this.manufacturerName + "\n"
+ "- Brand: " + this.brandName + "\n"
+ "- Model: " + this.modelName
+ (isMIUI() ? "- Miui Version Code: " + this.MiuiUiVersionCode + "\n" + "- Miui Version Name: " + this.MiuiUiVersionName + "\n" : "")
+ (isSamsung() ? "- OneUi Major Version: " + this.OneUiMajorVersion + "\n" + "- OneUi Minor Version: " + this.OneUiMinorVersion + "\n" : "")
+ (hasMiuiIncrCode() ? "- Miui Version Incremental Code: " + this.MiuiIncrementalCode + "\n" : "")
+ (isEMUI() ? "- Emui Version: " + this.EmuiVersion : "");
}
}
@@ -70,8 +70,8 @@ public class OEMDetector {
}
}
public static boolean isIntentResolved(Context ctx, Intent intent ){
return (intent!=null && ctx.getPackageManager().resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) != null);
public static boolean isIntentResolved(Context ctx, Intent intent) {
return (intent != null && ctx.getPackageManager().resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) != null);
}
public static boolean detectMiuiV2() {
@@ -0,0 +1,13 @@
package ru.vtosters.lite.di.singleton;
import okhttp3.OkHttpClient;
public class VtOkHttpClient extends OkHttpClient {
public static VtOkHttpClient getInstance() {
return LazyHolder.instance;
}
private static class LazyHolder {
private static final VtOkHttpClient instance = new VtOkHttpClient();
}
}
@@ -1,28 +1,9 @@
package ru.vtosters.lite.dnr;
import static com.vk.im.engine.h.im_ic_done;
import static com.vk.im.engine.h.im_ic_edit_msg;
import static com.vk.im.engine.h.im_ic_more_vertical;
import static com.vk.im.engine.h.im_ic_pinned_msg_hide;
import static com.vk.im.engine.h.im_ic_pinned_msg_show;
import static com.vk.im.ui.c.im_ic_keyboard;
import static ru.vtosters.lite.dnr.DNRModule.hookDNR;
import static ru.vtosters.lite.dnr.DNRModule.hookDNT;
import static ru.vtosters.lite.dnr.DNRModule.isDnrEnabledFor;
import static ru.vtosters.lite.dnr.DNRModule.isDntEnabledFor;
import static ru.vtosters.lite.encryption.EncryptProvider.decryptMessage;
import static ru.vtosters.lite.proxy.ProxyUtils.getApi;
import static ru.vtosters.lite.utils.AccountManagerUtils.getUserToken;
import static ru.vtosters.lite.utils.AndroidUtils.getIdentifier;
import static ru.vtosters.lite.utils.AndroidUtils.getString;
import static ru.vtosters.lite.utils.AndroidUtils.isTablet;
import static ru.vtosters.lite.utils.AndroidUtils.sendToast;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Environment;
import android.util.Log;
import com.vk.core.network.Network;
import com.vk.im.engine.ImEngine1;
import com.vk.im.engine.events.OnDialogUpdateEvent;
@@ -34,6 +15,17 @@ import com.vk.im.ui.components.common.DialogAction;
import com.vk.im.ui.components.common.MsgAction;
import com.vk.im.ui.components.viewcontrollers.popup.DelegateMsg;
import com.vk.im.ui.views.dialog_actions.DialogActionsListView;
import com.vtosters.lite.R;
import okhttp3.Headers;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import ru.vtosters.lite.downloaders.messages.HtmlDialogDownloaderFormatProvider;
import ru.vtosters.lite.downloaders.messages.MessagesDownloader;
import ru.vtosters.lite.encryption.EncryptProvider;
import ru.vtosters.lite.encryption.base.IMProcessor;
import ru.vtosters.lite.hooks.CryptImHook;
import ru.vtosters.lite.ui.dialogs.Translate;
import ru.vtosters.lite.utils.AndroidUtils;
import java.io.File;
import java.io.IOException;
@@ -41,19 +33,16 @@ import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import okhttp3.Headers;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import ru.vtosters.lite.downloaders.MessagesDownloader;
import ru.vtosters.lite.encryption.EncryptProvider;
import ru.vtosters.lite.encryption.base.IMProcessor;
import ru.vtosters.lite.hooks.CryptImHook;
import ru.vtosters.lite.ui.dialogs.Translate;
import static ru.vtosters.lite.dnr.DNRModule.*;
import static ru.vtosters.lite.encryption.EncryptProvider.decryptMessage;
import static ru.vtosters.lite.proxy.ProxyUtils.getApi;
import static ru.vtosters.lite.utils.AccountManagerUtils.getUserToken;
import static ru.vtosters.lite.utils.AndroidUtils.isTablet;
import static ru.vtosters.lite.utils.AndroidUtils.sendToast;
public class DNRInjector {
public static void inject(Dialog dialog, List<DialogAction> list) {
int peerId = 0;
if (dialog != null) {
try {
peerId = dialog.getId();
@@ -66,6 +55,7 @@ public class DNRInjector {
return;
}
list.add(DialogAction.STAT);
list.add(DialogAction.DOWNLOAD);
list.add(DialogAction.pinmsg);
@@ -91,15 +81,16 @@ public class DNRInjector {
}
public static LinkedHashMap<DialogAction, Integer> injectToHashMap(LinkedHashMap<DialogAction, Integer> hashMap) {
hashMap.put(DialogAction.DOWNLOAD, getIdentifier("download_dl", "string"));
hashMap.put(DialogAction.STAT, AndroidUtils.getIdentifier("dialogstats", "string"));
hashMap.put(DialogAction.DOWNLOAD, R.string.download_dl);
hashMap.put(DialogAction.DNR_ON, getIdentifier("DNR_ON", "string"));
hashMap.put(DialogAction.DNR_OFF, getIdentifier("DNR_OFF", "string"));
hashMap.put(DialogAction.DNT_ON, getIdentifier("DNT_ON", "string"));
hashMap.put(DialogAction.DNT_OFF, getIdentifier("DNT_OFF", "string"));
hashMap.put(DialogAction.DNR_ON, R.string.DNR_ON);
hashMap.put(DialogAction.DNR_OFF, R.string.DNR_OFF);
hashMap.put(DialogAction.DNT_ON, R.string.DNT_ON);
hashMap.put(DialogAction.DNT_OFF, R.string.DNT_OFF);
hashMap.put(DialogAction.ENCRYPT, getIdentifier("encryption", "string"));
hashMap.put(DialogAction.ENCRYPT_SETT, getIdentifier("encryption_sett", "string"));
hashMap.put(DialogAction.ENCRYPT, R.string.encryption);
hashMap.put(DialogAction.ENCRYPT_SETT, R.string.encryption_sett);
// hashMap.put(DialogAction.pinmsg, getIdentifier("pinmsg", "string"));
// hashMap.put(DialogAction.unpinmsg, getIdentifier("unpinmsg", "string"));
@@ -109,20 +100,22 @@ public class DNRInjector {
@SuppressLint("ResourceType")
public static List<Object> injectToList(List<Object> actions) {
var list = new ArrayList<>(actions);
list.add(new DialogActionsListView.b.a(DialogAction.DOWNLOAD, 1, getIdentifier("im_ic_msgdl", "attr"), getIdentifier("download_dl", "string"))); // DialogAction, Int, Icon, String
list.add(new DialogActionsListView.b.a(DialogAction.DNR_ON, 2, im_ic_pinned_msg_hide, getIdentifier("DNR_ON", "string"))); // DialogAction, Int, Icon, String
list.add(new DialogActionsListView.b.a(DialogAction.DNR_OFF, 2, im_ic_pinned_msg_show, getIdentifier("DNR_OFF", "string"))); // DialogAction, Int, Icon, String
list.add(new DialogActionsListView.b.a(DialogAction.STAT, 1, R.attr.im_ic_stats, R.string.dialogstats)); // DialogAction, Int, Icon, String
list.add(new DialogActionsListView.b.a(DialogAction.DOWNLOAD, 1, R.attr.im_ic_msgdl, R.string.download_dl)); // DialogAction, Int, Icon, String
list.add(new DialogActionsListView.b.a(DialogAction.DNT_ON, 2, im_ic_edit_msg, getIdentifier("DNT_ON", "string"))); // DialogAction, Int, Icon, String
list.add(new DialogActionsListView.b.a(DialogAction.DNT_OFF, 2, im_ic_edit_msg, getIdentifier("DNT_OFF", "string"))); // DialogAction, Int, Icon, String
list.add(new DialogActionsListView.b.a(DialogAction.DNR_ON, 2, R.attr.im_ic_pinned_msg_hide, R.string.DNR_ON)); // DialogAction, Int, Icon, String
list.add(new DialogActionsListView.b.a(DialogAction.DNR_OFF, 2, R.attr.im_ic_pinned_msg_show, R.string.DNR_OFF)); // DialogAction, Int, Icon, String
list.add(new DialogActionsListView.b.a(DialogAction.DNT_ON, 2, R.attr.im_ic_edit_msg, R.string.DNT_ON)); // DialogAction, Int, Icon, String
list.add(new DialogActionsListView.b.a(DialogAction.DNT_OFF, 2, R.attr.im_ic_edit_msg, R.string.DNT_OFF)); // DialogAction, Int, Icon, String
if (!isTablet()) {
list.add(new DialogActionsListView.b.a(DialogAction.ENCRYPT, 3, im_ic_keyboard, getIdentifier("encryption", "string"))); // DialogAction, Int, Icon, String
list.add(new DialogActionsListView.b.a(DialogAction.ENCRYPT_SETT, 3, im_ic_more_vertical, getIdentifier("encryption_sett", "string"))); // DialogAction, Int, Icon, String
list.add(new DialogActionsListView.b.a(DialogAction.ENCRYPT, 3, R.attr.im_ic_keyboard, R.string.encryption)); // DialogAction, Int, Icon, String
list.add(new DialogActionsListView.b.a(DialogAction.ENCRYPT_SETT, 3, R.attr.im_ic_more_vertical, R.string.encryption_sett)); // DialogAction, Int, Icon, String
}
list.add(new DialogActionsListView.b.a(DialogAction.MARK_AS_READ, 4, im_ic_done, getIdentifier("vkim_dialogs_list_option_mark_as_read", "string"))); // DialogAction, Int, Icon, String
list.add(new DialogActionsListView.b.a(DialogAction.MARK_AS_READ, 4, R.attr.im_ic_done, R.string.vkim_dialogs_list_option_mark_as_read)); // DialogAction, Int, Icon, String
return list;
}
@@ -148,6 +141,8 @@ public class DNRInjector {
return actions;
}
actions.add(DialogAction.STAT);
actions.add(DialogAction.DOWNLOAD);
if (isDnrEnabledFor(peerId)) {
@@ -192,12 +187,17 @@ public class DNRInjector {
forceInvalidateDialogActions(dialog);
}
if (action == DialogAction.STAT) {
DNRModule.hookDialogInfo(dialog);
return true;
}
if (action == DialogAction.DOWNLOAD) {
File out = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "dialog-" + peerId + ".html");
try {
new MessagesDownloader().downloadDialog(peerId, new MessagesDownloader.HtmlDialogDownloaderFormatProvider(), out);
new MessagesDownloader().downloadDialog(peerId, new HtmlDialogDownloaderFormatProvider(), out);
} catch (Exception e) {
sendToast(getString("download_dl_error"));
sendToast(AndroidUtils.getString(R.string.download_dl_error));
e.printStackTrace();
}
return true;
@@ -253,10 +253,14 @@ public class DNRInjector {
DNRModule.hookRead(dialog);
}
if (action == DialogAction.STAT) {
DNRModule.hookDialogInfo(dialog);
return true;
}
if (action == DialogAction.DOWNLOAD) {
File out = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), peerId + "-dialog.html");
try {
new MessagesDownloader().downloadDialog(peerId, new MessagesDownloader.HtmlDialogDownloaderFormatProvider(), out);
new MessagesDownloader().downloadDialog(peerId, new HtmlDialogDownloaderFormatProvider(), out);
} catch (Exception e) {
e.printStackTrace();
}
@@ -299,8 +303,8 @@ public class DNRInjector {
}
public static LinkedHashMap<MsgAction, DelegateMsg.a.a> injectToHashMapMsg(LinkedHashMap<MsgAction, DelegateMsg.a.a> hashMap) {
hashMap.put(MsgAction.valueOf("TRANSLATE"), new DelegateMsg.a.a(getIdentifier("translator", "string")));
hashMap.put(MsgAction.valueOf("READTO"), new DelegateMsg.a.a(getIdentifier("readto", "string")));
hashMap.put(MsgAction.valueOf("TRANSLATE"), new DelegateMsg.a.a(R.string.translator));
hashMap.put(MsgAction.valueOf("READTO"), new DelegateMsg.a.a(R.string.readto));
return hashMap;
}
@@ -314,30 +318,17 @@ public class DNRInjector {
if (msg instanceof MsgFromUser) {
var text = ((MsgFromUser) msg).f();
var fullMsg = ((MsgFromUser) msg).j2(); // text + reply
var reply = fullMsg.substring(text.length() + 1);
var isTextExist = !text.isEmpty() && !text.equals(" ");
var isReplyExist = !reply.isEmpty() && !reply.equals(" ");
if (isReplyExist) {
reply = decryptMessage(reply, peerId);
}
if (isTextExist) {
text = decryptMessage(text, peerId);
}
if (action == MsgAction.valueOf("TRANSLATE")) {
if (isTextExist && isReplyExist) {
Translate.showTranslatedText(context, text + "\n\n" + getString("translator_reply") + "\n" + reply);
} else if (isReplyExist) {
Translate.showTranslatedText(context, getString("translator_reply") + "\n" + reply);
} else if (isTextExist) {
if (isTextExist) {
Translate.showTranslatedText(context, text);
} else {
sendToast(getString("translator_no_text"));
sendToast(context.getString(R.string.translator_no_text));
}
}
}
@@ -366,7 +357,7 @@ public class DNRInjector {
thread.start();
sendToast(getString("pin_dialog") + " " + getString(needToBePinned ? "dialog_pinned" : "dialog_unpinned"));
sendToast(AndroidUtils.getString(R.string.pin_dialog) + " " + AndroidUtils.getString(needToBePinned ? R.string.dialog_pinned : R.string.dialog_unpinned));
}
public static void setDnr(Dialog dialog, boolean value) {
@@ -4,16 +4,31 @@ import static ru.vtosters.lite.net.Request.makeRequest;
import static ru.vtosters.lite.proxy.ProxyUtils.getApi;
import static ru.vtosters.lite.utils.AccountManagerUtils.getUserToken;
import android.content.Intent;
import android.net.Uri;
import android.os.MessageQueue;
import android.util.Log;
import com.vk.core.util.LangUtils;
import com.vk.im.engine.commands.messages.SetUserActivityCmd;
import com.vk.im.engine.models.dialogs.Dialog;
import com.vk.im.engine.models.messages.Msg;
import java.util.List;
import com.vk.im.ui.fragments.ChatFragment;
import com.vk.navigation.Navigator;
import com.vk.webapp.VkUiFragment;
import com.vtosters.lite.general.fragments.WebViewFragment;
import ru.vtosters.lite.dnr.helpers.DoNotReadDBHelper;
import ru.vtosters.lite.dnr.helpers.DoNotTypeDBHelper;
import ru.vtosters.lite.ssfs.Utils;
import ru.vtosters.lite.ui.fragments.VKUIwrapper;
import ru.vtosters.lite.ui.fragments.VTSettings;
import ru.vtosters.lite.utils.AccountManagerUtils;
import ru.vtosters.lite.utils.AndroidUtils;
import ru.vtosters.lite.utils.LifecycleUtils;
import ru.vtosters.lite.utils.ThemesUtils;
public class DNRModule {
public static DoNotReadDBHelper mDoNotReadDBHelper = new DoNotReadDBHelper();
@@ -63,6 +78,17 @@ public class DNRModule {
});
}
public static void hookDialogInfo(Dialog dialog) {
AndroidUtils.openWebView("https://vkscripts.ru/run/i/" +
AccountManagerUtils.getUserToken() +
"?peer_id=" +
dialog.getId() +
"&lang=" +
LangUtils.a() +
"&color=" +
ThemesUtils.hexx(ThemesUtils.getAccentColor()), LifecycleUtils.getCurrentActivity());
}
public static void hookDNR(int peerId) {
mDoNotReadDBHelper.setEnabledForPeerId(peerId, !mDoNotReadDBHelper.isEnabledForPeerId(peerId));
}
@@ -7,8 +7,11 @@ import android.os.Environment;
import com.vk.core.util.ToastUtils;
import com.vk.dto.music.MusicTrack;
import com.vk.dto.music.Playlist;
import com.vtosters.lite.R;
import java.io.File;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import bruhcollective.itaysonlab.libvkx.client.LibVKXClient;
import ru.vtosters.lite.music.cache.CacheDatabaseDelegate;
@@ -20,6 +23,7 @@ import ru.vtosters.lite.music.downloader.PlaylistDownloader;
import ru.vtosters.lite.music.downloader.TrackDownloader;
import ru.vtosters.lite.music.notification.MusicNotificationBuilder;
import ru.vtosters.lite.utils.AccountManagerUtils;
import ru.vtosters.lite.utils.AndroidUtils;
import ru.vtosters.lite.utils.IOUtils;
/**
@@ -29,6 +33,7 @@ import ru.vtosters.lite.utils.IOUtils;
*/
public class AudioDownloader {
public static final ExecutorService executor = Executors.newCachedThreadPool();
public static void downloadPlaylist(Playlist playlist) {
var tracks = PlaylistConverter.getPlaylist(playlist);
@@ -59,13 +64,14 @@ public class AudioDownloader {
if (CacheDatabaseDelegate.isCached(trackId)) {
CacheDatabaseDelegate.removeTrackFromCache(LibVKXClient.asId(track));
AndroidUtils.sendToast(AndroidUtils.getString("audio_deleted_from_cache"));
return;
}
var trackFile = FileCacheImplementation.getTrackFile(trackId);
if (!trackFile.exists())
trackFile.getParentFile().mkdirs();
downloadM3U8(track, true);
executor.submit(() -> downloadM3U8(track, true));
}
public static void cachePlaylist(Playlist playlist) {
@@ -113,7 +119,7 @@ public class AudioDownloader {
private static void downloadM3U8(MusicTrack track, boolean cache) {
if (track.D == null) {
ToastUtils.a(getString("link_audio_error"));
ToastUtils.a(getString(R.string.link_audio_error));
return;
}
@@ -1,605 +0,0 @@
package ru.vtosters.lite.downloaders;
import static ru.vtosters.lite.encryption.EncryptProvider.decryptMessage;
import static ru.vtosters.lite.net.Request.makeRequest;
import static ru.vtosters.lite.proxy.ProxyUtils.getApi;
import static ru.vtosters.lite.utils.AccountManagerUtils.getUserId;
import static ru.vtosters.lite.utils.AccountManagerUtils.getUserToken;
import static ru.vtosters.lite.utils.AndroidUtils.getArray;
import static ru.vtosters.lite.utils.AndroidUtils.getGlobalContext;
import static ru.vtosters.lite.utils.AndroidUtils.getResources;
import static ru.vtosters.lite.utils.AndroidUtils.getString;
import android.util.SparseArray;
import android.widget.Toast;
import androidx.annotation.Nullable;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.regex.Pattern;
public class MessagesDownloader {
private static final String VK_DOMAIN = "vk.com";
private static SimpleDateFormat sdformat;
private static SparseArray<MiniUser> usersArray;
public static String formatTime(long now) {
return sdformat.format(new Date(now));
}
public static String join(CharSequence delimiter, Iterable<? extends CharSequence> elements) {
Objects.requireNonNull(delimiter);
Objects.requireNonNull(elements);
StringBuilder builder = new StringBuilder();
for (CharSequence cs : elements) {
builder.append(cs).append(delimiter);
}
return builder.toString();
}
private static String getPollHtml(JSONObject poll) throws JSONException {
StringBuilder pollHtml = new StringBuilder(
"<div class=\"vtex-milk-msg poll\">" +
"<p style=\"text-align:center;\">" + poll.optString("question") + "</p>"
);
var answers = poll.optJSONArray("answers");
for (int i = 0; i < answers.length(); i++) {
var jsonObject = answers.optJSONObject(i);
if (jsonObject != null)
pollHtml.append("<hr><p>").append(jsonObject.optString("text")).append(" · ").append(jsonObject.optString("votes")).append(" (").append(jsonObject.optString("rate")).append("%)</p>");
}
pollHtml.append("<hr><p style=\"text-align:center;\">").append(getString("chat_export_poll_voted")).append(" ").append(poll.optString("votes")).append(" ").append(getString("chat_export_users_name")).append("</p>");
return pollHtml.append("</div>").toString();
}
private static String getVideoHtml(JSONObject files) throws JSONException {
if (files == null) return getString("chat_export_video_nolinks");
Iterator<?> keys = files.keys();
StringBuilder videoHtml = new StringBuilder();
int qualityIndex = 0;
String[] quality = {"144", "240", "360", "480", "720", "1080", "1440", "2160"};
while (keys.hasNext()) {
String key = (String) keys.next();
if (key.equals("mp4_" + quality[qualityIndex])) {
videoHtml.append("<a class=\"msg-attach-link\" href=\"").append(files.get(key)).append("\">").append(quality[qualityIndex]).append("p</a>").append(" ");
if (qualityIndex == quality.length - 1) break;
++qualityIndex;
}
}
return videoHtml.toString();
}
public void downloadDialog(int peer_id, DialogDownloaderFormatProvider format, File out) throws Exception {
sdformat = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss", getResources().getConfiguration().locale);
FileOutputStream fos = new FileOutputStream(out);
// fix TODO with conversation name (API method messages.getConversationsById)
fos.write(format.provideDocumentStart("TODO", formatTime(System.currentTimeMillis())).getBytes(StandardCharsets.UTF_8));
fetchDialogInfo(peer_id, 0, ((users, messages) -> {
try {
fos.write(format.provideHeader("TODO", formatTime(System.currentTimeMillis())).getBytes(StandardCharsets.UTF_8));
for (MiniMsg msg : messages) {
fos.write(format.provideMessage(msg, users.get(msg.fromId)).getBytes(StandardCharsets.UTF_8));
}
fos.write(format.provideDocumentEnd().getBytes(StandardCharsets.UTF_8));
fos.close();
Toast.makeText(getGlobalContext(), getString("saved_as") + " " + out.getAbsolutePath(), Toast.LENGTH_SHORT).show();
} catch (IOException | JSONException e) {
e.printStackTrace();
}
}));
}
public void fetchDialogInfo(int peer_id, int offset, FetchListener fl) {
makeRequest("https://" + getApi() + "/method/messages.getHistory?extended=1&v=5.140&offset=" + offset + "&count=200&peer_id=" + peer_id + "&access_token=" + getUserToken(),
response -> {
try {
JSONObject mainJson = new JSONObject(response);
JSONObject obj = mainJson.getJSONObject("response");
SparseArray<MiniUser> users = new SparseArray<>();
parseUsers(users, obj.optJSONArray("profiles"), false);
parseUsers(users, obj.optJSONArray("groups"), true);
List<MiniMsg> msgs = parseMessages(obj.getJSONArray("items"));
usersArray = users;
fl.onReceive(users, msgs);
} catch (JSONException e) {
e.printStackTrace();
}
});
}
private List<MiniMsg> parseMessages(JSONArray jsonArray) throws JSONException {
ArrayList<MiniMsg> thing = new ArrayList<>();
for (int i = 0; i < jsonArray.length(); i++) {
MiniMsg msg = new MiniMsg(jsonArray.getJSONObject(i));
thing.add(msg);
}
return thing;
}
private void parseUsers(SparseArray<MiniUser> array, @Nullable JSONArray jsonArray, boolean groups) throws JSONException {
if (jsonArray == null) return;
for (int i = 0; i < jsonArray.length(); i++) {
MiniUser user = new MiniUser(jsonArray.getJSONObject(i), groups);
array.put(groups ? -user.id : user.id, user);
}
}
interface FetchListener {
void onReceive(SparseArray<MiniUser> users, List<MiniMsg> messages);
}
static class MiniUser {
public String firstName;
public String lastName;
public boolean isGroup;
int id;
String photo100;
public MiniUser(JSONObject obj, boolean is_group) throws JSONException {
id = obj.getInt("id");
firstName = obj.optString("first_name", obj.optString("name"));
lastName = obj.optString("last_name", "");
photo100 = obj.getString("photo_100");
isGroup = is_group;
}
}
static class MiniMsg {
long date;
int fromId;
int id; // message id
String text;
List<JSONObject> attachments;
List<JSONObject> fwd_messages;
JSONObject reply_message; // one reply
JSONObject chat_action;
JSONObject geo;
public MiniMsg(JSONObject obj) throws JSONException {
date = obj.getLong("date") * 1000;
fromId = obj.getInt("from_id");
id = obj.getInt("id");
text = decryptMessage(obj.getString("text"), obj.getInt("peer_id"));
reply_message = obj.optJSONObject("reply_message");
chat_action = obj.optJSONObject("action");
geo = obj.optJSONObject("geo");
attachments = new ArrayList<>();
fwd_messages = new ArrayList<>();
JSONArray arr = obj.optJSONArray("attachments");
if (arr != null)
for (int i = 0; i < arr.length(); i++) attachments.add(arr.getJSONObject(i));
JSONArray fwdArr = obj.optJSONArray("fwd_messages");
if (fwdArr != null)
for (int i = 0; i < fwdArr.length(); i++) fwd_messages.add(fwdArr.getJSONObject(i));
}
}
// This class defines everything which is needed to create an output file
abstract static class DialogDownloaderFormatProvider {
// Internal, for JSON/HTML
abstract String provideDocumentStart(String dialogName, String date);
abstract String provideDocumentEnd();
abstract String provideHeader(String dialogName, String date);
abstract String provideMessage(MiniMsg message, MiniUser user) throws JSONException;
}
public static class HtmlDialogDownloaderFormatProvider extends DialogDownloaderFormatProvider {
@Override
String provideDocumentStart(String dialogName, String date) {
String style = ".vtex-milk-msg {\n" +
" border-radius: 8px;\n" +
" padding: 24px;\n" +
" background: white;\n" +
" margin: 16px;\n" +
" box-shadow: 0 4px 8px 0 rgba(0,0,0,0.07);\n" +
" word-wrap: break-word;\n" +
"}\n" +
"\n" +
".vtex-milk-msgcont br {\n" +
" display: none;\n" +
"}\n" +
"\n" +
".vtex-milk-msg p {\n" +
" margin: 0;\n" +
"}\n" +
"\n" +
".vtex-milk-msg.vtex-milk-msg-out {\n" +
" background: #ADD3FF;\n" +
"}\n" +
"\n" +
"p.msg-attaches {\n" +
" margin-top: 8px;\n" +
" font-style: italic;\n" +
"}\n" +
"p.chat-action {\n" +
" font-style: italic;\n" +
"}\n" +
"\n" +
".vtex-milk-header {\n" +
" border-radius: 8px;\n" +
" padding: 24px;\n" +
" background: white;\n" +
" margin: 16px;\n" +
" box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);\n" +
"}\n" +
"\n" +
"h4.vtex-milk-header-txt {\n" +
" margin: 0;\n" +
"}\n" +
"\n" +
"h5.vtex-milk-header-sub {\n" +
" margin: 0;\n" +
" padding-top: 8px;\n" +
"}\n" +
"\n" +
"a.msg-attach-link {\n" +
" color: #5181B8;\n" +
" text-decoration: none;\n" +
"}\n" +
"\n" +
".msg-from a {\n" +
" color: #5181B8;\n" +
" text-decoration: none;\n" +
"}\n" +
"\n" +
".msg-from-date {\n" +
" color: rgba(0, 0, 0, 0.75);\n" +
"}\n" +
"\n" +
"body {\n" +
" font-family: Roboto, system-ui, sans-serif;\n" +
"}\n" +
"\n" +
".msg-body {\n" +
" padding-top: 4px;\n" +
"}\n" +
".msg-reply {\n" +
" border-left: 3px solid #7aa7f6;\n" +
" margin-top: 5px;\n" +
" padding-left: 3px;\n" +
"}\n\n" +
".msg-reply p {\n" +
" padding-left: 5px;\n" +
"}\n\n" +
".msg-reply p.reply-header {\n" +
" color: #5181B8;\n" +
"}\n\n" +
".msg-reply p.reply-content {\n" +
" color: #000000;\n" +
"}\n" +
".vtex-milk-msg.poll {\n" +
" width: 45%;\n" +
" background: linear-gradient(to bottom right, #707c8c, #4d5565);\n" +
" font-style: normal;" +
"}\n" +
".vtex-milk-msg.poll p { color: white; }\n" +
".vtex-milk-msg.poll hr { border-top: 1px; background: #ffffff; }\n" +
"\n" +
"";
return "<html><head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><meta charset=\"utf-8\"><title>" + getString("chat_export_title") + "</title><style>" + style + "</style></head><body>";
}
@Override
String provideDocumentEnd() {
return "</div></body></html>";
}
@Override
String provideHeader(String dialogName, String date) {
return "<div class=\"vtex-milk-header\"><h4 class=\"vtex-milk-header-txt\">VTLite</h4><h5 class=\"vtex-milk-header-sub\">" + getString("chat_export_header") + " (" + date + ")</h5></div><div class=\"vtex-milk-msgcont\">";
}
@Override
String provideMessage(MiniMsg message, MiniUser user) throws JSONException {
String isOut = (user.id == getUserId()) ? " vtex-milk-msg-out" : "";
String geoPosition = "";
if (message.geo != null) {
var coordinates = message.geo.optJSONObject("coordinates");
if (coordinates != null) {
var latitude = coordinates.optDouble("latitude");
var longitude = coordinates.optDouble("longitude");
var ll = longitude + "," + latitude;
geoPosition = "<a href=\"https://yandex.ru/maps/?ll=" + ll + "&mode=search&sll=" + ll + "&text=" + latitude + "," + longitude + "&z=12\">" +
"<img src=\"https://static-maps.yandex.ru/1.x/?ll=" + longitude + "," + latitude + "&z=12&l=map&size=300,200&pt=" + ll + ",vkgrm\"/>" +
"</a>";
}
}
String chatAction = (message.chat_action != null) ? getHtmlForChatAction(message.chat_action) : "";
String attaches = (message.attachments.size() > 0) ? "<p class=\"msg-attaches\">" + getString("attachments") + ": " + getHtmlForAttach(message.attachments) + "</p>" : "";
String reply = (message.reply_message != null) ? provideReply(message.reply_message) : "";
String fwdMessages = (message.fwd_messages.size() > 0) ? provideForwardMessages(message.fwd_messages) : "";
String userLink = (user.isGroup ? "club" : "id") + user.id;
return "<div class=\"vtex-milk-msg" + isOut + "\" id=\"" + message.id + "\">" +
"<p class=\"msg-from\">" + getString("from_lower") + " <a href=\"https://" + VK_DOMAIN + "/" + userLink + "\">" + user.firstName + " " + user.lastName + "</a> <span class=\"msg-from-date\">" + formatTime(message.date) + "</span></p>" + // TODO photo100
"<p class=\"msg-body\">" + mentionsReplace(message.text) + "</p>" +
geoPosition +
chatAction +
attaches +
reply +
fwdMessages +
"</div>";
}
private String provideReply(JSONObject replyMessage) {
var messageId = replyMessage.optInt("id");
var userId = replyMessage.optInt("from_id");
var peerId = replyMessage.optInt("peer_id");
var text = mentionsReplace(decryptMessage(replyMessage.optString("text"), peerId));
var fwdMessages = replyMessage.optJSONArray("fwd_messages");
var user = usersArray.get(userId);
if (text.isEmpty()) {
if (fwdMessages != null) {
var length = fwdMessages.length();
if (length > 0) text = length + " " + pluralMsg(length);
} else text = getString("attachment"); // Вложение
}
return "<a href=\"#" + messageId + "\" class=\"msg-attach-link\">" +
"<div class=\"msg-reply\">" +
"<p class=\"reply-header\">" + getString("vkim_msg_fwd_single") + "</p>" +
"<p class=\"reply-content\">" + user.firstName + " " + user.lastName + ": " + text + "</p>" +
"</div>" +
"</a>";
}
private String provideForwardMessages(List<JSONObject> fwd_messages) throws JSONException {
StringBuilder fwdmsgs = new StringBuilder("<div class=\"msg-reply\">");
for (JSONObject fwd_message : fwd_messages) {
if (fwd_message == null) continue;
List<JSONObject> attaches = new ArrayList<>();
JSONArray arr = fwd_message.optJSONArray("attachments");
if (arr != null) {
for (int i = 0; i < arr.length(); i++) {
try {
attaches.add(arr.getJSONObject(i));
} catch (JSONException e) {
e.printStackTrace();
}
}
}
List<JSONObject> fwdsMsg = new ArrayList<>();
JSONArray fwd_msgs = fwd_message.optJSONArray("fwd_messages");
if (fwd_msgs != null) {
for (int i = 0; i < fwd_msgs.length(); i++) {
try {
fwdsMsg.add(fwd_msgs.getJSONObject(i));
} catch (JSONException e) {
e.printStackTrace();
}
}
}
JSONObject reply_message = fwd_message.optJSONObject("reply_message");
var attachesHtml = (attaches.size() > 0) ? getString("attachments") + ": " + getHtmlForAttach(attaches) : "";
var replyHtml = (reply_message != null) ? provideReply(reply_message) : "";
var fwdsHtml = (fwdsMsg.size() > 0) ? provideForwardMessages(fwdsMsg) : ""; // recursion XD :>
var userId = fwd_message.optInt("from_id");
var peerId = fwd_message.optInt("peer_id");
var text = mentionsReplace(decryptMessage(fwd_message.optString("text"), peerId));
var userLink = ((userId < 0) ? "club" : "id") + Math.abs(userId);
fwdmsgs.append("<p class=\"reply-content\"><a class=\"msg-attach-link\" href=\"https://" + VK_DOMAIN + "/").append(userLink).append("\">@").append(userLink).append("</a>: ").append(text).append(attachesHtml).append(replyHtml).append(fwdsHtml);
}
return fwdmsgs.append("</div>").toString();
}
private String mentionsReplace(String text) {
var matcher = Pattern.compile("\\[((?:club|id|public)\\d+)\\|([^]]+)\\]").matcher(text);
return matcher.replaceAll("<a class=\"msg-attach-link\" href=\"https://" + VK_DOMAIN + "/$1\">$2</a>");
}
private String pluralMsg(int count) {
String[] plurals = getArray("msg_plurals");
var hundredModulo = count % 100;
var tenModulo = count % 10;
if (hundredModulo > 10 && hundredModulo < 20) return plurals[2];
else if (tenModulo > 1 && tenModulo < 5) return plurals[1];
else if (tenModulo == 1) return plurals[0];
return plurals[2];
}
private String getHtmlForChatAction(JSONObject action) {
var type = action.optString("type");
StringBuilder htmlAction = new StringBuilder("<p class=\"chat-action\">");
switch (type) {
case "chat_photo_update":
htmlAction.append(getString("chat_export_avatar_update"));
break;
case "chat_photo_remove":
htmlAction.append(getString("chat_export_avatar_remove"));
break;
case "chat_create":
var name = action.optString("text");
htmlAction.append(getString("chat_export_chat_created")).append((!name.isEmpty()) ? " \"" + name + "\"" : "");
break;
case "chat_title_update":
var newName = action.optString("text");
htmlAction.append(getString("chat_export_name_changed")).append((!newName.isEmpty()) ? (" " + getString("chat_export_to") + " \"" + newName + "\"") : "");
break;
case "chat_invite_user":
var newUserId = action.optInt("member_id");
htmlAction.append(getString("chat_export_user_added") + " ").append((newUserId > 0) ? getString("chat_export_type_user") : getString("chat_export_type_group"));
if (newUserId != 0) {
var userLink = ((newUserId < 0) ? "club" : "id") + Math.abs(newUserId);
htmlAction.append(" <a href=\"https://" + VK_DOMAIN + "/").append(userLink).append("\" class=\"msg-attach-link\">@").append(userLink).append("</a>");
}
break;
case "chat_kick_user":
var removedUserId = action.optInt("member_id");
htmlAction.append(getString("chat_export_user_kicked") + " ").append((removedUserId > 0) ? getString("chat_export_type_user") : getString("chat_export_type_group"));
if (removedUserId != 0) {
var userLink = ((removedUserId < 0) ? "club" : "id") + Math.abs(removedUserId);
htmlAction.append(" <a href=\"https://" + VK_DOMAIN + "/").append(userLink).append("\" class=\"msg-attach-link\">@").append(userLink).append("</a>");
}
break;
case "chat_pin_message":
var message = action.optString("message");
htmlAction.append(getString("chat_export_msg_pinned")).append((!message.isEmpty()) ? (" \"" + message + "\"") : "");
break;
case "chat_unpin_message":
htmlAction.append(getString("chat_export_msg_unpinned"));
break;
case "chat_invite_user_by_link":
htmlAction.append(getString("chat_export_join_by_link"));
break;
case "chat_screenshot":
htmlAction.append(getString("chat_export_chat_screenshot"));
break;
case "custom":
htmlAction.append(action.optString("message"));
break;
default:
htmlAction.append(getString("vkim_msg_unsupported") + " (").append(type).append(")");
break;
}
return htmlAction.append("</p>").toString();
}
private String getHtmlForAttach(List<JSONObject> attaches) throws JSONException {
StringBuilder rs = new StringBuilder();
for (JSONObject attach : attaches) {
String type = attach.getString("type");
JSONObject item = attach.optJSONObject(type);
if (item == null) continue;
switch (type) { // market_album is not supported
case "photo":
var sizes = item.getJSONArray("sizes");
rs.append("<a href=\"").append(sizes.getJSONObject(sizes.length() - 1).getString("url")).append("\" class=\"msg-attach-link\">").append(getString("chat_export_type_photo")).append("</a>");
break;
case "doc":
rs.append("<a href=\"").append(item.getString("url")).append("\" class=\"msg-attach-link\">").append(getString("chat_export_type_doc")).append("</a>");
break;
case "link":
rs.append("<a href=\"").append(item.getString("url")).append("\" class=\"msg-attach-link\">").append(getString("chat_export_type_link")).append("</a>");
break;
case "wall":
rs.append("<a href=\"https://" + VK_DOMAIN + "/wall").append(item.optString("to_id", item.optString("owner_id"))).append("_").append(item.getString("id")).append("\" class=\"msg-attach-link\">").append(getString("chat_export_type_wallpost")).append("</a>");
break;
case "sticker":
rs.append("<img style=\"width:128px;height:128px;\" src=\"").append(item.getJSONArray("images").getJSONObject(1).getString("url")).append("\"/>");
break;
case "audio_message":
rs.append("<a href=\"").append(item.optString("link_mp3")).append("\" class=\"msg-attach-link\">").append(getString("chat_export_type_audiomsg")).append("</a>");
break;
case "audio":
rs.append(getString("chat_export_type_audio")).append(" (").append(item.optString("artist")).append(" — ").append(item.optString("title")).append(")");
break;
case "video":
rs.append(getString("chat_export_type_video")).append(" (").append(getVideoHtml(item.optJSONObject("files"))).append(")");
break;
case "wall_reply":
var link = "https://" + VK_DOMAIN + "/wall" + item.optString("owner_id") + "_" + item.optString("post_id") + "?reply=" + item.optString("id");
rs.append("<a class=\"msg-attach-link\" href=\"").append(link).append("\">").append(getString("chat_export_type_wallreply")).append("</a>");
break;
case "story":
var story_link = "https://" + VK_DOMAIN + "/story" + item.optString("owner_id") + "_" + item.optString("id");
rs.append("<a class=\"msg-attach-link\" href=\"").append(story_link).append("\">").append(getString("chat_export_type_story")).append("</a>");
break;
case "podcast":
var podcast_link = "https://" + VK_DOMAIN + "/podcast" + item.optString("owner_id") + "_" + item.optString("id");
rs.append("<a class=\"msg-attach-link\" href=\"").append(podcast_link).append("\">").append(getString("chat_export_type_podcast")).append("</a>");
break;
case "gift":
rs.append("<img src=\"").append(item.optString("thumb_96")).append("\"/>");
break;
case "mini_app":
var app = item.optJSONObject("app");
if (app != null) {
var miniapp_link = "https://" + VK_DOMAIN + "/app" + app.optString("id");
rs.append("<a class=\"msg-attach-link\" href=\"").append(miniapp_link).append("\">").append(getString("chat_export_type_miniapp")).append("</a>");
break;
}
rs.append(getString("chat_export_error"));
break;
case "audio_playlist":
var playlist_link = "https://" + VK_DOMAIN + "/music/album/" + item.optString("owner_id") + "_" + item.optString("id") + "_" + item.optString("access_key");
rs.append("<a class=\"msg-attach-link\" href=\"").append(playlist_link).append("\">").append(getString("chat_export_type_playlist")).append(" (").append(item.optString("title")).append(")</a>");
break;
case "poll":
rs.append(getString(item.optBoolean("anonymous") ? "chat_export_poll_anon" : "chat_export_poll_publ")).append(" ").append(getString("chat_export_type_poll")).append(" ").append(getPollHtml(item));
break;
case "market":
var market_item_link = "https://" + VK_DOMAIN + "/market" + item.optString("owner_id") + "_" + item.optString("id");
rs.append("<a class=\"msg-attach-link\" href=\"").append(market_item_link).append("\">").append(getString("chat_export_type_market")).append("</a>");
break;
default:
rs.append(getString("vkim_msg_unsupported")).append(" (").append(type).append(")");
break;
}
rs.append(" ");
}
return rs.toString();
}
}
}
@@ -1,7 +1,5 @@
package ru.vtosters.lite.downloaders;
import static ru.vtosters.lite.utils.AndroidUtils.getString;
import android.annotation.SuppressLint;
import android.app.DownloadManager;
import android.content.BroadcastReceiver;
@@ -11,17 +9,20 @@ import android.content.IntentFilter;
import android.net.Uri;
import android.os.Environment;
import androidx.core.content.FileProvider;
import com.vk.core.dialogs.alert.VkAlertDialog;
import com.vtosters.lite.R;
import java.io.File;
import b.h.g.m.FileUtils;
import ru.vtosters.lite.ui.activities.APKInstallActivity;
import ru.vtosters.lite.utils.AndroidUtils;
public class OTADownloader {
private static final BroadcastReceiver callback = new BroadcastReceiver() {
@SuppressLint("Range")
@Override
public void onReceive(Context context, Intent intent) {
var extras = intent.getExtras();
@@ -31,32 +32,23 @@ public class OTADownloader {
var query = new DownloadManager.Query();
var id = extras.getLong(DownloadManager.EXTRA_DOWNLOAD_ID);
query.setFilterById(id);
var manager = (DownloadManager) AndroidUtils.getGlobalContext().getSystemService(Context.DOWNLOAD_SERVICE);
var manager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
var cursor = manager.query(query);
if (cursor.moveToFirst()) {
@SuppressLint("Range") var status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS));
if (status == DownloadManager.STATUS_SUCCESSFUL) {
var downloadDir = new File(Environment.getExternalStorageDirectory(), "Download");
if (!downloadDir.exists()) return;
var list = downloadDir.listFiles();
if (list == null || list.length == 0) return;
var maxIndex = -1;
for (File file : list) {
var name = file.getAbsolutePath().substring(file.getAbsolutePath().lastIndexOf("/") + 1);
if (file.isFile()
&& name.matches("VTLite-\\d+\\.apk"))
maxIndex = Math.max(maxIndex, Integer.parseInt(name.replaceFirst(".+-(\\d+).+", "$1")));
}
File apk = maxIndex == -1 ? new File(downloadDir, "VTLite.apk") : new File(downloadDir, "VTLite-" + maxIndex + ".apk");
APKInstallActivity.installOta(context, FileUtils.uriFromFile(apk));
var uriStr = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
var apk = new File(uriStr.replace("file://", ""));
var uri = FileProvider.getUriForFile(context, "com.vtosters.lite.common.VKFileProvider", apk);
APKInstallActivity.installOta(context, uri);
} else if (status == DownloadManager.STATUS_FAILED) {
AndroidUtils.sendToast(getString("downloaderr"));
AndroidUtils.sendToast(context.getString(R.string.downloaderr));
}
}
}
};
public static void downloadBuild(String url) {
public static void downloadBuild(String url, String commitSHA) {
var context = AndroidUtils.getGlobalContext();
try {
var uri = Uri.parse(url);
@@ -65,8 +57,8 @@ public class OTADownloader {
var request = new DownloadManager.Request(uri);
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
request.setTitle("VTLite.apk");
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "VTLite.apk");
request.setTitle(AndroidUtils.getString("downloading_update"));
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "VTLite-" + commitSHA + ".apk");
var manager = (DownloadManager) AndroidUtils.getGlobalContext().getSystemService(Context.DOWNLOAD_SERVICE);
manager.enqueue(request);
@@ -74,12 +66,12 @@ public class OTADownloader {
e.printStackTrace();
new VkAlertDialog.Builder(context)
.setTitle(AndroidUtils.getString("update_error"))
.setMessage(AndroidUtils.getString("update_error_text"))
.setPositiveButton(AndroidUtils.getString("no"),
.setTitle(R.string.update_error)
.setMessage(R.string.update_error_text)
.setPositiveButton(R.string.no,
(dialog, which) -> dialog.cancel()
)
.setNeutralButton(AndroidUtils.getString("yes"),
.setNeutralButton(R.string.yes,
(dialog, which) -> {
var intent = new Intent(Intent.ACTION_VIEW)
.setData(Uri.parse("https://github.com/vtosters/lite/releases/latest/download/VTLite.apk"));
@@ -7,32 +7,35 @@ import android.app.DownloadManager;
import android.content.Context;
import android.net.Uri;
import android.os.Environment;
import android.util.Log;
import com.vk.dto.common.ImageSize;
import com.vk.dto.stories.model.StoryEntry;
import org.json.JSONArray;
import java.util.List;
public class StoryDownloader {
public static Runnable injectButton(StoryEntry story) {
return () -> {
// if story is a pic
if (story.T1()) {
List<ImageSize> imageSizes = story.E.Q.t1();
String url = imageSizes.get(imageSizes.size() - 1).url;
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
request.setTitle("photo" + story.E.a + ".jpg");
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_PICTURES, "photo" + story.E.a + ".jpg");
request.allowScanningByMediaScanner();
((DownloadManager) getGlobalContext().getSystemService(Context.DOWNLOAD_SERVICE)).enqueue(request);
return;
}
return () -> downloadStory(story);
}
// if story is a video
if (story.F != null) {
downloadVideo(story.F, null);
}
};
public static void downloadStory(StoryEntry story){
if (story.T1()) {
List<ImageSize> imageSizes = story.E.Q.t1();
String url = imageSizes.get(imageSizes.size() - 1).url;
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
request.setTitle("photo" + story.E.a + ".jpg");
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_PICTURES, "photo" + story.E.a + ".jpg");
request.allowScanningByMediaScanner();
((DownloadManager) getGlobalContext().getSystemService(Context.DOWNLOAD_SERVICE)).enqueue(request);
return;
}
// if story is a video
if (story.F != null) {
downloadVideo(story.F, null);
}
}
}
@@ -1,35 +1,35 @@
package ru.vtosters.lite.downloaders;
import static ru.vtosters.lite.net.Request.makeRequest;
import static ru.vtosters.lite.proxy.ProxyUtils.getApi;
import static ru.vtosters.lite.utils.AccountManagerUtils.getUserToken;
import static ru.vtosters.lite.utils.AndroidUtils.getGlobalContext;
import static ru.vtosters.lite.utils.AndroidUtils.getIdentifier;
import static ru.vtosters.lite.utils.AndroidUtils.getString;
import android.app.DownloadManager;
import android.app.ProgressDialog;
import android.content.Context;
import android.net.Uri;
import android.os.Environment;
import android.text.TextUtils;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import android.widget.Toast;
import com.vk.core.dialogs.alert.VkAlertDialog;
import com.vk.core.dialogs.bottomsheet.MenuBottomSheetAction;
import com.vk.core.util.ToastUtils;
import com.vk.dto.common.VideoFile;
import com.vk.dto.stories.model.StoryEntry;
import com.vtosters.lite.R;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import ru.vtosters.lite.utils.ExternalLinkParser;
import ru.vtosters.lite.utils.LifecycleUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import ru.vtosters.lite.utils.AndroidUtils;
import ru.vtosters.lite.utils.ExternalLinkParser;
import static ru.vtosters.lite.net.Request.makeRequest;
import static ru.vtosters.lite.proxy.ProxyUtils.getApi;
import static ru.vtosters.lite.utils.AccountManagerUtils.getUserToken;
import static ru.vtosters.lite.utils.ThemesUtils.getTextAttr;
public class VideoDownloader {
private static final int DOWNLOAD_ID = 0;
@@ -48,7 +48,7 @@ public class VideoDownloader {
public static void injectAction(ArrayList<MenuBottomSheetAction> list, VideoFile video) {
if (!video.U && !video.I1()) {
addAction(list, DOWNLOAD_ID, R.drawable.ic_download_outline_24, R.string.download, 9);
addAction(list, OPEN_EXTERNAL_LINK_ID, R.drawable.ic_link_outline_28, getIdentifier("interfacevideoext_short", "string"), 9);
addAction(list, OPEN_EXTERNAL_LINK_ID, R.drawable.ic_link_outline_28, R.string.interfacevideoext_short, 9);
}
}
@@ -57,82 +57,110 @@ public class VideoDownloader {
}
public static void downloadVideo(VideoFile videoFile, Context context) {
final List<String> list = new ArrayList<>();
final List<String> qualities = new ArrayList<>();
final List<String> urls = new ArrayList<>();
if (!Objects.equals(videoFile.e, "")) {
list.add("240p");
urls.add(videoFile.e);
}
if (!Objects.equals(videoFile.f, "")) {
list.add("360p");
urls.add(videoFile.f);
}
if (!Objects.equals(videoFile.g, "")) {
list.add("480p");
urls.add(videoFile.g);
}
if (!Objects.equals(videoFile.h, "")) {
list.add("720p");
urls.add(videoFile.h);
}
if (!Objects.equals(videoFile.B, "")) {
list.add("1080p");
urls.add(videoFile.B);
}
if (!Objects.equals(videoFile.C, "")) {
list.add("1440p");
urls.add(videoFile.C);
}
if (!Objects.equals(videoFile.D, "")) {
list.add("2160p");
if (!TextUtils.isEmpty(videoFile.D)) {
qualities.add("2160p");
urls.add(videoFile.D);
}
if (list.isEmpty()) {
ToastUtils.a(getString("videodownloaderr"));
if (!TextUtils.isEmpty(videoFile.C)) {
qualities.add("1440p");
urls.add(videoFile.C);
}
if (!TextUtils.isEmpty(videoFile.B)) {
qualities.add("1080p");
urls.add(videoFile.B);
}
if (!TextUtils.isEmpty(videoFile.h)) {
qualities.add("720p");
urls.add(videoFile.h);
}
if (!TextUtils.isEmpty(videoFile.g)) {
qualities.add("480p");
urls.add(videoFile.g);
}
if (!TextUtils.isEmpty(videoFile.f)) {
qualities.add("360p");
urls.add(videoFile.f);
}
if (!TextUtils.isEmpty(videoFile.e)) {
qualities.add("240p");
urls.add(videoFile.e);
}
Context finalContext = context != null ? context : LifecycleUtils.getCurrentActivity();
if (qualities.isEmpty()) {
ToastUtils.a(finalContext.getString(R.string.videodownloaderr));
return;
}
var max = new String[list.size()];
var adapter = new ArrayAdapter(
finalContext,
android.R.layout.simple_list_item_1,
qualities.toArray(new String[0])) {
@Override
public View getView(int position, View convertView, ViewGroup parent) {
TextView textView = (TextView) super.getView(position, convertView, parent);
textView.setTextColor(getTextAttr());
return textView;
}
};
for (int i = 0; i < list.size(); i++) {
max[i] = list.get(i);
}
if (context == null) {
var url = urls.get(urls.size() - 1);
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
request.setTitle(videoFile.toString());
request.allowScanningByMediaScanner();
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_MOVIES, videoFile + ".mp4");
((DownloadManager) getGlobalContext().getSystemService(Context.DOWNLOAD_SERVICE)).enqueue(request);
return;
}
new VkAlertDialog.Builder(context)
.setItems(max, ((dialog, which) -> {
var url = urls.get(which);
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
request.setTitle(videoFile.toString());
request.allowScanningByMediaScanner();
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_MOVIES, videoFile + ".mp4");
((DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE)).enqueue(request);
}))
.show();
new VkAlertDialog.Builder(finalContext)
.setAdapter(adapter, (dialog, which) -> {
try {
var url = urls.get(which);
var request = new DownloadManager.Request(Uri.parse(url))
.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
.setTitle(videoFile.toString())
.setDestinationInExternalPublicDir(Environment.DIRECTORY_MOVIES, videoFile + ".mp4");
request.allowScanningByMediaScanner();
((DownloadManager) finalContext.getSystemService(Context.DOWNLOAD_SERVICE)).enqueue(request);
} catch (Throwable ignored) {
}
})
.show();
}
public static void parseVideoLink(String url, Context ctx) {
if (url.startsWith("http://")) {
url.replace("http://", "https://");
} else if (url.startsWith("vk.com"))
url = "https://" + url;
if (url.contains("vk.com/story")) {
ToastUtils.a(AndroidUtils.getString("video_dl_stories_not_supported"));
String storyId = url.substring(url.startsWith("https") ? 20 : 19) + "_story";
ProgressDialog progressDialog = new ProgressDialog(ctx);
progressDialog.setMessage(ctx.getString(R.string.video_dl_progress));
progressDialog.show();
makeRequest("https://" + getApi() + "/method/stories.getById?stories=" + storyId + "&v=5.99&access_token=" + getUserToken(),
response -> {
progressDialog.cancel();
try {
JSONObject mainJson = new JSONObject(response);
JSONObject responseJson = mainJson.getJSONObject("response").optJSONArray("items").optJSONObject(0);
StoryEntry story = new StoryEntry(responseJson);
StoryDownloader.downloadStory(story);
} catch (JSONException e) {
e.printStackTrace();
Toast.makeText(ctx, ctx.getString(R.string.video_dl_error), Toast.LENGTH_SHORT).show();
}
});
return;
}
if (!url.contains("vk.com/video")) {
ToastUtils.a(AndroidUtils.getString("video_dl_wrong_link"));
ToastUtils.a(ctx.getString(R.string.video_dl_wrong_link));
return;
}
@@ -140,7 +168,7 @@ public class VideoDownloader {
String ownerId = videoId.split("_")[0];
final ProgressDialog progressDialog = new ProgressDialog(ctx);
progressDialog.setMessage(AndroidUtils.getString("video_dl_progress"));
progressDialog.setMessage(ctx.getString(R.string.video_dl_progress));
progressDialog.show();
makeRequest("https://" + getApi() + "/method/video.get?owner_id=" + ownerId + "&videos=" + videoId + "&v=5.99&access_token=" + getUserToken(),
@@ -156,7 +184,7 @@ public class VideoDownloader {
downloadVideo(videoFile, ctx);
} catch (JSONException e) {
e.printStackTrace();
Toast.makeText(ctx, AndroidUtils.getString("video_dl_error"), Toast.LENGTH_SHORT).show();
Toast.makeText(ctx, ctx.getString(R.string.video_dl_error), Toast.LENGTH_SHORT).show();
}
});
}
@@ -0,0 +1,96 @@
package ru.vtosters.lite.downloaders.messages;
public class Constants {
public static final String VK_DOMAIN = "vk.com";
public static final String HTML_DOC_STYLE = ".vtex-milk-msg {\n" +
" border-radius: 8px;\n" +
" padding: 24px;\n" +
" background: white;\n" +
" margin: 16px;\n" +
" box-shadow: 0 4px 8px 0 rgba(0,0,0,0.07);\n" +
" word-wrap: break-word;\n" +
"}\n" +
"\n" +
".vtex-milk-msgcont br {\n" +
" display: none;\n" +
"}\n" +
"\n" +
".vtex-milk-msg p {\n" +
" margin: 0;\n" +
"}\n" +
"\n" +
".vtex-milk-msg.vtex-milk-msg-out {\n" +
" background: #ADD3FF;\n" +
"}\n" +
"\n" +
"p.msg-attaches {\n" +
" margin-top: 8px;\n" +
" font-style: italic;\n" +
"}\n" +
"p.chat-action {\n" +
" font-style: italic;\n" +
"}\n" +
"\n" +
".vtex-milk-header {\n" +
" border-radius: 8px;\n" +
" padding: 24px;\n" +
" background: white;\n" +
" margin: 16px;\n" +
" box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);\n" +
"}\n" +
"\n" +
"h4.vtex-milk-header-txt {\n" +
" margin: 0;\n" +
"}\n" +
"\n" +
"h5.vtex-milk-header-sub {\n" +
" margin: 0;\n" +
" padding-top: 8px;\n" +
"}\n" +
"\n" +
"a.msg-attach-link {\n" +
" color: #5181B8;\n" +
" text-decoration: none;\n" +
"}\n" +
"\n" +
".msg-from a {\n" +
" color: #5181B8;\n" +
" text-decoration: none;\n" +
"}\n" +
"\n" +
".msg-from-date {\n" +
" color: rgba(0, 0, 0, 0.75);\n" +
"}\n" +
"\n" +
"body {\n" +
" font-family: Roboto, system-ui, sans-serif;\n" +
"}\n" +
"\n" +
".msg-body {\n" +
" padding-top: 4px;\n" +
"}\n" +
".msg-reply {\n" +
" border-left: 3px solid #7aa7f6;\n" +
" margin-top: 5px;\n" +
" padding-left: 3px;\n" +
"}\n\n" +
".msg-reply p {\n" +
" padding-left: 5px;\n" +
"}\n\n" +
".msg-reply p.reply-header {\n" +
" color: #5181B8;\n" +
"}\n\n" +
".msg-reply p.reply-content {\n" +
" color: #000000;\n" +
"}\n" +
".vtex-milk-msg.poll {\n" +
" width: 45%;\n" +
" background: linear-gradient(to bottom right, #707c8c, #4d5565);\n" +
" font-style: normal;" +
"}\n" +
".vtex-milk-msg.poll p { color: white; }\n" +
".vtex-milk-msg.poll hr { border-top: 1px; background: #ffffff; }\n" +
"\n" +
"";
}
@@ -0,0 +1,17 @@
package ru.vtosters.lite.downloaders.messages;
import org.json.JSONException;
import ru.vtosters.lite.downloaders.messages.items.MiniMsg;
import ru.vtosters.lite.downloaders.messages.items.MiniUser;
// This class defines everything which is needed to create an output file
public abstract class DialogDownloaderFormatProvider {
// Internal, for JSON/HTML
public abstract String provideDocumentStart(String dialogName, String date);
public abstract String provideDocumentEnd();
public abstract String provideHeader(String dialogName, String date);
public abstract String provideMessage(MiniMsg message, MiniUser user) throws JSONException;
}
@@ -0,0 +1,402 @@
package ru.vtosters.lite.downloaders.messages;
import com.vtosters.lite.R;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import ru.vtosters.lite.downloaders.messages.items.MiniMsg;
import ru.vtosters.lite.downloaders.messages.items.MiniUser;
import ru.vtosters.lite.utils.AndroidUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
import static ru.vtosters.lite.encryption.EncryptProvider.decryptMessage;
import static ru.vtosters.lite.utils.AccountManagerUtils.getUserId;
import static ru.vtosters.lite.utils.AndroidUtils.getArray;
public class HtmlDialogDownloaderFormatProvider extends DialogDownloaderFormatProvider {
@Override
public String provideDocumentStart(String dialogName, String date) {
return "<html><head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><meta charset=\"utf-8\"><title>"
+ AndroidUtils.getString(R.string.chat_export_title)
+ "</title><style>"
+ Constants.HTML_DOC_STYLE
+ "</style></head><body>";
}
@Override
public String provideDocumentEnd() {
return "</div></body></html>";
}
@Override
public String provideHeader(String dialogName, String date) {
return "<div class=\"vtex-milk-header\"><h4 class=\"vtex-milk-header-txt\">VTLite</h4><h5 class=\"vtex-milk-header-sub\">"
+ AndroidUtils.getString(R.string.chat_export_header)
+ " (" + date + ")</h5></div><div class=\"vtex-milk-msgcont\">";
}
@Override
public String provideMessage(MiniMsg message, MiniUser user) throws JSONException {
String isOut = (user.id == getUserId()) ? " vtex-milk-msg-out" : "";
String geoPosition = "";
if (message.geo != null) {
var coordinates = message.geo.optJSONObject("coordinates");
if (coordinates != null) {
var latitude = coordinates.optDouble("latitude");
var longitude = coordinates.optDouble("longitude");
var ll = longitude + "," + latitude;
geoPosition = "<a href=\"https://yandex.ru/maps/?ll=" + ll + "&mode=search&sll=" + ll + "&text=" + latitude + "," + longitude + "&z=12\">" +
"<img src=\"https://static-maps.yandex.ru/1.x/?ll=" + longitude + "," + latitude + "&z=12&l=map&size=300,200&pt=" + ll + ",vkgrm\"/>" +
"</a>";
}
}
String chatAction = (message.chat_action != null) ? getHtmlForChatAction(message.chat_action) : "";
String attaches = (message.attachments.size() > 0) ? "<p class=\"msg-attaches\">" + AndroidUtils.getString(R.string.attachments) + ": " + getHtmlForAttach(message.attachments) + "</p>" : "";
String reply = (message.reply_message != null) ? provideReply(message.reply_message) : "";
String fwdMessages = (message.fwd_messages.size() > 0) ? provideForwardMessages(message.fwd_messages) : "";
String userLink = (user.isGroup ? "club" : "id") + user.id;
return "<div class=\"vtex-milk-msg" + isOut + "\" id=\"" + message.id + "\">" +
"<p class=\"msg-from\">" + AndroidUtils.getString(R.string.from_lower)
+ " <a href=\"https://" + Constants.VK_DOMAIN + "/" + userLink + "\">" + user.firstName + " " + user.lastName
+ "</a> <span class=\"msg-from-date\">" + MessagesDownloader.formatTime(message.date) + "</span></p>" + // TODO photo100
"<p class=\"msg-body\">" + mentionsReplace(message.text) + "</p>" +
geoPosition +
chatAction +
attaches +
reply +
fwdMessages +
"</div>";
}
private String provideReply(JSONObject replyMessage) {
var text = mentionsReplace(decryptMessage(replyMessage.optString("text"), replyMessage.optInt("peer_id")));
var fwdMessages = replyMessage.optJSONArray("fwd_messages");
var user = MessagesDownloader.usersArray.get(replyMessage.optInt("from_id"));
if (text.isEmpty()) {
if (fwdMessages != null) {
var length = fwdMessages.length();
if (length > 0) text = length + " " + pluralMsg(length);
} else text = AndroidUtils.getString(R.string.attachment); // Вложение
}
return "<a href=\"#" + replyMessage.optInt("id") + "\" class=\"msg-attach-link\">" +
"<div class=\"msg-reply\">" +
"<p class=\"reply-header\">" + AndroidUtils.getString(R.string.vkim_msg_fwd_single) + "</p>" +
"<p class=\"reply-content\">" + user.firstName + " " + user.lastName + ": " + text + "</p>" +
"</div>" +
"</a>";
}
private String provideForwardMessages(List<JSONObject> fwd_messages) throws JSONException {
StringBuilder fwdmsgs = new StringBuilder("<div class=\"msg-reply\">");
for (JSONObject fwd_message : fwd_messages) {
if (fwd_message == null) continue;
List<JSONObject> attaches = new ArrayList<>();
JSONArray arr = fwd_message.optJSONArray("attachments");
if (arr != null) {
for (int i = 0; i < arr.length(); i++) {
try {
attaches.add(arr.getJSONObject(i));
} catch (JSONException e) {
e.printStackTrace();
}
}
}
List<JSONObject> fwdsMsg = new ArrayList<>();
JSONArray fwd_msgs = fwd_message.optJSONArray("fwd_messages");
if (fwd_msgs != null) {
for (int i = 0; i < fwd_msgs.length(); i++) {
try {
fwdsMsg.add(fwd_msgs.getJSONObject(i));
} catch (JSONException e) {
e.printStackTrace();
}
}
}
JSONObject reply_message = fwd_message.optJSONObject("reply_message");
var attachesHtml = (attaches.size() > 0) ? AndroidUtils.getString(R.string.attachments) + ": " + getHtmlForAttach(attaches) : "";
var replyHtml = (reply_message != null) ? provideReply(reply_message) : "";
var fwdsHtml = (fwdsMsg.size() > 0) ? provideForwardMessages(fwdsMsg) : ""; // recursion XD :>
var userId = fwd_message.optInt("from_id");
var peerId = fwd_message.optInt("peer_id");
var text = mentionsReplace(decryptMessage(fwd_message.optString("text"), peerId));
var userLink = ((userId < 0) ? "club" : "id") + Math.abs(userId);
fwdmsgs.append("<p class=\"reply-content\"><a class=\"msg-attach-link\" href=\"https://" + Constants.VK_DOMAIN + "/")
.append(userLink)
.append("\">@")
.append(userLink)
.append("</a>: ")
.append(text)
.append(attachesHtml)
.append(replyHtml)
.append(fwdsHtml);
}
return fwdmsgs.append("</div>").toString();
}
private String mentionsReplace(String text) {
var matcher = Pattern.compile("\\[((?:club|id|public)\\d+)\\|([^]]+)\\]").matcher(text);
return matcher.replaceAll("<a class=\"msg-attach-link\" href=\"https://" + Constants.VK_DOMAIN + "/$1\">$2</a>");
}
private String pluralMsg(int count) {
String[] plurals = getArray(R.array.msg_plurals);
var hundredModulo = count % 100;
var tenModulo = count % 10;
if (hundredModulo > 10 && hundredModulo < 20) return plurals[2];
else if (tenModulo > 1 && tenModulo < 5) return plurals[1];
else if (tenModulo == 1) return plurals[0];
return plurals[2];
}
private String getHtmlForChatAction(JSONObject action) {
var type = action.optString("type");
StringBuilder htmlAction = new StringBuilder("<p class=\"chat-action\">");
switch (type) {
case "chat_photo_update" -> htmlAction.append(AndroidUtils.getString(R.string.chat_export_avatar_update));
case "chat_photo_remove" -> htmlAction.append(AndroidUtils.getString(R.string.chat_export_avatar_remove));
case "chat_create" -> {
htmlAction.append(AndroidUtils.getString(R.string.chat_export_chat_created));
var name = action.optString("text");
if (!name.isEmpty())
htmlAction.append(" \"").append(name).append("\"");
}
case "chat_title_update" -> {
var newName = action.optString("text");
htmlAction.append(AndroidUtils.getString(R.string.chat_export_name_changed));
if (!newName.isEmpty())
htmlAction.append(" ")
.append(AndroidUtils.getString(R.string.chat_export_to))
.append(" \"")
.append(newName)
.append("\"");
}
case "chat_invite_user" -> {
var newMemberId = action.optInt("member_id");
htmlAction.append(AndroidUtils.getString(R.string.chat_export_user_added))
.append(" ");
if (newMemberId != 0) {
int id;
String endpoint;
if (newMemberId >= 0) {
id = R.string.chat_export_type_user;
endpoint = "id" + newMemberId;
} else {
id = R.string.chat_export_type_group;
endpoint = "club" + -newMemberId;
}
htmlAction.append(AndroidUtils.getString(id))
.append(" <a href=\"https://" + Constants.VK_DOMAIN + "/")
.append(endpoint)
.append("\" class=\"msg-attach-link\">@")
.append(endpoint)
.append("</a>");
}
}
case "chat_kick_user" -> {
var removedMemberId = action.optInt("member_id");
htmlAction.append(AndroidUtils.getString(R.string.chat_export_user_kicked))
.append(" ");
if (removedMemberId != 0) {
int id;
String endpoint;
if (removedMemberId >= 0) {
id = R.string.chat_export_type_user;
endpoint = "id" + removedMemberId;
} else {
id = R.string.chat_export_type_group;
endpoint = "club" + -removedMemberId;
}
htmlAction.append(AndroidUtils.getString(id))
.append(" <a href=\"https://" + Constants.VK_DOMAIN + "/")
.append(endpoint)
.append("\" class=\"msg-attach-link\">@")
.append(endpoint)
.append("</a>");
}
}
case "chat_pin_message" -> {
var message = action.optString("message");
htmlAction.append(AndroidUtils.getString(R.string.chat_export_msg_pinned));
if (!message.isEmpty()) htmlAction.append(" \"").append(message).append("\"");
}
case "chat_unpin_message" -> htmlAction.append(AndroidUtils.getString(R.string.chat_export_msg_unpinned));
case "chat_invite_user_by_link" ->
htmlAction.append(AndroidUtils.getString(R.string.chat_export_join_by_link));
case "chat_screenshot" -> htmlAction.append(AndroidUtils.getString(R.string.chat_export_chat_screenshot));
case "custom" -> htmlAction.append(action.optString("message"));
default ->
htmlAction.append(AndroidUtils.getString(R.string.vkim_msg_unsupported)).append(" (").append(type).append(")");
}
return htmlAction.append("</p>").toString();
}
private String getHtmlForAttach(List<JSONObject> attaches) throws JSONException {
StringBuilder rs = new StringBuilder();
for (JSONObject attach : attaches) {
String type = attach.getString("type");
JSONObject item = attach.optJSONObject(type);
if (item == null) continue;
switch (type) { // market_album is not supported
case "photo" -> {
var sizes = item.getJSONArray("sizes");
rs.append("<a href=\"")
.append(sizes.getJSONObject(sizes.length() - 1).getString("url"))
.append("\" class=\"msg-attach-link\">")
.append(AndroidUtils.getString(R.string.chat_export_type_photo))
.append("</a>");
}
case "doc" -> rs.append("<a href=\"")
.append(item.getString("url"))
.append("\" class=\"msg-attach-link\">")
.append(AndroidUtils.getString(R.string.chat_export_type_doc))
.append("</a>");
case "link" -> rs.append("<a href=\"")
.append(item.getString("url"))
.append("\" class=\"msg-attach-link\">")
.append(AndroidUtils.getString(R.string.chat_export_type_link))
.append("</a>");
case "wall" -> rs.append("<a href=\"https://" + Constants.VK_DOMAIN + "/wall")
.append(item.optString("to_id", item.optString("owner_id")))
.append("_").append(item.getString("id"))
.append("\" class=\"msg-attach-link\">")
.append(AndroidUtils.getString(R.string.chat_export_type_wallpost))
.append("</a>");
case "sticker" -> rs.append("<img style=\"width:128px;height:128px;\" src=\"")
.append(item.getJSONArray("images")
.getJSONObject(1)
.getString("url"))
.append("\"/>");
case "audio_message" -> rs.append("<a href=\"")
.append(item.optString("link_mp3"))
.append("\" class=\"msg-attach-link\">")
.append(AndroidUtils.getString(R.string.chat_export_type_audiomsg))
.append("</a>");
case "audio" -> rs.append(AndroidUtils.getString(R.string.chat_export_type_audio))
.append(" (")
.append(item.optString("artist"))
.append(" — ")
.append(item.optString("title"))
.append(")");
case "video" -> rs.append(AndroidUtils.getString(R.string.chat_export_type_video))
.append(" (")
.append(MessagesDownloader.getVideoHtml(item.optJSONObject("files")))
.append(")");
case "wall_reply" -> {
var link = String.format("https://" + Constants.VK_DOMAIN + "/wall%s_%s?reply=%s",
item.optString("owner_id"),
item.optString("post_id"),
item.optString("id"));
rs.append("<a class=\"msg-attach-link\" href=\"")
.append(link).append("\">")
.append(AndroidUtils.getString(R.string.chat_export_type_wallreply))
.append("</a>");
}
case "story" -> {
var story_link = String.format("https://" + Constants.VK_DOMAIN + "/story%s_%s",
item.optString("owner_id"),
item.optString("id"));
rs.append("<a class=\"msg-attach-link\" href=\"")
.append(story_link)
.append("\">")
.append(AndroidUtils.getString(R.string.chat_export_type_story))
.append("</a>");
}
case "podcast" -> {
var podcast_link = String.format("https://" + Constants.VK_DOMAIN + "/podcast%s_%s",
item.optString("owner_id"),
item.optString("id"));
rs.append("<a class=\"msg-attach-link\" href=\"")
.append(podcast_link)
.append("\">")
.append(AndroidUtils.getString(R.string.chat_export_type_podcast))
.append("</a>");
}
case "gift" -> rs.append("<img src=\"")
.append(item.optString("thumb_96"))
.append("\"/>");
case "mini_app" -> {
var app = item.optJSONObject("app");
if (app != null) {
var miniapp_link = String.format("https://" + Constants.VK_DOMAIN + "/app%s", app.optString("id"));
rs.append("<a class=\"msg-attach-link\" href=\"")
.append(miniapp_link)
.append("\">")
.append(AndroidUtils.getString(R.string.chat_export_type_miniapp))
.append("</a>");
break;
}
rs.append(AndroidUtils.getString(R.string.chat_export_error));
}
case "audio_playlist" -> {
var playlist_link = String.format("https://" + Constants.VK_DOMAIN + "/music/album/%s_%s_%s",
item.optString("owner_id"),
item.optString("id"),
item.optString("access_key"));
rs.append("<a class=\"msg-attach-link\" href=\"")
.append(playlist_link)
.append("\">")
.append(AndroidUtils.getString(R.string.chat_export_type_playlist))
.append(" (")
.append(item.optString("title"))
.append(")</a>");
}
case "poll" ->
rs.append(AndroidUtils.getString(item.optBoolean("anonymous") ? R.string.chat_export_poll_anon : R.string.chat_export_poll_publ))
.append(" ")
.append(AndroidUtils.getString(R.string.chat_export_type_poll))
.append(" ")
.append(MessagesDownloader.getPollHtml(item));
case "market" -> {
var market_item_link = String.format("https://" + Constants.VK_DOMAIN + "/market%s_%s",
item.optString("owner_id"),
item.optString("id"));
rs.append("<a class=\"msg-attach-link\" href=\"")
.append(market_item_link)
.append("\">")
.append(AndroidUtils.getString(R.string.chat_export_type_market))
.append("</a>");
}
default -> rs.append(AndroidUtils.getString(R.string.vkim_msg_unsupported))
.append(" (")
.append(type)
.append(")");
}
rs.append(" ");
}
return rs.toString();
}
}
@@ -0,0 +1,162 @@
package ru.vtosters.lite.downloaders.messages;
import android.util.Log;
import android.util.SparseArray;
import androidx.annotation.Nullable;
import com.vk.core.util.ToastUtils;
import com.vtosters.lite.R;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Request;
import okhttp3.Response;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import ru.vtosters.lite.di.singleton.VtOkHttpClient;
import ru.vtosters.lite.downloaders.messages.items.MiniMsg;
import ru.vtosters.lite.downloaders.messages.items.MiniUser;
import ru.vtosters.lite.proxy.ProxyUtils;
import ru.vtosters.lite.utils.AccountManagerUtils;
import ru.vtosters.lite.utils.AndroidUtils;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
public class MessagesDownloader {
private static final int[] VIDEO_QUALITIES = { 144, 240, 360, 480, 720, 1080, 1440, 2160 };
private static final SimpleDateFormat SDFORMAT = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss", AndroidUtils.getResources().getConfiguration().locale);
public static SparseArray<MiniUser> usersArray;
public static String formatTime(long now) {
return SDFORMAT.format(new Date(now));
}
public static String getPollHtml(JSONObject poll) {
StringBuilder pollHtml = new StringBuilder(
"<div class=\"vtex-milk-msg poll\">" +
"<p style=\"text-align:center;\">" + poll.optString("question") + "</p>"
);
var answers = poll.optJSONArray("answers");
for (int i = 0; i < answers.length(); i++) {
var jsonObject = answers.optJSONObject(i);
if (jsonObject != null)
pollHtml.append("<hr><p>")
.append(jsonObject.optString("text"))
.append(" · ")
.append(jsonObject.optString("votes"))
.append(" (")
.append(jsonObject.optString("rate"))
.append("%)</p>");
}
pollHtml.append("<hr><p style=\"text-align:center;\">")
.append(AndroidUtils.getString(R.string.chat_export_poll_voted))
.append(" ")
.append(poll.optString("votes"))
.append(" ")
.append(AndroidUtils.getString(R.string.chat_export_users_name))
.append("</p>")
.append("</div>");
return pollHtml.toString();
}
public static String getVideoHtml(JSONObject files) throws JSONException {
if (files == null) return AndroidUtils.getString(R.string.chat_export_video_nolinks);
Iterator<? extends String> keys = files.keys();
StringBuilder videoHtml = new StringBuilder();
int i = 0;
while (keys.hasNext() && i < VIDEO_QUALITIES.length) {
var key = keys.next();
if (key.equals("mp4_" + VIDEO_QUALITIES[i])) {
videoHtml.append("<a class=\"msg-attach-link\" href=\"")
.append(files.get(key))
.append("\">")
.append(VIDEO_QUALITIES[i])
.append("p</a>")
.append(" ");
}
++i;
}
return videoHtml.toString();
}
public void downloadDialog(int peer_id, DialogDownloaderFormatProvider format, File out) throws Exception {
FileOutputStream fos = new FileOutputStream(out);
// fix TODO with conversation name (API method messages.getConversationsById)
fos.write(format.provideDocumentStart("TODO", formatTime(System.currentTimeMillis())).getBytes(StandardCharsets.UTF_8));
var url = String.format("https://" + ProxyUtils.getApi() + "/method/messages.getHistory?extended=1&v=5.140&offset=%d&count=200&peer_id=%d&access_token=%s",
0,
peer_id,
AccountManagerUtils.getUserToken());
var req = new Request.a()
.b(url)
.a();
VtOkHttpClient.getInstance().a(req).a(new Callback() {
@Override
public void a(Call call, IOException e) {
Log.d("MessagesDownloader", e+"");
}
@Override
public void a(Call call, Response response)
throws IOException {
try {
JSONObject obj = new JSONObject(response.a().g()).getJSONObject("response");
SparseArray<MiniUser> users = new SparseArray<>();
parseUsers(users, obj.optJSONArray("profiles"), false);
parseUsers(users, obj.optJSONArray("groups"), true);
List<MiniMsg> msgs = parseMessages(obj.getJSONArray("items"));
usersArray = users;
fos.write(format.provideHeader("TODO", formatTime(System.currentTimeMillis())).getBytes(StandardCharsets.UTF_8));
for (MiniMsg msg : msgs) {
fos.write(format.provideMessage(msg, users.get(msg.fromId)).getBytes(StandardCharsets.UTF_8));
}
fos.write(format.provideDocumentEnd().getBytes(StandardCharsets.UTF_8));
fos.close();
ToastUtils.a(AndroidUtils.getString(R.string.saved_as) + " " + out.getAbsolutePath());
} catch (IOException | JSONException e) {
e.printStackTrace();
}
}
});
}
private List<MiniMsg> parseMessages(JSONArray jsonArray) throws JSONException {
List<MiniMsg> thing = new ArrayList<>();
for (int i = 0; i < jsonArray.length(); i++)
thing.add(new MiniMsg(jsonArray.getJSONObject(i)));
return thing;
}
private void parseUsers(SparseArray<MiniUser> array, @Nullable JSONArray jsonArray, boolean groups) throws JSONException {
if (jsonArray == null) return;
for (int i = 0; i < jsonArray.length(); i++) {
MiniUser user = new MiniUser(jsonArray.getJSONObject(i), groups);
array.put(groups ? -user.id : user.id, user);
}
}
}
@@ -0,0 +1,42 @@
package ru.vtosters.lite.downloaders.messages.items;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.List;
import static ru.vtosters.lite.encryption.EncryptProvider.decryptMessage;
public class MiniMsg {
public long date;
public int fromId;
public int id; // message id
public String text;
public List<JSONObject> attachments;
public List<JSONObject> fwd_messages;
public JSONObject reply_message; // one reply
public JSONObject chat_action;
public JSONObject geo;
public MiniMsg(JSONObject obj) throws JSONException {
date = obj.getLong("date") * 1000;
fromId = obj.getInt("from_id");
id = obj.getInt("id");
text = decryptMessage(obj.getString("text"), obj.getInt("peer_id"));
reply_message = obj.optJSONObject("reply_message");
chat_action = obj.optJSONObject("action");
geo = obj.optJSONObject("geo");
attachments = new ArrayList<>();
fwd_messages = new ArrayList<>();
JSONArray arr = obj.optJSONArray("attachments");
if (arr != null)
for (int i = 0; i < arr.length(); i++) attachments.add(arr.getJSONObject(i));
JSONArray fwdArr = obj.optJSONArray("fwd_messages");
if (fwdArr != null)
for (int i = 0; i < fwdArr.length(); i++) fwd_messages.add(fwdArr.getJSONObject(i));
}
}
@@ -0,0 +1,20 @@
package ru.vtosters.lite.downloaders.messages.items;
import org.json.JSONException;
import org.json.JSONObject;
public class MiniUser {
public String firstName;
public String lastName;
public boolean isGroup;
public int id;
public String photo100;
public MiniUser(JSONObject obj, boolean is_group) throws JSONException {
id = obj.getInt("id");
firstName = obj.optString("first_name", obj.optString("name"));
lastName = obj.optString("last_name", "");
photo100 = obj.getString("photo_100");
isGroup = is_group;
}
}
@@ -1,7 +1,5 @@
package ru.vtosters.lite.downloaders.notifications;
import static ru.vtosters.lite.utils.AndroidUtils.getString;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
@@ -10,18 +8,20 @@ import android.os.Build;
import androidx.annotation.RequiresApi;
import androidx.core.app.NotificationManagerCompat;
import com.vtosters.lite.R;
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 = getString("music_downloading");
public static final String MUSIC_DOWNLOAD_CHANNEL_DESCRIPTION = getString("music_downloading_progress");
public static final String MUSIC_DOWNLOAD_CHANNEL_NAME = AndroidUtils.getString(R.string.music_downloading);
public static final String MUSIC_DOWNLOAD_CHANNEL_DESCRIPTION = AndroidUtils.getString(R.string.music_downloading_progress);
public static final String MUSIC_PLAYLIST_DOWNLOAD_CHANNEL_ID = "music_playlist_download_channel";
public static final String MUSIC_PLAYLIST_DOWNLOAD_CHANNEL_NAME = getString("playlist_downloading");
public static final String MUSIC_PLAYLIST_DOWNLOAD_CHANNEL_DESCRIPTION = getString("playlist_downloading_progress");
public static final String MUSIC_PLAYLIST_DOWNLOAD_CHANNEL_NAME = AndroidUtils.getString(R.string.playlist_downloading);
public static final String MUSIC_PLAYLIST_DOWNLOAD_CHANNEL_DESCRIPTION = AndroidUtils.getString(R.string.playlist_downloading_progress);
public static final String MUSIC_CACHING_NAME = getString("caching_track_or_pic");
public static final String MUSIC_CACHING_NAME = AndroidUtils.getString(R.string.caching_track_or_pic);
private static NotificationManagerCompat notificationManagerCompat;
@@ -90,6 +90,14 @@ public class EncryptProvider {
return (String) decryptMessage(msgBody, peer, showEmoji);
}
public static CharSequence decryptMessage(CharSequence msgBody, MsgFromUser msgFromUser, boolean showEmoji) {
try {
return decryptMessage((String) msgBody, msgFromUser.v1(), showEmoji);
} catch (Exception e) {
return decryptMessage((String) msgBody, 0, showEmoji);
}
}
// For MentionsFormatter that requires CharSequence
public static CharSequence decryptMessage(String msgBody, int peer, boolean showEmoji) {
var decryptedMessage = decryptMessage(msgBody, peer);
@@ -5,8 +5,6 @@ import static android.widget.Toast.makeText;
import static ru.vtosters.lite.proxy.ProxyUtils.getApi;
import static ru.vtosters.lite.proxy.ProxyUtils.isAnyProxyEnabled;
import static ru.vtosters.lite.utils.AndroidUtils.getGlobalContext;
import static ru.vtosters.lite.utils.AndroidUtils.getIdentifier;
import static ru.vtosters.lite.utils.AndroidUtils.getString;
import android.annotation.SuppressLint;
import android.app.ProgressDialog;
@@ -15,6 +13,7 @@ import android.util.Log;
import com.vk.core.dialogs.alert.VkAlertDialog;
import com.vk.core.network.Network;
import com.vtosters.lite.R;
import org.json.JSONException;
import org.json.JSONObject;
@@ -32,16 +31,17 @@ import java.util.regex.Pattern;
import okhttp3.Headers;
import okhttp3.OkHttpClient;
import ru.vtosters.lite.di.singleton.VtOkHttpClient;
import ru.vtosters.lite.net.Request;
import ru.vtosters.lite.utils.LifecycleUtils;
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 Pattern FOAF_REGEX_LOGIN = Pattern.compile("<ya:created dc:date=\"(.+?)\"\\/>");
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 = new OkHttpClient();
private static final OkHttpClient client = VtOkHttpClient.getInstance();
public static long getLastSeen(long origtime, int id) throws ParseException, IOException {
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();
@@ -88,14 +88,13 @@ public class FoafBase {
}
}
public static JSONObject getBypassedOnlineInfo(String ids) throws JSONException {
public static JSONObject getBypassedOnlineInfo(String ids) {
var request = new okhttp3.Request.a()
.b(API_VKNEXT + ids)
.a();
String response = null;
String response;
var dummy = new JSONObject();
@@ -126,7 +125,7 @@ public class FoafBase {
public static void loadAndShow(Context context, int i) {
ProgressDialog progressDialog = new ProgressDialog(context);
progressDialog.setMessage(getString("com_facebook_loading"));
progressDialog.setMessage(context.getString(R.string.com_facebook_loading));
progressDialog.setCancelable(false);
progressDialog.show();
@@ -138,23 +137,20 @@ public class FoafBase {
try {
progressDialog.cancel();
Matcher matcher = FOAF_REGEX.matcher(str);
Matcher matcher2 = FOAF_REGEX_LOGIN.matcher(str);
matcher.find();
matcher2.find();
String normalHumanDate = getNormalHumanDate(Objects.requireNonNull(matcher.group(1)));
getNormalHumanDate(Objects.requireNonNull(matcher2.group(1)));
new VkAlertDialog.Builder(context)
.setTitle(getIdentifier("addinf", "string"))
.setMessage(getString("foafid") +
" " + i + getString("foafregdate")
+ " " + normalHumanDate + getString("foafdate")
LifecycleUtils.getCurrentActivity().runOnUiThread(() -> new VkAlertDialog.Builder(context)
.setTitle(R.string.addinf)
.setMessage(context.getString(R.string.foafid) +
" " + i + context.getString(R.string.foafregdate)
+ " " + normalHumanDate + context.getString(R.string.foafdate)
+ " " + daysPassedFromFoafDate(normalHumanDate))
.setPositiveButton(android.R.string.ok, null)
.show();
.show());
} catch (Exception e) {
LifecycleUtils.getCurrentActivity().runOnUiThread(() -> makeText(getGlobalContext(), context.getString(R.string.foaferr), LENGTH_SHORT).show());
e.printStackTrace();
makeText(getGlobalContext(), getString("foaferr"), LENGTH_SHORT).show();
}
}
@@ -9,6 +9,7 @@ import static ru.vtosters.lite.utils.Preferences.adsstories;
import static ru.vtosters.lite.utils.Preferences.authorsrecomm;
import static ru.vtosters.lite.utils.Preferences.dev;
import static ru.vtosters.lite.utils.Preferences.friendsrecomm;
import static ru.vtosters.lite.utils.Preferences.getBoolValue;
import static ru.vtosters.lite.utils.Preferences.postsrecomm;
import android.util.Log;
@@ -18,7 +19,6 @@ import org.json.JSONException;
import org.json.JSONObject;
import java.util.Collections;
import java.util.LinkedHashSet;
import ru.vtosters.lite.utils.AndroidUtils;
import ru.vtosters.lite.utils.NewsFeedFiltersUtils;
@@ -225,12 +225,22 @@ public class AdBlockHook {
continue;
}
if (NewsFeedFiltersUtils.injectFiltersReposts(list) && !isWhitelistedFilters(list)) {
if (hasMiniAppAds(list) && !isWhitelistedFilters(list)) {
if (dev())
Log.d("NewsfeedAdBlockV2", "Removed post " + list.optInt("post_id") + " from feed, Reason: repost ad");
Log.d("NewsfeedAdBlockV2", "Removed post " + list.optInt("post_id") + " from feed, Reason: MiniApp filters");
continue;
}
try {
if (NewsFeedFiltersUtils.injectFiltersReposts(list) && !isWhitelistedFilters(list)) {
if (dev())
Log.d("NewsfeedAdBlockV2", "Removed post " + list.optInt("post_id") + " from feed, Reason: repost ad");
continue;
}
} catch (Exception ex) {
Log.d("RepostInj", ex.getMessage());
}
newItems.put(list);
}
@@ -267,41 +277,9 @@ public class AdBlockHook {
}
}
if (!json.has("items")) return json;
var items = json.optJSONArray("items");
if (items != null) {
for (int i = 0; i < items.length(); i++) {
var item = items.optJSONObject(i);
if (item != null) {
parseStoriesItem(item);
}
}
}
return json;
}
private static void parseStoriesItem(JSONObject item) throws JSONException {
var stories = item.optJSONArray("stories");
var newStories = new JSONArray();
if (stories == null) return;
for (int j = 0; j < stories.length(); j++) {
var story = stories.optJSONObject(j);
if (!story.optBoolean("is_ads") && !story.optBoolean("is_promo")) {
newStories.put(story);
} else {
if (dev())
Log.d("StoriesAds", "Fetched ad, owner id " + story.optString("owner_id") + ", caption " + story.optString("caption"));
}
}
item.put("stories", newStories);
}
public static Boolean isAds(JSONObject list, String type) throws JSONException {
if (list == null || type == null || !ads()) return false;
@@ -339,7 +317,10 @@ public class AdBlockHook {
return true;
}
var whitelist = AndroidUtils.getDefaultPrefs().getStringSet("whitelisted_filters_groups", Collections.synchronizedSet(new LinkedHashSet<>()));
var whitelist = AndroidUtils.getDefaultPrefs().getStringSet(
"whitelisted_filters_groups",
Collections.emptySet()
);
return whitelist.contains(id);
}
@@ -351,8 +332,24 @@ public class AdBlockHook {
id = String.valueOf(list.optInt("source_id"));
}
var whitelist = AndroidUtils.getDefaultPrefs().getStringSet("whitelisted_ad_groups", Collections.synchronizedSet(new LinkedHashSet<>()));
var whitelist = AndroidUtils.getDefaultPrefs().getStringSet(
"whitelisted_ad_groups",
Collections.emptySet()
);
return whitelist.contains(id);
}
public static Boolean hasMiniAppAds(JSONObject list) {
var attachments = list.optJSONArray("attachments");
if (attachments != null && getBoolValue("blockminiapps", false)) {
for (int j = 0; j < attachments.length(); j++) {
var type = attachments.optJSONObject(j).optString("type");
if (type.contains("mini_app")) return true;
}
}
return false;
}
}
@@ -0,0 +1,15 @@
package ru.vtosters.lite.hooks;
import static ru.vtosters.lite.utils.Preferences.getBoolValue;
import b.h.g.g.BuildInfo;
public class AppVerHook {
public static int appBuild() {
return getBoolValue("useOldAppVer", false) ? 6561 : BuildInfo.i.e();
}
public static String appVer() {
return getBoolValue("useOldAppVer", false) ? "6.18.1" : BuildInfo.i.d();
}
}
@@ -0,0 +1,9 @@
package ru.vtosters.lite.hooks;
import static ru.vtosters.lite.utils.Preferences.getBoolValue;
public class BusinessNotifHook {
public static boolean hook(boolean i){
return !getBoolValue("businessDisabler", false) && i;
}
}
@@ -0,0 +1,67 @@
package ru.vtosters.lite.hooks;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.view.View;
import android.widget.Toast;
import com.vk.im.engine.models.dialogs.Dialog;
import com.vtosters.lite.api.ExtendedUserProfile;
import ru.vtosters.lite.utils.AccountManagerUtils;
import ru.vtosters.lite.utils.AndroidUtils;
import ru.vtosters.lite.utils.LifecycleUtils;
import static android.widget.Toast.LENGTH_SHORT;
import static ru.vtosters.lite.utils.AndroidUtils.getGlobalContext;
public class CallsHook {
public static void forwardToVkOffApps(View view, ExtendedUserProfile p) {
call(view.getContext(), AccountManagerUtils.getUserID(p));
}
public static void forwardToVkOffApps(View view, Dialog dl) {
call(view.getContext(), dl.getId());
}
public static void forwardToVkOffApps(Context ctx, int userId) {
call(ctx, userId);
}
public static void call(Context ctx, int id) {
// FIXME: add com.vk.calls, right now it doesn't support intent
var vkim = checkPackage("com.vk.im");
var vk = checkPackage("com.vkontakte.android");
var callsAvailable = vkim || vk;
if (!callsAvailable) {
Toast.makeText(LifecycleUtils.getCurrentActivity(), AndroidUtils.getString("calls_not_available"), LENGTH_SHORT).show();
try {
var action = new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=com.vkontakte.android"));
action.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
ctx.startActivity(action);
} catch (android.content.ActivityNotFoundException e) {
var action = new Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=com.vkontakte.android"));
action.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
ctx.startActivity(action);
}
return;
}
var link = "https://vk.com/call?id=" + id;
var startCall = new Intent(Intent.ACTION_VIEW, Uri.parse(link)).setPackage(vk ? "com.vkontakte.android" : "com.vk.im");
startCall.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
ctx.startActivity(startCall);
}
private static Boolean checkPackage(String packageName) {
try {
var pm = getGlobalContext().getPackageManager();
return pm.getApplicationInfo(packageName, 0).enabled;
} catch (PackageManager.NameNotFoundException e) {
return false;
}
}
}
@@ -2,7 +2,6 @@ package ru.vtosters.lite.hooks;
import static ru.vtosters.lite.dnr.DNRInjector.forceInvalidateDialogActions;
import static ru.vtosters.lite.utils.AndroidUtils.dp2px;
import static ru.vtosters.lite.utils.AndroidUtils.getString;
import static ru.vtosters.lite.utils.AndroidUtils.sendToast;
import static ru.vtosters.lite.utils.LifecycleUtils.getCurrentActivity;
import static ru.vtosters.lite.utils.ThemesUtils.getSTextAttr;
@@ -17,12 +16,14 @@ import android.widget.LinearLayout;
import com.vk.core.dialogs.alert.VkAlertDialog;
import com.vk.im.engine.models.dialogs.Dialog;
import com.vtosters.lite.R;
import java.util.List;
import java.util.Objects;
import ru.vtosters.lite.encryption.EncryptProvider;
import ru.vtosters.lite.encryption.base.IMProcessor;
import ru.vtosters.lite.utils.AndroidUtils;
public class CryptImHook {
public static boolean isPrivateProcessor(int peerID) {
@@ -42,7 +43,7 @@ public class CryptImHook {
int enabledPosition = 0;
String[] titles = new String[processors.size() + 1];
titles[0] = getString("encryption_disable");
titles[0] = AndroidUtils.getString(R.string.encryption_disable);
for (int i = 0; i < processors.size(); i++) {
IMProcessor proc = processors.get(i);
@@ -58,13 +59,8 @@ public class CryptImHook {
IMProcessor finalEnabled = enabled;
Activity ctx = getCurrentActivity();
if (ctx == null) {
sendToast(getString("app_activities_error"));
return;
}
new VkAlertDialog.Builder(ctx)
.setTitle(getString("encryption_sett"))
.setTitle(AndroidUtils.getString(R.string.encryption_sett))
.setSingleChoiceItems(titles, enabledPosition, (dialog, which) -> {
if (which == 0) {
if (finalEnabled != null) finalEnabled.disableEncryptFor(peerId);
@@ -96,7 +92,7 @@ public class CryptImHook {
}
if (enabled != null && enabled.isPublic()) {
sendToast(getString("encryption_public_algorithm_error"));
sendToast(AndroidUtils.getString(R.string.encryption_public_algorithm_error));
return;
}
@@ -110,11 +106,6 @@ public class CryptImHook {
Context ctx = getCurrentActivity();
if (ctx == null) {
sendToast(getString("app_activities_error"));
return;
}
LinearLayout linearLayout = new LinearLayout(ctx);
final EditText editText = new EditText(ctx);
@@ -130,8 +121,8 @@ public class CryptImHook {
IMProcessor finalEnabled = enabled;
new VkAlertDialog.Builder(ctx)
.setTitle(getString("encryption_enter_key"))
.setMessage(String.format(getString("encryption_current_algorithm_title"), enabled.getUIName()))
.setTitle(R.string.encryption_enter_key)
.setMessage(String.format(ctx.getString(R.string.encryption_current_algorithm_title), enabled.getUIName()))
.setView(linearLayout)
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
Editable edit = editText.getText();
@@ -1,30 +1,29 @@
package ru.vtosters.lite.hooks;
import static ru.vtosters.lite.utils.AndroidUtils.getIdentifier;
import static ru.vtosters.lite.utils.AndroidUtils.getPrefsValue;
import static ru.vtosters.lite.utils.AndroidUtils.getString;
import static ru.vtosters.lite.utils.AndroidUtils.getStringDate;
import android.annotation.SuppressLint;
import com.vtosters.lite.R;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import ru.vtosters.lite.utils.AndroidUtils;
public class DateHook {
public static boolean fulltime() {
return getPrefsValue("dateformat").equals("noyear") || getPrefsValue("dateformat").equals("full") || getPrefsValue("dateformat").equals("noseconds");
}
public static String getDateFormat() {
switch (getPrefsValue("dateformat")) {
case "noyear":
return getString("fulltime2");
case "full":
return getString("fulltime");
default:
return getString("fulltime3");
}
return switch (getPrefsValue("dateformat")) {
case "noyear" -> AndroidUtils.getString(R.string.fulltime2);
case "full" -> AndroidUtils.getString(R.string.fulltime);
default -> AndroidUtils.getString(R.string.fulltime3);
};
}
@SuppressLint("SimpleDateFormat")
@@ -38,7 +37,7 @@ public class DateHook {
if (!fulltime()) return null;
try {
return getStringDate(isFemale ? getIdentifier("last_seen_profile_f", "string") : getIdentifier("last_seen_profile_m", "string"), date.format(new Date(time)));
return getStringDate(isFemale ? R.string.last_seen_profile_f : R.string.last_seen_profile_m, date.format(new Date(time)));
} catch (Throwable th) {
th.printStackTrace();
return null;
@@ -83,7 +83,7 @@ public class DockBarInjector {
}
} catch (NoSuchFieldException | IllegalAccessException | ClassNotFoundException |
NoSuchMethodException | InvocationTargetException e) {
NoSuchMethodException | InvocationTargetException e) {
e.printStackTrace();
}
@@ -102,6 +102,7 @@ public class DockBarInjector {
item.setStaticMode(true);
item.setIconsMode(false);
item.setShiftingMode(false);
item.setTextColor(getCSTDock());
}
}
}
@@ -190,33 +191,24 @@ public class DockBarInjector {
int val = 0;
switch (tabId) {
case tab_messages:
val = MenuCountersState.j();
break;
case menu_groups:
val = MenuCountersState.h();
break;
case menu_fave:
val = MenuCountersState.c();
break;
case menu_games:
val = MenuCountersState.a();
break;
case menu_photos:
val = MenuCountersState.l();
break;
case menu_videos:
val = MenuCountersState.n();
break;
case tab_feedback:
case tab_news:
case tab_messages -> val = MenuCountersState.j();
case menu_groups -> val = MenuCountersState.h();
case menu_fave -> val = MenuCountersState.c();
case menu_games -> val = MenuCountersState.a();
case menu_photos -> val = MenuCountersState.l();
case menu_videos -> val = MenuCountersState.n();
case tab_feedback -> {
if (milkshake())
val = MenuCountersState.k();
break;
case menu_friends:
}
case tab_news -> {
if (milkshake() && getBoolValue("newsfeed_notif", true))
val = MenuCountersState.k();
}
case menu_friends -> {
if (!milkshake())
val = MenuCountersState.k();
break;
}
}
//
// val = MenuCountersState.g(); // friends ?????? menu_friends
@@ -1,6 +1,6 @@
package ru.vtosters.lite.hooks;
import static ru.vtosters.lite.utils.Preferences.musicFixNew;
import static ru.vtosters.lite.utils.Preferences.isValidSignature;
import android.os.Build;
import android.util.Base64;
@@ -27,9 +27,13 @@ import java.util.concurrent.atomic.AtomicBoolean;
import javax.net.ssl.HttpsURLConnection;
public class GcmHook {
private static final String agent = String.format("Android-GCM/1.5 (%s %s)", Build.MODEL, Build.MODEL);
private static KeyPair pair;
private static int rid = 0;
static {
@@ -37,14 +41,11 @@ public class GcmHook {
}
public static String requestTokenV2(String orig) {
if (musicFixNew()) return "null"; //not public
return orig;
return isValidSignature() ? "eyJhbGciOiAibm9uZSJ9.eyJub25jZSI6ICJ0ZXN0PT0ifQ." : "e251bGx9";
}
public static String requestToken(String orig) {
if (musicFixNew()) return "{null}"; //not public
if (orig.equals("")) return requestToken();
return orig;
return "yssp9o9p9pamz5t-nvmq8spgwtin3e0==";
}
public static String requestToken() {
@@ -60,6 +61,7 @@ public class GcmHook {
} catch (NoSuchAlgorithmException e) {
xappide = "";
}
AtomicBoolean wait = new AtomicBoolean(false);
StringBuilder sb = new StringBuilder();
List<String> params = new ArrayList<>();
@@ -71,17 +73,20 @@ public class GcmHook {
while (!wait.get()) {
Thread.sleep(100);
}
String token = sb.toString();
if (token.equals("REGISTRATION_ERROR")) {
return requestToken();
}
rid = 0;
String pub22 = genNewKey();
String sig2 = getSig(pub22);
params.clear();
fillParams(params, sig2, pub22, xappide, Long.parseLong(aid.split(" ")[1].split(":")[0]), true);
doRequest("https://android.clients.google.com/c2dm/register3", "POST", params, aid, GcmHook::lambda$requestToken$1);
return token;
return isValidSignature() ? token : null;
} catch (FileNotFoundException e2) {
return requestToken();
} catch (Exception e3) {
@@ -100,12 +105,14 @@ public class GcmHook {
private static void fillParams(List<String> params, String sig, String pub2, String xappid, long androidId, boolean del) {
rid++;
params.add("X-subtype=841415684880");
if (del) {
params.add("X-delete=1");
params.add("X-X-delete=1");
} else {
params.add("X-X-subscription=841415684880");
}
params.add("X-X-subtype=841415684880");
params.add("X-app_ver=" + "7.26");
params.add("X-kid=|ID|" + rid + "|");
@@ -115,19 +122,20 @@ public class GcmHook {
params.add("X-gmsv=11949480");
params.add("X-pub2=" + pub2);
params.add("X-X-kid=|ID|" + rid + "|");
StringBuilder sb = new StringBuilder();
sb.append("X-appid=");
sb.append(xappid);
params.add(sb.toString());
params.add("X-appid=" + xappid);
if (del) {
params.add("X-scope=GCM");
} else {
params.add("X-scope=*");
}
params.add("X-subscription=841415684880");
if (!del) {
params.add("X-gmp_app_id=1:841415684880:android:632f429381141121");
}
params.add("X-app_ver_name=" + "7.26");
params.add("app=com.vkontakte.android");
params.add("sender=841415684880");
@@ -139,9 +147,11 @@ public class GcmHook {
private static String join(String chr, Iterable<String> arr) {
String str = "";
for (String s : arr) {
str = str.isEmpty() ? s : str + chr + s;
}
return str;
}
@@ -168,8 +178,12 @@ public class GcmHook {
}
private static String getRandomAid() {
var arr1 = new String[]{"3974055026275073921", "4418584909973341826", "4585634953328772978"};
var arr2 = new String[]{"1932960345884890854", "6594645578425092292", "1792344590975444730"};
var arr1 = new String[]{
"3974055026275073921", "4418584909973341826", "4585634953328772978"
};
var arr2 = new String[]{
"1932960345884890854", "6594645578425092292", "1792344590975444730"
};
int r = new Random().nextInt(2);
return "AidLogin " + arr1[r] + ":" + arr2[r];
}
@@ -182,6 +196,7 @@ public class GcmHook {
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return Base64.encodeToString(pair.getPublic().getEncoded(), 0);
}
@@ -190,7 +205,9 @@ public class GcmHook {
PrivateKey key = pair.getPrivate();
Signature sign = Signature.getInstance(key instanceof RSAPrivateKey ? "SHA256withRSA" : "SHA256withECDSA");
sign.initSign(key);
sign.update(join("\n", new String[]{"com.vkontakte.android", pub2}).getBytes(StandardCharsets.UTF_8));
sign.update(join("\n", new String[]{
"com.vkontakte.android", pub2
}).getBytes(StandardCharsets.UTF_8));
return Base64.encodeToString(sign.sign(), 0);
} catch (Exception e) {
e.printStackTrace();
@@ -201,4 +218,4 @@ public class GcmHook {
public interface Callback {
void doCallback(String str);
}
}
}
@@ -1,19 +1,13 @@
package ru.vtosters.lite.hooks;
import static ru.vtosters.lite.foaf.FoafBase.getBypassedOnlineInfo;
import static ru.vtosters.lite.hooks.DateHook.getLocale;
import static ru.vtosters.lite.hooks.OnlineFormatterHook.onlineHookProfiles;
import static ru.vtosters.lite.music.cache.helpers.PlaylistHelper.*;
import static ru.vtosters.lite.music.cache.helpers.PlaylistHelper.getCatalogHeader;
import static ru.vtosters.lite.music.cache.helpers.PlaylistHelper.getCatalogSeparator;
import static ru.vtosters.lite.proxy.ProxyUtils.getApi;
import static ru.vtosters.lite.proxy.ProxyUtils.getStatic;
import static ru.vtosters.lite.utils.AndroidUtils.getDefaultPrefs;
import static ru.vtosters.lite.utils.Preferences.dev;
import static ru.vtosters.lite.utils.Preferences.friendsblock;
import static ru.vtosters.lite.utils.Preferences.getBoolValue;
import static ru.vtosters.lite.utils.Preferences.hasVerification;
import static ru.vtosters.lite.utils.Preferences.podcastcatalog;
import static ru.vtosters.lite.utils.VTVerifications.isDeveloper;
import static ru.vtosters.lite.utils.VTVerifications.isPrometheus;
import static ru.vtosters.lite.utils.VTVerifications.isVerified;
@@ -21,9 +15,8 @@ import static ru.vtosters.lite.utils.VTVerifications.isVerified;
import android.text.TextUtils;
import android.util.Log;
import com.vk.core.network.Network;
import com.vk.core.util.DeviceIdProvider;
import com.vk.core.util.LangUtils;
import com.vtosters.lite.R;
import org.json.JSONArray;
import org.json.JSONException;
@@ -31,176 +24,16 @@ import org.json.JSONObject;
import java.io.IOException;
import java.text.ParseException;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Objects;
import java.util.Random;
import bruhcollective.itaysonlab.libvkx.client.LibVKXClient;
import okhttp3.Headers;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import ru.vtosters.lite.music.cache.CacheDatabaseDelegate;
import ru.vtosters.lite.music.cache.helpers.PlaylistHelper;
import ru.vtosters.lite.di.singleton.VtOkHttpClient;
import ru.vtosters.lite.utils.AccountManagerUtils;
import ru.vtosters.lite.utils.AndroidUtils;
public class JsonInjectors {
private static final OkHttpClient mClient = new OkHttpClient();
public static JSONObject profileButton(JSONObject orig) throws JSONException {
var id = orig.getInt("id");
var newItem = new JSONArray();
if (orig.has("buttons")) {
newItem = orig.getJSONArray("buttons");
}
if (!haveDonateButton() && id == AccountManagerUtils.getUserId()) {
newItem.put(donateRecomm());
}
if (!orig.has("type") && AccountManagerUtils.isVKTester()) {
newItem.put(vktesters(id));
}
orig.put("buttons", newItem);
return orig;
}
public static JSONObject donateRecomm() throws JSONException {
var title = AndroidUtils.getString("donate_to_vtl");
var link = "https://vk.com/vtosters_official";
var text_color = "2D81E0";
var json = new JSONObject();
var icons = new JSONArray();
var action = new JSONObject();
action.put("target", "internal");
action.put("type", "open_url"); // may be open_url, open_internal_vkui, open_game, help_hint, show_full_post, open_vkapp, show_recommendations_for_post and groups_advertisement
action.put("url", link);
json.put("action", action);
json.put("title", title);
var icon1 = new JSONObject();
icon1.put("url", "https://sun2-10.userapi.com/NLd_rNpGuSaBnPV6O-j5mqCGZk8BK8drAMd2LQ/5R-DEF37PFs.png");
icon1.put("width", "20");
icon1.put("height", "20");
icons.put(icon1);
var icon2 = new JSONObject();
icon2.put("url", "https://sun2-12.userapi.com/N8y9pU1meJq_eug-wB1u-HUuGyMqDfdq7A025w/A4Aio-xuLY8.png");
icon2.put("width", "40");
icon2.put("height", "40");
icons.put(icon2);
var icon3 = new JSONObject();
icon3.put("url", "https://sun2-9.userapi.com/3Hx4hff63_2Lt6wjjthJMF_3QLUswNNlQKoAXQ/oyzEu1NL9T8.png");
icon3.put("width", "80");
icon3.put("height", "80");
icons.put(icon3);
json.put("icons", icons);
json.put("text_color", text_color);
return json;
}
public static JSONObject vktesters(int id) throws JSONException {
var title = AndroidUtils.getString("tester_profile");
var text_color = "2D81E0";
var link = "https://" + getStatic() + "/bugs?lang=" + LangUtils.a() + "#/reporter" + id;
var json = new JSONObject();
var icons = new JSONArray();
var action = new JSONObject();
action.put("target", "internal");
action.put("type", "open_internal_vkui");
action.put("url", link);
json.put("action", action);
json.put("title", title);
var icon1 = new JSONObject();
icon1.put("url", "https://sun2-10.userapi.com/NLd_rNpGuSaBnPV6O-j5mqCGZk8BK8drAMd2LQ/5R-DEF37PFs.png");
icon1.put("width", "20");
icon1.put("height", "20");
icons.put(icon1);
var icon2 = new JSONObject();
icon2.put("url", "https://sun2-12.userapi.com/N8y9pU1meJq_eug-wB1u-HUuGyMqDfdq7A025w/A4Aio-xuLY8.png");
icon2.put("width", "40");
icon2.put("height", "40");
icons.put(icon2);
var icon3 = new JSONObject();
icon3.put("url", "https://sun2-9.userapi.com/3Hx4hff63_2Lt6wjjthJMF_3QLUswNNlQKoAXQ/oyzEu1NL9T8.png");
icon3.put("width", "80");
icon3.put("height", "80");
icons.put(icon3);
json.put("icons", icons);
json.put("text_color", text_color);
return json;
}
public static JSONObject convBar(JSONObject orig) throws JSONException {
var peerid = Objects.requireNonNull(orig.optJSONObject("peer")).optInt("id");
var pic = "https://image.pngaaa.com/641/326641-middle.png"; // can be null
var text = AndroidUtils.getString("no_data_error");
var link = "https://vtosters.app"; // can be null
var linktitle = "Test button"; // can be null
var hasIcon = !pic.isEmpty();
var hasButton = !link.isEmpty();
var isPicture = pic.endsWith(".png") || pic.endsWith(".jpg") || pic.endsWith(".jpeg") || pic.endsWith(".webp");
if (isVerified(peerid)) text = AndroidUtils.getString("i_bought") + " VTosters Premium";
if (isPrometheus(peerid))
text = AndroidUtils.getString("i_bought") + " VTosters Premium Gold Prime Pro Plus";
if (isDeveloper(peerid)) text = AndroidUtils.getString("i_created_poop");
if (!isVerified(peerid) || text.equals("") || peerid == AccountManagerUtils.getUserId() || peerid == 0) {
if (getBoolValue("convBarRecomm", false)) {
return null;
} else {
return orig.optJSONObject("conversation_bar");
}
}
if (!dev()) return orig.optJSONObject("conversation_bar"); // for devs only
var json = new JSONObject();
var buttonsJson = new JSONObject();
buttonsJson.put("layout", "tertiary");
buttonsJson.put("text", linktitle);
buttonsJson.put("type", "link");
buttonsJson.put("link", link);
var buttonsjson = new JSONArray(); // max 3 buttons in array
if (hasButton) buttonsjson.put(buttonsJson);
json.put("name", "group_admin_welcome");
json.put("text", text);
json.put("buttons", buttonsjson);
if (hasIcon && isPicture) {
json.put("icon", pic);
}
return json;
}
public static JSONObject menu(JSONObject orig) throws JSONException {
var Special = orig.optJSONArray("special");
var Main = orig.getJSONArray("main");
@@ -242,19 +75,6 @@ public class JsonInjectors {
return json.putOpt("items", newItems);
}
public static JSONObject musiclink(JSONObject json) {
var links = json.optJSONArray("links");
if (links != null) {
if (links.optJSONObject(0).optString("url").contains("?section=recent")) {
json.remove("links");
if (dev()) Log.d("VKMusic", "Removed links buttons");
}
}
return json;
}
public static JSONObject setOnlineInfo(JSONObject json) throws JSONException {
var id = json.optInt("id");
@@ -336,193 +156,38 @@ public class JsonInjectors {
public static JSONArray newsfeedlist(JSONArray items) throws JSONException {
var selectedItems = getDefaultPrefs().getString("news_feed_selected_items", "");
var filtersSet = getDefaultPrefs().getStringSet("news_feed_items_set", null);
var mutableFiltersSet = Collections.synchronizedSet(new LinkedHashSet<String>());
var mutableFiltersSet = new LinkedHashSet<String>();
if (filtersSet != null)
mutableFiltersSet.addAll(filtersSet);
synchronized (mutableFiltersSet) {
for (int i = 0; i < items.length(); i++) {
var item = items.optJSONObject(i);
if (item == null) continue;
for (int i = 0; i < items.length(); i++) {
var item = items.optJSONObject(i);
if (item == null) continue;
var id = item.optString("id");
var title = item.optString("title");
var id = item.optString("id");
var title = item.optString("title");
if (TextUtils.isEmpty(id) || TextUtils.isEmpty(title)
// this items not working
|| id.equals("kpop") || id.equals("foryou")
|| id.equals("qazaqstan") || id.equals("podcasts"))
continue;
if (TextUtils.isEmpty(id) || TextUtils.isEmpty(title)
// this items not working
|| id.equals("kpop") || id.equals("foryou")
|| id.equals("qazaqstan") || id.equals("podcasts"))
continue;
mutableFiltersSet.add(id + "|" + title);
mutableFiltersSet.add(id + "|" + title);
var hide = selectedItems.contains(id);
var hide = selectedItems.contains(id);
Log.d("NewsfeedList", "Added list " + id + " to feed");
item.put("is_hidden", hide)
.put("is_unavailable", hide);
if (dev()) Log.d("NewsfeedListInj", "Unlocked " + id + " in newsfeed list");
}
getDefaultPrefs().edit().putStringSet("news_feed_items_set", mutableFiltersSet)
.apply();
Log.d("NewsfeedList", "Added list " + id + " to feed");
item.put("is_hidden", hide)
.put("is_unavailable", hide);
if (dev()) Log.d("NewsfeedListInj", "Unlocked " + id + " in newsfeed list");
}
getDefaultPrefs().edit().putStringSet("news_feed_items_set", mutableFiltersSet)
.apply();
return items;
}
public static JSONObject music(JSONObject json) throws JSONException {
var catalog = json.optJSONObject("catalog");
JSONArray oldItems = null;
if (catalog != null) {
oldItems = catalog.optJSONArray("sections");
}
if (oldItems != null && oldItems.optJSONObject(0).optString("url").contains("general")) {
json.optJSONArray("playlists").put(getPlaylist());
if (podcastcatalog()) {
var podcasts = fetchCatalogPodcast();
if (podcasts != null) {
var catalogarr = podcasts.optJSONObject("catalog").optJSONArray("sections").optJSONObject(0);
var title = catalogarr.optString("title");
var id = catalogarr.optString("id");
var url = catalogarr.optString("url");
if (dev()) Log.d("VKMusic", "Added " + title + " in music sections");
oldItems.put(new JSONObject().put("id", id).put("title", title).put("url", url));
}
}
var playlists = fetchCatalogId("https://vk.com/audio?section=my_playlists");
if (playlists != null) {
var catalogarr = playlists.optJSONObject("catalog").optJSONArray("sections").optJSONObject(0);
var title = catalogarr.optString("title");
var id = catalogarr.optString("id");
var url = catalogarr.optString("url");
if (dev()) Log.d("VKMusic", "Added " + title + " in music sections");
oldItems.put(new JSONObject().put("id", id).put("title", title).put("url", url));
}
var albums = fetchCatalogId("https://vk.com/audio?section=albums");
if (albums != null) {
var catalogarr = albums.optJSONObject("catalog").optJSONArray("sections").optJSONObject(0);
var title = catalogarr.optString("title");
var id = catalogarr.optString("id");
var url = catalogarr.optString("url");
if (dev()) Log.d("VKMusic", "Added " + title + " in music sections");
oldItems.put(new JSONObject().put("id", id).put("title", title).put("url", url));
}
var recent = fetchCatalogId("https://vk.com/audio?section=recent");
if (recent != null) {
var catalogarr = recent.optJSONObject("catalog").optJSONArray("sections").optJSONObject(0);
var title = catalogarr.optString("title");
var id = catalogarr.optString("id");
var url = catalogarr.optString("url");
if (dev()) Log.d("VKMusic", "Added " + title + " in music sections");
oldItems.put(new JSONObject().put("id", id).put("title", title).put("url", url));
}
for (int i = 0; i < oldItems.length(); i++) {
var item = oldItems.optJSONObject(i);
var title = item.optString("title");
var id = item.optString("id");
var url = item.optString("url");
var value = AndroidUtils.getPrefsValue("musicdefcatalog");
Log.d("VKMusic", "title: " + title + " id: " + id + " url: " + url + " value: " + value);
if (url.contains(value) && !value.isEmpty()) {
catalog.put("default_section", id);
if (dev()) Log.d("VKMusic", "Added " + title + " as default music section");
}
}
if (CacheDatabaseDelegate.hasTracks() && !LibVKXClient.isIntegrationEnabled()){ // inj in playlist list
var blocks = oldItems.optJSONObject(0).optJSONArray("blocks");
var newBlocks = new JSONArray();
if (blocks != null){
newBlocks
.put(getCatalogHeader())
.put(getCatalogPlaylist())
.put(getCatalogSeparator());
Log.d("VKMusic", "added cache catalog playlist");
for (int i = 0; i < blocks.length(); i++) {
newBlocks.put(blocks.optJSONObject(i));
}
oldItems.optJSONObject(0).put("blocks", newBlocks);
}
}
}
return json;
}
public static JSONObject fetchCatalogId(String section) {
if (section.isEmpty()) return null;
var request = new Request.a()
.b("https://" + getApi() + "/method/catalog.getAudio"
+ "?v=5.119"
+ "&https=1"
+ "&need_blocks=1"
+ "&lang="
+ getLocale()
+ "&device_id="
+ DeviceIdProvider.d(AndroidUtils.getGlobalContext())
+ "&url="
+ section
+ "&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()).getJSONObject("response");
} catch (JSONException | IOException e) {
Log.d("VTLMusic", "Error: " + e.getMessage());
}
return null;
}
public static JSONObject fetchCatalogPodcast() {
var request = new Request.a()
.b("https://" + getApi() + "/method/catalog.getPodcasts"
+ "?v=5.119"
+ "&https=1"
+ "&need_blocks=1"
+ "&lang="
+ getLocale()
+ "&device_id="
+ DeviceIdProvider.d(AndroidUtils.getGlobalContext())
+ "&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()).getJSONObject("response");
} catch (JSONException | IOException e) {
Log.d("VTLMusic", "Error: " + e.getMessage());
}
return null;
}
public static JSONObject friends(JSONObject json) throws JSONException, ParseException, IOException {
JSONObject catalog = onlineHookProfiles(json);
boolean sectionexecute = true;
@@ -588,8 +253,4 @@ public class JsonInjectors {
return json;
}
public static boolean haveDonateButton() {
return hasVerification() || new Random().nextInt(6) != 1;
}
}
@@ -4,41 +4,32 @@ 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;
import static ru.vtosters.lite.utils.Preferences.checkupdates;
import static ru.vtosters.lite.utils.ThemesUtils.isDarkTheme;
import static ru.vtosters.lite.utils.ThemesUtils.setNeededTheme;
import android.app.Activity;
import android.os.Build;
import android.preference.PreferenceManager;
import com.vk.core.concurrent.VkExecutors;
import ru.vtosters.lite.concurrent.VTExecutors;
import ru.vtosters.lite.downloaders.notifications.NotificationChannels;
import ru.vtosters.lite.ssfs.UsersList;
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;
import ru.vtosters.lite.utils.DeletedMessagesHandler;
/**
* @
*/
public class MainActivityInjector {
public static void inject(Activity activity) {
setNeededTheme(activity);
sendRequest();
UsersList.getUsersList();
if (checkupdates()) OTADialog.checkUpdates(activity);
VkExecutors.x.f().a(DeletedMessagesHandler::reloadMessagesList); // ioScheduler
VTExecutors.getIoScheduler().a(DeletedMessagesHandler::reloadMessagesList); // ioScheduler
Start.alert(activity);
InstallGMS.alert(activity);
DisableBattery.alert(activity);
PreferenceManager.getDefaultSharedPreferences(activity)
.edit()
.putBoolean("isdark", isDarkTheme())
.commit();
VkExecutors.x.q().a(() -> {
VTExecutors.getSlowTasksScheduler().a(() -> {
getInstance().autoCleaningCache();
}); // slowTasksScheduler
@@ -99,19 +99,13 @@ public class MessagesHook {
}
private static int expireTime() {
switch (bombCount) {
case "15s":
return 15;
case "1m":
return 60;
case "5m":
return 300;
case "1h":
return 3600;
case "24h":
return 86400;
default:
return 0;
}
return switch (bombCount) {
case "15s" -> 15;
case "1m" -> 60;
case "5m" -> 300;
case "1h" -> 3600;
case "24h" -> 86400;
default -> 0;
};
}
}
@@ -5,7 +5,6 @@ import static ru.vtosters.lite.utils.AndroidUtils.getDefaultPrefs;
import static ru.vtosters.lite.utils.AndroidUtils.getGlobalContext;
import static ru.vtosters.lite.utils.AndroidUtils.getPrefsValue;
import static ru.vtosters.lite.utils.Preferences.ads;
import static ru.vtosters.lite.utils.Preferences.adsslider;
import static ru.vtosters.lite.utils.Preferences.authorsrecomm;
import static ru.vtosters.lite.utils.Preferences.friendsrecomm;
import static ru.vtosters.lite.utils.Preferences.getBoolValue;
@@ -30,14 +29,12 @@ public class NewsfeedHook {
if (vkme()) {
return MAX_VALUE;
}
switch (getPrefsValue("newsupdate")) {
case "no_update":
return MAX_VALUE;
case "imd_update":
return 10000L;
default:
return Preference.b().getLong(refresh_timeout ? "refresh_timeout_top" : "refresh_timeout_recent", 600000L);
}
return switch (getPrefsValue("newsupdate")) {
case "no_update" -> MAX_VALUE;
case "imd_update" -> 10000L;
default ->
Preference.b().getLong(refresh_timeout ? "refresh_timeout_top" : "refresh_timeout_recent", 600000L);
};
}
public static List hideElement(ArrayList list) {
@@ -70,19 +67,25 @@ public class NewsfeedHook {
}
public static boolean isWhitelistedFilter(ExtendedCommunityProfile eup) {
return getDefaultPrefs().getStringSet("whitelisted_filters_groups", Collections.synchronizedSet(new LinkedHashSet<>())).contains(String.valueOf(AccountManagerUtils.getUserID(eup)));
return getDefaultPrefs().getStringSet(
"whitelisted_filters_groups", Collections.emptySet()
).contains(String.valueOf(AccountManagerUtils.getUserID(eup)));
}
public static boolean isWhitelistedAd(ExtendedCommunityProfile eup) {
return getDefaultPrefs().getStringSet("whitelisted_ad_groups", Collections.synchronizedSet(new LinkedHashSet<>())).contains(String.valueOf(AccountManagerUtils.getUserID(eup)));
return getDefaultPrefs().getStringSet(
"whitelisted_ad_groups",
Collections.emptySet()
).contains(String.valueOf(AccountManagerUtils.getUserID(eup)));
}
public static void setWhitelistedFilter(ExtendedCommunityProfile eup, boolean needWhitelist) {
var mutableAdsSet = Collections.synchronizedSet(new LinkedHashSet<String>());
var id = String.valueOf(AccountManagerUtils.getUserID(eup));
mutableAdsSet.addAll(getDefaultPrefs().getStringSet("whitelisted_filters_groups", mutableAdsSet));
var mutableAdsSet = new LinkedHashSet<>(getDefaultPrefs().getStringSet(
"whitelisted_filters_groups",
Collections.emptySet())
);
if (needWhitelist) {
mutableAdsSet.add(id);
} else {
@@ -93,10 +96,11 @@ public class NewsfeedHook {
}
public static void setWhitelistedAd(ExtendedCommunityProfile eup, boolean needWhitelist) {
var mutableAdsSet = Collections.synchronizedSet(new LinkedHashSet<String>());
var id = String.valueOf(AccountManagerUtils.getUserID(eup));
mutableAdsSet.addAll(getDefaultPrefs().getStringSet("whitelisted_ad_groups", mutableAdsSet));
var mutableAdsSet = new LinkedHashSet<>(getDefaultPrefs().getStringSet(
"whitelisted_ad_groups",
Collections.emptySet()));
if (needWhitelist) {
mutableAdsSet.add(id);
@@ -108,14 +112,16 @@ public class NewsfeedHook {
}
public static void adsParams(HashSet<String> hashSet) {
if (ads())
if (ads()) {
hashSet.add("ads_disabled");
} else {
hashSet.add("ads_app_slider");
hashSet.add("ads_site_slider");
}
hashSet.add("ads_app");
hashSet.add("ads_site");
hashSet.add("ads_post");
if (!adsslider())
hashSet.add("ads_app_slider");
hashSet.add("ads_site_slider");
hashSet.add("ads_app_video");
hashSet.add("ads_post_pretty_cards");
hashSet.add("ads_post_snippet_video");
@@ -5,7 +5,6 @@ import static ru.vtosters.lite.hooks.JsonInjectors.setOnlineInfoUsers;
import static ru.vtosters.lite.net.Request.makeRequest;
import static ru.vtosters.lite.proxy.ProxyUtils.getApi;
import static ru.vtosters.lite.utils.AccountManagerUtils.getUserToken;
import static ru.vtosters.lite.utils.AndroidUtils.getString;
import static ru.vtosters.lite.utils.AndroidUtils.sendToast;
import static ru.vtosters.lite.utils.Preferences.dev;
import static ru.vtosters.lite.utils.Preferences.getBoolValue;
@@ -13,6 +12,8 @@ import static ru.vtosters.lite.utils.Preferences.getPrefsFromFile;
import android.util.Log;
import com.vtosters.lite.R;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@@ -28,117 +29,61 @@ public class OnlineFormatterHook {
public static String getAppName(int appid) { // thanks to egormetlitsky (vk mp3 mod) for helping with online ids
if (appid <= 3 || !getBoolValue("onlineformatter", true)) return null;
switch (appid) {
case 2274003:
return "Android";
case 6328039:
case 6328868:
case 6820516:
return "VK mp3 mod";
case 5910839:
return "DarkVK";
case 3140623:
return "iPhone";
case 3087106:
return "iPhone (Dev)";
case 3682744:
return "iPad";
case 4083558:
case 5316500:
return "VFeed (iPhone)";
case 4385266:
return "VFeed Pro (iPhone)";
case 3502557:
case 3502561:
return "Windows Phone";
case 3697615:
return "Windows";
case 2685278:
return "Kate Mobile";
case 3469984:
return "Lynt";
case 6121396:
return "VK Admin (Android)";
case 5776857:
return "VK Admin (iPhone)";
case 5027722:
return "VK Messenger (Desktop)";
case 6146827:
return AndroidUtils.getString("vk_messenger") + " (Android)"; // VK Мессенджер (Android)
case 6482950:
return AndroidUtils.getString("vk_messenger") + " (iPhone)"; // VK Мессенджер (iPhone)
case 6481715:
return AndroidUtils.getString("vk_messenger") + " Dev (iPhone)"; // VK Мессенджер Dev (iPhone)
case 7799655:
return AndroidUtils.getString("vk_mail_online"); // VK Почта
case 7598572:
return AndroidUtils.getString("sferum_online") + " (Android)"; // Сферум (Android)
case 7571751:
return AndroidUtils.getString("sferum_online") + " (iPhone)"; // Сферум (iPhone)
case 7556576:
return AndroidUtils.getString("sferum_online"); // Сферум
case 7497650:
return "VK ID";
case 8094476:
return AndroidUtils.getString("vk_calls_online") + " (Android)"; // VK Звонки (Android)
case 8093730:
return AndroidUtils.getString("vk_calls_online") + " (iPhone)"; // VK Звонки (iPhone)
case 7793118:
return AndroidUtils.getString("vk_calls_online") + " (Desktop)"; // VK Звонки (Desktop)
case 6767438:
return AndroidUtils.getString("vk_music_online") + " (Android)"; // VK Музыка (Android)
case 8113297:
return AndroidUtils.getString("vk_clips_online") + " (Android)"; // VK Клипы (Android)
case 8106428:
return AndroidUtils.getString("vk_clips_online") + " (iPhone)"; // VK Клипы (iPhone)
case 5044491:
return "Candy";
case 8114066:
return AndroidUtils.getString("vk_video_online"); // VK Видео
case 4894723:
return "Phoenix Lite";
case 4994316:
return "Phoenix for VK";
case 3032107:
return "Vika";
case 3698024:
return "Instagram";
case 4580399:
return "Snapster (Android)";
case 4986954:
return "Snapster (iPhone)";
case 3900098:
case 5353544:
return "Домофон";
case 5462895:
return "Fast Messenger";
case 6964679:
return "Fast";
case 6030003:
return "Juno Messenger";
case 5955265:
return "VK Mobile";
case 6614620:
return "Laney";
case 5632485:
return "SpaceVK";
case 6287487:
return "vk.com";
case 4542624:
return "Black VK";
case 3917910:
return "Miranda NG (bridge)";
case 8043814:
return "Quise";
case 7221936:
return "Вечный Online";
case 6892935:
return "VK Helper";
case 6248661:
return "MemeVoices for VK";
default:
return getAppNameFromOnline(appid);
}
return switch (appid) {
case 2274003 -> "Android";
case 6328039, 6328868, 6820516 -> "VK mp3 mod";
case 5910839 -> "DarkVK";
case 3140623 -> "iPhone";
case 3087106 -> "iPhone (Dev)";
case 3682744 -> "iPad";
case 4083558, 5316500 -> "VFeed (iPhone)";
case 4385266 -> "VFeed Pro (iPhone)";
case 3502557, 3502561 -> "Windows Phone";
case 3697615 -> "Windows";
case 2685278 -> "Kate Mobile";
case 3469984 -> "Lynt";
case 6121396 -> "VK Admin (Android)";
case 5776857 -> "VK Admin (iPhone)";
case 5027722 -> "VK Messenger (Desktop)";
case 6146827 -> AndroidUtils.getString(R.string.vk_messenger) + " (Android)"; // VK Мессенджер (Android)
case 6482950 -> AndroidUtils.getString(R.string.vk_messenger) + " (iPhone)"; // VK Мессенджер (iPhone)
case 6481715 ->
AndroidUtils.getString(R.string.vk_messenger) + " Dev (iPhone)"; // VK Мессенджер Dev (iPhone)
case 7799655 -> AndroidUtils.getString(R.string.vk_mail_online); // VK Почта
case 7598572 -> AndroidUtils.getString(R.string.sferum_online) + " (Android)"; // Сферум (Android)
case 7571751 -> AndroidUtils.getString(R.string.sferum_online) + " (iPhone)"; // Сферум (iPhone)
case 7556576 -> AndroidUtils.getString(R.string.sferum_online); // Сферум
case 7497650 -> "VK ID";
case 8094476 -> AndroidUtils.getString(R.string.vk_calls_online) + " (Android)"; // VK Звонки (Android)
case 8093730 -> AndroidUtils.getString(R.string.vk_calls_online) + " (iPhone)"; // VK Звонки (iPhone)
case 7793118 -> AndroidUtils.getString(R.string.vk_calls_online) + " (Desktop)"; // VK Звонки (Desktop)
case 6767438 -> AndroidUtils.getString(R.string.vk_music_online) + " (Android)"; // VK Музыка (Android)
case 8113297 -> AndroidUtils.getString(R.string.vk_clips_online) + " (Android)"; // VK Клипы (Android)
case 8106428 -> AndroidUtils.getString(R.string.vk_clips_online) + " (iPhone)"; // VK Клипы (iPhone)
case 5044491 -> "Candy";
case 8114066 -> AndroidUtils.getString(R.string.vk_video_online); // VK Видео
case 4894723 -> "Phoenix Lite";
case 4994316 -> "Phoenix for VK";
case 3032107 -> "Vika";
case 3698024 -> "Instagram";
case 4580399 -> "Snapster (Android)";
case 4986954 -> "Snapster (iPhone)";
case 3900098, 5353544 -> "Домофон";
case 5462895 -> "Fast Messenger";
case 6964679 -> "Fast";
case 6030003 -> "Juno Messenger";
case 5955265 -> "VK Mobile";
case 6614620 -> "Laney";
case 5632485 -> "SpaceVK";
case 6287487 -> "vk.com";
case 4542624 -> "Black VK";
case 3917910 -> "Miranda NG (bridge)";
case 8043814 -> "Quise";
case 7221936 -> "Вечный Online";
case 6892935 -> "VK Helper";
case 6248661 -> "MemeVoices for VK";
default -> getAppNameFromOnline(appid);
};
}
@@ -173,7 +118,7 @@ public class OnlineFormatterHook {
if (appname == null) return null;
return getString("custom_online") + " " + appname;
return AndroidUtils.getString(R.string.custom_online) + " " + appname;
}
public static JSONObject onlineHook(JSONObject json) throws ParseException, IOException, JSONException {
@@ -1,15 +1,12 @@
package ru.vtosters.lite.hooks;
import static ru.vtosters.lite.utils.AndroidUtils.getIdentifier;
import static ru.vtosters.lite.utils.AndroidUtils.getPreferences;
import static ru.vtosters.lite.utils.AndroidUtils.getString;
import android.annotation.SuppressLint;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.net.Uri;
import android.util.Log;
import android.view.MenuItem;
@@ -19,7 +16,9 @@ import android.widget.Toast;
import androidx.annotation.Nullable;
import com.vk.core.dialogs.actionspopup.ActionsPopup;
import com.vk.core.drawable.RecoloredDrawable;
import com.vk.core.dialogs.alert.VkAlertDialog;
import com.vk.core.ui.themes.VKThemeHelper;
import com.vk.core.util.ContextExtKt;
import com.vk.core.util.ToastUtils;
import com.vk.dto.common.AttachmentWithMedia;
import com.vk.dto.common.ImageSize;
@@ -39,29 +38,30 @@ import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import ru.vtosters.lite.di.singleton.VtOkHttpClient;
import ru.vtosters.lite.utils.AndroidUtils;
import ru.vtosters.lite.utils.ThemesUtils;
import ru.vtosters.lite.utils.LifecycleUtils;
public class PhotoViewer {
static OkHttpClient client = new OkHttpClient();
static OkHttpClient client = VtOkHttpClient.getInstance();
public static boolean interceptClick(AttachmentWithMedia attachment, MenuItem item, View view) {
if (item.getItemId() == AndroidUtils.getIdentifier("search_photo", "id")) {
if (item.getItemId() == R.id.search_photo) {
if (attachment instanceof PhotoAttachment) {
searchPhoto(getUrlFromPhotoAttachment((PhotoAttachment) attachment));
searchDialog(LifecycleUtils.getCurrentActivity(), getUrlFromPhotoAttachment((PhotoAttachment) attachment));
} else if (attachment instanceof DocumentAttachment) {
var documentAttachment = (DocumentAttachment) attachment;
if (documentAttachment.J != null)
try {
searchPhoto(getUrlFromDocumentAttachment(documentAttachment));
searchDialog(LifecycleUtils.getCurrentActivity(), getUrlFromDocumentAttachment(documentAttachment));
} catch (Exception e) {
ToastUtils.a(getString("photo_get_error"));
ToastUtils.a(R.string.photo_get_error);
}
else
ToastUtils.a(getString("photo_get_error"));
ToastUtils.a(R.string.photo_get_error);
}
return true;
} else if (item.getItemId() == AndroidUtils.getIdentifier("copy_photo_url", "id")) {
} else if (item.getItemId() == R.id.copy_photo_url) {
if (attachment instanceof PhotoAttachment) {
copyPhotoUrl(getUrlFromPhotoAttachment((PhotoAttachment) attachment));
} else if (attachment instanceof DocumentAttachment) {
@@ -70,13 +70,13 @@ public class PhotoViewer {
try {
copyPhotoUrl(getUrlFromDocumentAttachment(documentAttachment));
} catch (Exception e) {
ToastUtils.a(getString("photo_get_error"));
ToastUtils.a(R.string.photo_get_error);
}
else
ToastUtils.a(getString("photo_get_error"));
ToastUtils.a(R.string.photo_get_error);
}
return true;
} else if (item.getItemId() == AndroidUtils.getIdentifier("open_original_photo", "id")) {
} else if (item.getItemId() == R.id.open_original_photo) {
if (attachment instanceof PhotoAttachment) {
openUrl(getUrlFromPhotoAttachment((PhotoAttachment) attachment));
} else if (attachment instanceof DocumentAttachment) {
@@ -85,44 +85,40 @@ public class PhotoViewer {
try {
openUrl(getUrlFromDocumentAttachment(documentAttachment));
} catch (Exception e) {
ToastUtils.a(getString("photo_get_error"));
ToastUtils.a(R.string.photo_get_error);
}
else
ToastUtils.a(getString("photo_get_error"));
ToastUtils.a(R.string.photo_get_error);
}
return true;
}
return false;
return true;
}
@SuppressLint("UseCompatLoadingForDrawables")
public static void addMenuItems(AttachmentWithMedia attachment, ActionsPopup.b actionPopup, int i, boolean z) {
actionPopup.a(getIdentifier("search_photo_content", "string"),
ThemesUtils.isDarkTheme()
? AndroidUtils.getGlobalContext().getDrawable(R.drawable.ic_menu_search_outline_28)
: new RecoloredDrawable(AndroidUtils.getGlobalContext().getDrawable(R.drawable.ic_menu_search_outline_28), ThemesUtils.getAccentColor()),
actionPopup.a(R.string.search_photo_content,
ContextExtKt.b(AndroidUtils.getGlobalContext(), R.drawable.ic_menu_search_outline_28, VKThemeHelper.g(R.attr.header_tint)),
false,
() -> {
if (attachment instanceof PhotoAttachment) {
searchPhoto(getUrlFromPhotoAttachment((PhotoAttachment) attachment));
searchDialog(LifecycleUtils.getCurrentActivity(), getUrlFromPhotoAttachment((PhotoAttachment) attachment));
} else if (attachment instanceof DocumentAttachment) {
var documentAttachment = (DocumentAttachment) attachment;
if (documentAttachment.J != null)
try {
searchPhoto(getUrlFromDocumentAttachment(documentAttachment));
searchDialog(LifecycleUtils.getCurrentActivity(), getUrlFromDocumentAttachment(documentAttachment));
} catch (Exception e) {
ToastUtils.a(getString("photo_get_error"));
ToastUtils.a(R.string.photo_get_error);
}
else
ToastUtils.a(getString("photo_get_error"));
ToastUtils.a(R.string.photo_get_error);
}
return null;
}
);
actionPopup.a(getIdentifier("copy_photo_url_content", "string"),
!ThemesUtils.isDarkTheme()
? new RecoloredDrawable(AndroidUtils.getGlobalContext().getDrawable(R.drawable.ic_copy_outline_28), ThemesUtils.getAccentColor())
: new RecoloredDrawable(AndroidUtils.getGlobalContext().getDrawable(R.drawable.ic_copy_outline_28), Color.WHITE),
actionPopup.a(R.string.copy_photo_url_content,
ContextExtKt.b(AndroidUtils.getGlobalContext(), R.drawable.ic_copy_outline_28, VKThemeHelper.g(R.attr.header_tint)),
false,
() -> {
if (attachment instanceof PhotoAttachment) {
@@ -133,19 +129,16 @@ public class PhotoViewer {
try {
copyPhotoUrl(getUrlFromDocumentAttachment(documentAttachment));
} catch (Exception e) {
ToastUtils.a(getString("photo_get_error"));
ToastUtils.a(R.string.photo_get_error);
}
else
ToastUtils.a(getString("photo_get_error"));
ToastUtils.a(R.string.photo_get_error);
}
return null;
}
);
actionPopup.a(getIdentifier("open_original_photo_content", "string"),
ThemesUtils.isDarkTheme()
? AndroidUtils.getGlobalContext().getDrawable(R.drawable.ic_link_outline_28)
: new RecoloredDrawable(AndroidUtils.getGlobalContext().getDrawable(R.drawable.ic_link_outline_28),
ThemesUtils.getAccentColor()),
actionPopup.a(R.string.open_original_photo_content,
ContextExtKt.b(AndroidUtils.getGlobalContext(), R.drawable.ic_link_outline_28, VKThemeHelper.g(R.attr.header_tint)),
false,
() -> {
if (attachment instanceof PhotoAttachment) {
@@ -156,10 +149,10 @@ public class PhotoViewer {
try {
openUrl(getUrlFromDocumentAttachment(documentAttachment));
} catch (Exception e) {
ToastUtils.a(getString("photo_get_error"));
ToastUtils.a(R.string.photo_get_error);
}
else
ToastUtils.a(getString("photo_get_error"));
ToastUtils.a(R.string.photo_get_error);
}
return null;
}
@@ -193,21 +186,46 @@ public class PhotoViewer {
return max.url;
}
private static void searchPhoto(String url) {
if (url == null || url.isEmpty()) return;
switch (getPreferences().getInt("search_engine", 0)) {
case 0:
searchWithYandex(url);
break;
case 1:
searchWithGoogle(url);
break;
case 2:
searchWithBing(url);
break;
private static void searchDialog(Context context, String url) {
if (selectedEngine() == 0) {
var items = new String[]{
"Yandex", "Google", "Bing", "TraceMoe", "Ascii2d", "Saucenao"
};
new VkAlertDialog.Builder(context)
.setItems(items, (dl, i) -> {
searchPhoto(i + 1, url);
})
.show();
} else {
searchPhoto(selectedEngine(), url);
}
}
private static int selectedEngine() {
return getPreferences().getInt("search_engine", 0);
}
private static void searchPhoto(int i, String url) {
if (url == null || url.isEmpty()) {
Log.d("PhotoViewer", "url is null or empty");
return;
}
switch (i) {
case 1 -> searchWithYandex(url);
case 2 -> searchWithGoogle(url);
case 3 -> searchWithBing(url);
case 4 -> searchWithTraceMoe(url);
case 5 -> searchWithAscii2d(url);
case 6 -> searchWithSaucenao(url);
}
}
private static void searchPhoto(String url) {
searchPhoto(selectedEngine(), url);
}
private static void searchWithYandex(String url) {
var request = new Request.a()
.b(String.format("https://yandex.ru/images-apphost/image-download?url=%s&images_avatars_size=preview&images_avatars_namespace=images-cbir",
@@ -235,8 +253,19 @@ public class PhotoViewer {
}
private static void searchWithGoogle(String url) {
openUrl("https://www.google.ru/searchbyimage?image_url=" +
URLEncoder.encode(url));
openUrl("https://www.google.com/searchbyimage?image_url=" + URLEncoder.encode(url) + "&client=app");
}
private static void searchWithTraceMoe(String url) {
openUrl("https://trace.moe/?url=" + URLEncoder.encode(url));
}
private static void searchWithAscii2d(String url) {
openUrl("https://ascii2d.net/search/url/" + URLEncoder.encode(url));
}
private static void searchWithSaucenao(String url) {
openUrl("https://saucenao.com/search.php?url=" + URLEncoder.encode(url));
}
private static void searchWithBing(String url) {
@@ -248,7 +277,7 @@ public class PhotoViewer {
if (url == null || url.isEmpty()) return;
ClipboardManager manager = (ClipboardManager) AndroidUtils.getGlobalContext().getSystemService(Context.CLIPBOARD_SERVICE);
manager.setPrimaryClip(ClipData.newPlainText("vk_photo_url", url));
Toast.makeText(AndroidUtils.getGlobalContext(), AndroidUtils.getIdentifier("copied_to_clipboard", "string"), Toast.LENGTH_SHORT).show();
Toast.makeText(AndroidUtils.getGlobalContext(), R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show();
}
private static void openUrl(String url) {
@@ -0,0 +1,9 @@
package ru.vtosters.lite.hooks;
import static ru.vtosters.lite.utils.Preferences.getBoolValue;
public class PromoStickersHook {
public static String hook() {
return getBoolValue("promotedstickers", true) ? "purchased,active" : "purchased,active,promoted";
}
}
@@ -21,15 +21,15 @@ import androidx.appcompat.view.ContextThemeWrapper;
import com.vk.auth.ui.VkAuthTextView;
import com.vk.core.dialogs.alert.VkAlertDialog;
import com.vk.navigation.Navigator;
import com.vtosters.lite.R;
import ru.vtosters.lite.ui.fragments.ProxySettingsFragment;
import ru.vtosters.lite.utils.AndroidUtils;
import ru.vtosters.lite.utils.ThemesUtils;
import ru.vtosters.lite.utils.NavigatorUtils;
public class ProxyHook {
public static void hookAuth(View v) {
VkAuthTextView button = (VkAuthTextView) v;
button.setText(AndroidUtils.getString("proxy_setup"));
button.setText(R.string.proxy_setup);
button.setOnClickListener(v1 -> callProxyDialog(v1.getContext()));
}
@@ -54,16 +54,16 @@ public class ProxyHook {
rg.setPadding(dp2px(18f), dp2px(12f), dp2px(18f), 0);
rgDefault.setText(AndroidUtils.getString("proxy_disable"));
rgDefault.setText(ctx.getString(R.string.proxy_disable));
rgDefault.setTextColor(getTextAttr());
rgZaborona.setText(AndroidUtils.getString("proxy_enable") + " (Zaborona)");
rgZaborona.setText(ctx.getString(R.string.proxy_enable) + " (Zaborona)");
rgZaborona.setTextColor(getTextAttr());
rgRandomProxy.setText(AndroidUtils.getString("proxy_enable") + " (Random Socks Proxy)");
rgRandomProxy.setText(ctx.getString(R.string.proxy_enable) + " (Random Socks Proxy)");
rgRandomProxy.setTextColor(getTextAttr());
rgVika.setText(AndroidUtils.getString("proxy_enable") + " (Vika Mobile)");
rgVika.setText(ctx.getString(R.string.proxy_enable) + " (Vika Mobile)");
rgVika.setTextColor(getTextAttr());
rgZaborona.setChecked(isZaboronaEnabled());
@@ -71,11 +71,11 @@ public class ProxyHook {
rgRandomProxy.setChecked(isRandomProxyEnabled());
rgVika.setChecked(isVikaProxyEnabled());
new VkAlertDialog.Builder(ctx)
.setTitle(AndroidUtils.getString("vtlproxy"))
.setMessage(AndroidUtils.getString("proxy_warning"))
.setView(rg)
.setPositiveButton(AndroidUtils.getString("vtl_confirm"), ((dialog, which) -> { // Применить
new VkAlertDialog.Builder(ctx)
.setTitle(ctx.getString(R.string.vtlproxy))
.setMessage(ctx.getString(R.string.proxy_warning))
.setView(rg)
.setPositiveButton(ctx.getString(R.string.vtl_confirm), ((dialog, which) -> { // Применить
if (rgZaborona.isChecked()) {
edit().putString("proxy", "zaborona").commit();
restartApplication();
@@ -89,13 +89,10 @@ public class ProxyHook {
edit().putString("proxy", "noproxy").commit();
restartApplication();
}
}))
.setNeutralButton(AndroidUtils.getString("proxy_settings"), ((dialog, which) -> {
var intent = new Navigator(ProxySettingsFragment.class)
.b(ctx)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
ctx.startActivity(intent);
}))
.show();
}))
.setNeutralButton(ctx.getString(R.string.proxy_settings), (dialog, which) -> {
NavigatorUtils.switchFragment(ctx, ProxySettingsFragment.class);
})
.show();
}
}
@@ -35,47 +35,28 @@ public class StartFragmentHook {
if (vkme()) {
return DialogsFragment.class;
}
switch (getPrefsValue("start_values")) {
case "newsfeed":
return milkshake() ? HomeFragment.class : NewsfeedFragment.class;
case "messenger":
return DialogsFragment.class;
case "groups":
return milkshake() ? CommunitiesCatalogFragment.class : GroupsFragment1.class;
case "music":
return MusicCatalogFragment1.class;
case "friends":
return FriendsFragment.class;
case "photos":
return PhotosFragment.class;
case "videos":
return VideoCatalogFragment.class;
case "settings":
return useNewSettings();
case "apps":
return AppsFragment.class;
case "discover":
return DiscoverFragment.class;
case "notifications":
return NotificationsContainerFragment.class;
case "money":
return MoneyTransfersFragment.class;
case "games":
return GamesFragment.class;
case "liked":
return FaveTabFragment.class;
case "menu":
return milkshake() ? (superapp() ? SuperAppFragment.class : SearchMenuFragment.class) : MenuFragment.class;
case "profile":
return ProfileFragment.class;
case "lives":
return LivesTabsFragment.class;
case "docs":
return DocumentsViewFragment.class;
case "brtd":
return BirthdaysFragment.class;
default:
return getInstance().getSelectedTabs().get(0).fragmentClass;
}
return switch (getPrefsValue("start_values")) {
case "newsfeed" -> milkshake() ? HomeFragment.class : NewsfeedFragment.class;
case "messenger" -> DialogsFragment.class;
case "groups" -> milkshake() ? CommunitiesCatalogFragment.class : GroupsFragment1.class;
case "music" -> MusicCatalogFragment1.class;
case "friends" -> FriendsFragment.class;
case "photos" -> PhotosFragment.class;
case "videos" -> VideoCatalogFragment.class;
case "settings" -> useNewSettings();
case "apps" -> AppsFragment.class;
case "discover" -> DiscoverFragment.class;
case "notifications" -> NotificationsContainerFragment.class;
case "money" -> MoneyTransfersFragment.class;
case "games" -> GamesFragment.class;
case "liked" -> FaveTabFragment.class;
case "menu" ->
milkshake() ? (superapp() ? SuperAppFragment.class : SearchMenuFragment.class) : MenuFragment.class;
case "profile" -> ProfileFragment.class;
case "lives" -> LivesTabsFragment.class;
case "docs" -> DocumentsViewFragment.class;
case "brtd" -> BirthdaysFragment.class;
default -> getInstance().getSelectedTabs().get(0).fragmentClass;
};
}
}
@@ -12,137 +12,25 @@ import com.vk.toggle.Features;
public class TogglesHook {
public static boolean shouldPatch(Features.Type feature) {
switch (feature) {
case AB_IM_LONGPOLL_MSG_BATCHING:
case AB_IM_VIEW_POOL:
case AB_MULTI_GIFTS:
case AB_NEWS_VIDEO_LAYOUT_TEXT:
case AB_STICKERS_DISCOVER:
case AB_GIFTS_FROM_KEYBOARD:
case FEATURE_FEED_DEFAULT_LIST_ALWAYS:
case EXPERIMENT_NEWS_VIDEO_LAYOUT_TEXT:
case FEATURE_MILKSHAKE_DISCOVER_IN_MENU:
case FEATURE_DISCOVER_CATEGORIES:
case FEATURE_FAVE_PERF_IMPROVE:
case FEATURE_GROUP_ADMIN_MESSAGES:
case FEATURE_GROUP_MSG_PUSH_PARAM:
case FEATURE_IMAGE_QUALITY_UPGRADE:
case FEATURE_IM_AUDIO_MSG_TRANSCRIPT:
case FEATURE_IM_CASPER_MSGS:
case FEATURE_IM_DISABLE_FORCE_OPEN_VK_ME:
case FEATURE_IM_GIF_AUTOPLAY:
case FEATURE_IM_MR_IN_VKAPP:
case FEATURE_MEMORIES_ENABLED:
case FEATURE_MENU_GAMES_CAROUSEL:
case FEATURE_MILKSHAKE:
case FEATURE_MILKSHAKE_CHANGE_FEED_BY_TIMEOUT:
case FEATURE_MILKSHAKE_NEWS_SCROLL_ON_BACK:
case FEATURE_MILKSHAKE_SWITCH_THEME_ON_TAP:
case FEATURE_ML_BRANDS:
case FEATURE_ML_FEATURES:
case FEATURE_ML_MODELS_LOADING:
case FEATURE_MONEY_TRANSFERS_VKPAY:
case FEATURE_MUSIC_ARTIST_CATALOG:
case FEATURE_MUSIC_NEW_CATALOG:
case FEATURE_NARRATIVE_SNIPPET_TYPE:
case FEATURE_NEWS_GAMES_IN_DISCOVER:
case FEATURE_NEWS_HEADER_SCROLL:
case FEATURE_NEW_FORMAT_SHOW_WPB:
case FEATURE_NOTIFICATION_ANIM:
case FEATURE_PODCASTS_PAGE:
case FEATURE_QR_CREATE_QR:
case FEATURE_QR_DYNAMIC_FRAME:
case FEATURE_QR_SCAN_FROM_PHOTO:
case FEATURE_QR_VISION_DECODER:
case FEATURE_QUEUE_COUNTERS:
case FEATURE_REEF:
case FEATURE_ROAMING_AUTOPLAY:
case FEATURE_SHARE_MSGS_ON_INVITE:
case FEATURE_STICKERS_BOT_LINK:
case FEATURE_STICKERS_NEW_CATALOG:
case FEATURE_STORIES_AVATAR:
case FEATURE_STORIES_FEED_TYPE:
case FEATURE_STORIES_SHOW_ALWAYS:
case FEATURE_STORY_ANSWER_PUBLIC:
case FEATURE_STORY_APP_STICKER:
case FEATURE_STORY_ARCHIVE:
case FEATURE_STORY_ARCHIVE_POST:
case FEATURE_STORY_BAKGROUND_ALL:
case FEATURE_STORY_BOX:
case FEATURE_STORY_CADRE:
case FEATURE_STORY_CAMERA_TOOLTIP:
case FEATURE_STORY_EDITOR_GALLERY:
case FEATURE_STORY_EDITOR_TYPE:
case FEATURE_STORY_ENDLESS_VIDEO:
case FEATURE_STORY_FAST_REACTIONS:
case FEATURE_STORY_GIF:
case FEATURE_STORY_GROUPED:
case FEATURE_STORY_HIGH_QUALITY:
case FEATURE_STORY_MASKS_FRONTAL:
case FEATURE_STORY_MENTION_ICON:
case FEATURE_STORY_MODERN_PUBLISH:
case FEATURE_STORY_MUSIC_EDITOR:
case FEATURE_STORY_MUSIC_REPLIES:
case FEATURE_STORY_MUSIC_REPLIES_2:
case FEATURE_STORY_NEW_FRAME:
case FEATURE_STORY_ONE_TIME:
case FEATURE_STORY_PHOTO_SHARING:
case FEATURE_STORY_PHOTO_STICKER:
case FEATURE_STORY_POLLS:
case FEATURE_STORY_POST_REPOST:
case FEATURE_STORY_PRELOADING:
case FEATURE_STORY_QUESTION:
case FEATURE_STORY_QUESTION_STYLE:
case FEATURE_STORY_RLOTTIE:
case FEATURE_STORY_RLOTTIE_CACHE:
case FEATURE_STORY_SAVE_DEFAULT_ON:
case FEATURE_STORY_SEND_DIALOG_LIST:
case FEATURE_STORY_STICKER_PACK:
case FEATURE_STORY_TIME:
case FEATURE_STORY_VIEWER_CAMERA:
case FEATURE_STORY_VOICE_ANSWER:
case FEATURE_STORY_WITH_CONTEXT:
case FEATURE_SUPERAPP_MENU:
case FEATURE_TRAFFIC_SAVER:
case FEATURE_VKPAY_WIDGET:
case FEATURE_VKUI_INTERNAL_TO_MINI_APPS:
case FEATURE_VKUI_TOKEN_CACHE:
case FEATURE_VK_APPS_SEARCH:
case FEATURE_VOTES_BALANCE:
case FEATURE_WEBVIEW_TOKEN_ACTIVE:
case FEATURE_WISHLIST_ENTRY_POINT:
case FEATURE_WISHLIST_MY_PROFILE:
case FEATURE_MILKSHAKE_FORCE_DISABLED:
case FEATURE_MILKSHAKE_ACTIVATION_DISABLED:
case EXPERIMENT_NEWS_DISABLE_CACHE:
return true;
default:
return false;
}
return switch (feature) {
case AB_IM_LONGPOLL_MSG_BATCHING, FEATURE_MOZJPEG, AB_IM_VIEW_POOL, AB_MULTI_GIFTS, AB_NEWS_VIDEO_LAYOUT_TEXT, AB_STICKERS_DISCOVER, AB_GIFTS_FROM_KEYBOARD, FEATURE_FEED_DEFAULT_LIST_ALWAYS, EXPERIMENT_NEWS_VIDEO_LAYOUT_TEXT, FEATURE_MILKSHAKE_DISCOVER_IN_MENU, FEATURE_DISCOVER_CATEGORIES, FEATURE_FAVE_PERF_IMPROVE, FEATURE_GROUP_ADMIN_MESSAGES, FEATURE_GROUP_MSG_PUSH_PARAM, FEATURE_IMAGE_QUALITY_UPGRADE, FEATURE_IM_AUDIO_MSG_TRANSCRIPT, FEATURE_IM_CASPER_MSGS, FEATURE_IM_DISABLE_FORCE_OPEN_VK_ME, FEATURE_IM_GIF_AUTOPLAY, FEATURE_IM_MR_IN_VKAPP, FEATURE_MEMORIES_ENABLED, FEATURE_MENU_GAMES_CAROUSEL, FEATURE_MILKSHAKE, FEATURE_MILKSHAKE_CHANGE_FEED_BY_TIMEOUT, FEATURE_MILKSHAKE_NEWS_SCROLL_ON_BACK, FEATURE_MILKSHAKE_SWITCH_THEME_ON_TAP, FEATURE_ML_BRANDS, FEATURE_ML_FEATURES, FEATURE_ML_MODELS_LOADING, FEATURE_MONEY_TRANSFERS_VKPAY, FEATURE_MUSIC_ARTIST_CATALOG, FEATURE_MUSIC_NEW_CATALOG, FEATURE_NARRATIVE_SNIPPET_TYPE, FEATURE_NEWS_GAMES_IN_DISCOVER, FEATURE_NEWS_HEADER_SCROLL, FEATURE_NEW_FORMAT_SHOW_WPB, FEATURE_PODCASTS_PAGE, FEATURE_QR_CREATE_QR, FEATURE_QR_DYNAMIC_FRAME, FEATURE_QR_SCAN_FROM_PHOTO, FEATURE_QR_VISION_DECODER, FEATURE_QUEUE_COUNTERS, FEATURE_REEF, FEATURE_ROAMING_AUTOPLAY, FEATURE_SHARE_MSGS_ON_INVITE, FEATURE_STICKERS_BOT_LINK, FEATURE_STICKERS_NEW_CATALOG, FEATURE_STORIES_AVATAR, FEATURE_STORIES_FEED_TYPE, FEATURE_STORIES_SHOW_ALWAYS, FEATURE_STORY_ANSWER_PUBLIC, FEATURE_STORY_APP_STICKER, FEATURE_STORY_ARCHIVE, FEATURE_STORY_ARCHIVE_POST, FEATURE_STORY_BAKGROUND_ALL, FEATURE_STORY_BOX, FEATURE_STORY_CADRE, FEATURE_STORY_CAMERA_TOOLTIP, FEATURE_STORY_EDITOR_GALLERY, FEATURE_STORY_EDITOR_TYPE, FEATURE_STORY_ENDLESS_VIDEO, FEATURE_STORY_FAST_REACTIONS, FEATURE_STORY_GIF, FEATURE_STORY_GROUPED, FEATURE_STORY_HIGH_QUALITY, FEATURE_STORY_MASKS_FRONTAL, FEATURE_STORY_MENTION_ICON, FEATURE_STORY_MODERN_PUBLISH, FEATURE_STORY_MUSIC_EDITOR, FEATURE_STORY_MUSIC_REPLIES, FEATURE_STORY_MUSIC_REPLIES_2, FEATURE_STORY_NEW_FRAME, FEATURE_STORY_ONE_TIME, FEATURE_STORY_PHOTO_SHARING, FEATURE_STORY_PHOTO_STICKER, FEATURE_STORY_POLLS, FEATURE_STORY_POST_REPOST, FEATURE_STORY_PRELOADING, FEATURE_STORY_QUESTION, FEATURE_STORY_QUESTION_STYLE, FEATURE_STORY_RLOTTIE, FEATURE_STORY_RLOTTIE_CACHE, FEATURE_STORY_SAVE_DEFAULT_ON, FEATURE_STORY_SEND_DIALOG_LIST, FEATURE_STORY_STICKER_PACK, FEATURE_STORY_TIME, FEATURE_STORY_VIEWER_CAMERA, FEATURE_STORY_VOICE_ANSWER, FEATURE_STORY_WITH_CONTEXT, FEATURE_SUPERAPP_MENU, FEATURE_TRAFFIC_SAVER, FEATURE_VKPAY_WIDGET, FEATURE_VKUI_INTERNAL_TO_MINI_APPS, FEATURE_VKUI_TOKEN_CACHE, FEATURE_VK_APPS_SEARCH, FEATURE_VOTES_BALANCE, FEATURE_WEBVIEW_TOKEN_ACTIVE, FEATURE_MILKSHAKE_FORCE_DISABLED, FEATURE_MILKSHAKE_ACTIVATION_DISABLED, EXPERIMENT_NEWS_DISABLE_CACHE ->
true;
default -> false;
};
}
public static boolean redirect(Features.Type feature) {
switch (feature) {
case EXPERIMENT_NEWS_DISABLE_CACHE:
return !feedcache();
case FEATURE_SUPERAPP_MENU:
return superapp();
case FEATURE_MILKSHAKE:
return milkshake();
case FEATURE_MILKSHAKE_FORCE_DISABLED:
case FEATURE_MILKSHAKE_ACTIVATION_DISABLED:
return !milkshake();
case AB_NEWS_VIDEO_LAYOUT_TEXT:
case FEATURE_COMPACT_REPOST:
case EXPERIMENT_NEWS_VIDEO_LAYOUT_TEXT:
return postsredesign();
case FEATURE_IMAGE_QUALITY_UPGRADE:
return !getBoolValue("compressPhotos", true);
case FEATURE_MENU_GAMES_CAROUSEL:
return miniapps();
default:
return true;
}
return switch (feature) {
case EXPERIMENT_NEWS_DISABLE_CACHE -> !feedcache();
case FEATURE_SUPERAPP_MENU -> superapp();
case FEATURE_MILKSHAKE -> milkshake();
case FEATURE_MILKSHAKE_FORCE_DISABLED, FEATURE_MILKSHAKE_ACTIVATION_DISABLED -> !milkshake();
case AB_NEWS_VIDEO_LAYOUT_TEXT, FEATURE_COMPACT_REPOST, EXPERIMENT_NEWS_VIDEO_LAYOUT_TEXT ->
postsredesign();
case FEATURE_IMAGE_QUALITY_UPGRADE -> !getBoolValue("compressPhotos", true);
case FEATURE_MENU_GAMES_CAROUSEL -> miniapps();
default -> true;
};
}
public static FeatureManager.b redirectStrings(Features.Type feature) {
@@ -5,7 +5,6 @@ import static ru.vtosters.lite.utils.AndroidUtils.getGlobalContext;
import static ru.vtosters.lite.utils.AndroidUtils.getPrefsValue;
import static ru.vtosters.lite.utils.Preferences.getBoolValue;
import static ru.vtosters.lite.utils.ThemesUtils.getAccentColor;
import static ru.vtosters.lite.utils.ThemesUtils.getAttrId;
import static ru.vtosters.lite.utils.ThemesUtils.getBackgroundContent;
import static ru.vtosters.lite.utils.ThemesUtils.getBackgroundPage;
import static ru.vtosters.lite.utils.ThemesUtils.getColorFromAttr;
@@ -17,7 +16,10 @@ import static ru.vtosters.lite.utils.ThemesUtils.hex;
import android.webkit.WebView;
import com.vk.articles.preload.WebCachePreloader;
import com.vtosters.lite.R;
import com.vtosters.lite.data.ThemeTracker;
import ru.vtosters.lite.themes.ThemesCore;
import ru.vtosters.lite.utils.ThemesUtils;
import java.io.BufferedReader;
import java.io.IOException;
@@ -61,10 +63,10 @@ public class VKUIHook {
.replace("%header_text%", hex(getHeaderText()))
.replace("%accent%", hex(getAccentColor()))
.replace("%background_page%", hex(getBackgroundPage()))
.replace("%content_tint_background%", hex(getColorFromAttr(getAttrId("content_tint_background"))))
.replace("%button_primary_background%", hex(getColorFromAttr(getAttrId("button_primary_background"))))
.replace("%button_outline_foreground%", hex(getColorFromAttr(getAttrId("button_outline_foreground"))))
.replace("%button_outline_border%", hex(getColorFromAttr(getAttrId("button_outline_border"))))
.replace("%content_tint_background%", hex(getColorFromAttr(R.attr.content_tint_background)))
.replace("%button_primary_background%", hex(getColorFromAttr(R.attr.button_primary_background)))
.replace("%button_outline_foreground%", hex(getColorFromAttr(R.attr.button_outline_foreground)))
.replace("%button_outline_border%", hex(getColorFromAttr(R.attr.button_outline_border)))
.replace("%text_primary%", hex(getTextAttr()));
@@ -77,22 +79,19 @@ public class VKUIHook {
}
public static void inject(WebView webView) {
String string = getPrefsValue("darktheme");
if (getBoolValue("VKUI_INJ", true)) {
if (!isLoaded) {
load();
}
StringBuilder sb = new StringBuilder();
if (true/*Themes.isAccentedCache()*/) {
if (ThemesCore.isCachedAccents()) {
sb.append("\n\n");
sb.append(loadedCSS);
}
if (string.equals("amoled")) {
if (ThemesUtils.isAmoledTheme()) {
sb.append("\n\n");
sb.append(loadedCSSAmoled);
}
@@ -2,12 +2,13 @@ package ru.vtosters.lite.hooks;
import static ru.vtosters.lite.utils.Preferences.wbios;
import ru.vtosters.lite.utils.AndroidUtils;
import com.vtosters.lite.R;
import ru.vtosters.lite.utils.ThemesUtils;
public class WritebarHook {
public static int getWriteBar() {
return AndroidUtils.getIdentifier((wbios() ? "write_bar_i" : "write_bar"), "layout");
return wbios() ? R.layout.write_bar_i : R.layout.write_bar;
}
public static int getIconsColors(int def) {
@@ -0,0 +1,221 @@
package ru.vtosters.lite.music;
import android.os.RemoteException;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.Nullable;
import com.vk.dto.music.MusicTrack;
import com.vtosters.lite.R;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import bruhcollective.itaysonlab.libvkx.client.LibVKXClient;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.FormBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import ru.vtosters.lite.di.singleton.VtOkHttpClient;
import ru.vtosters.lite.downloaders.AudioDownloader;
import ru.vtosters.lite.music.cache.CacheDatabaseDelegate;
import ru.vtosters.lite.utils.AndroidUtils;
import ru.vtosters.lite.utils.Preferences;
public class LastFMScrobbler {
private static final String KEY = "5965d63402414776c54c266db0211746";
private static final String KEY_SECRET = "d982180eed13275bb948e41cf225f88f";
private static final String URL = "https://ws.audioscrobbler.com/2.0/";
private static final OkHttpClient CLIENT = VtOkHttpClient.getInstance();
private static final List<String> SCROBBLED_UUIDS = new ArrayList<>();
public static void grabMusicTrack(MusicTrack musictrack) {
var uid = musictrack.y1();
var artist = musictrack.C;
var title = musictrack.f;
var duration = musictrack.h;
var track_id = musictrack.d;
var owner_id = musictrack.e;
var access_key = !TextUtils.isEmpty(musictrack.J) ? musictrack.J : "";
var album_id = musictrack.I;
var album = album_id != null ? album_id.getTitle() : null;
if (TextUtils.isEmpty(uid) || TextUtils.isEmpty(artist) || TextUtils.isEmpty(title) || duration == 0) {
Log.d("Scrobbler", "grabTrackInfo: " + "Empty track, info: " + artist + " - " + title + " - " + duration + " - " + uid);
return;
}
if (Preferences.autocache() && !CacheDatabaseDelegate.isCached(uid)) {
if (LibVKXClient.isIntegrationEnabled()) {
LibVKXClient.getInstance().runOnService(service -> {
try {
service.addTrackToCache(track_id, owner_id, access_key);
} catch (RemoteException e) {
e.printStackTrace();
}
});
} else {
AudioDownloader.cacheTrack(musictrack);
}
}
scrobbleTrack(duration, artist, title, uid, album);
}
public static void scrobbleTrack(
long duration,
String artist,
String title,
String uid,
@Nullable String album
) {
Log.d("Scrobbler", "scrobbleTrack: " + title + " - " + artist + " - " + duration + " - " + uid);
if (!needScrobbling(uid) || !isLoggedIn() || TextUtils.isEmpty(artist) || TextUtils.isEmpty(title) || duration == 0)
return;
SCROBBLED_UUIDS.add(uid);
var params = new TreeMap<String, String>();
params.put("api_key", KEY);
params.put("method", "track.scrobble");
params.put("sk", getSessionKey());
params.put("artist[0]", artist);
params.put("track[0]", title);
if (!TextUtils.isEmpty(album))
params.put("album[0]", album);
params.put("duration[0]", String.valueOf(duration));
params.put("timestamp[0]", String.valueOf(System.currentTimeMillis() / 1000));
fetch(params, new Callback() {
@Override
public void a(Call call, IOException e) {
Log.d("Scrobbler", e + "");
}
@Override
public void a(Call call, Response response) throws IOException {
Log.d("Scrobbler", "Scrobbled: " + artist + " - " + title);
Log.d("Scrobbler", "Response scrobble: " + response.a().g());
}
});
}
public static void authenticate(String username, String password) {
if (TextUtils.isEmpty(username) || TextUtils.isEmpty(password)) {
AndroidUtils.sendToast(AndroidUtils.getString(R.string.lastfm_credentials_empty));
return;
}
var params = new TreeMap<String, String>();
params.put("api_key", KEY);
params.put("method", "auth.getMobileSession");
params.put("username", username);
params.put("password", password);
fetch(params, new Callback() {
@Override
public void a(Call call, IOException e) {
Log.d("Scrobbler", "Auth failed", e);
AndroidUtils.sendToast(AndroidUtils.getString(R.string.auth_error_vtl));
}
@Override
public void a(Call call, Response response) throws IOException {
try {
var resBody = response.a().g();
var json = new JSONObject(resBody);
fetchSession(json);
Log.d("Scrobbler", "Auth response " + resBody);
} catch (JSONException e) {
Log.d("Scrobbler", "Auth failed", e);
}
}
});
}
private static String generateMD5(Map<String, String> params) {
var sb = new StringBuilder();
for (var entry : params.entrySet())
sb.append(entry.getKey()).append(entry.getValue());
return AndroidUtils.MD5(sb.append(KEY_SECRET).toString());
}
private static void fetch(TreeMap<String, String> params, Callback callback) {
// Each call must be signed with md5.
// For more information check https://www.last.fm/api
params.put("api_sig", generateMD5(params));
params.put("format", "json");
var reqBodyBuilder = new FormBody.a(StandardCharsets.UTF_8);
for (var entry : params.entrySet())
reqBodyBuilder.a(entry.getKey(), entry.getValue());
var req = new Request.a()
.b(URL)
.a(reqBodyBuilder.a())
.a();
CLIENT.a(req).a(callback);
}
public static void fetchSession(JSONObject json) throws JSONException {
if (!json.has("session")) {
Log.d("Scrobbler", "Auth failed - no session");
return;
}
var session = json.getJSONObject("session");
var name = session.getString("name");
var key = session.getString("key");
AndroidUtils.edit()
.putString("username", name)
.putString("sessionKey", key)
.putBoolean("lastfm_enabled", true)
.apply();
Log.d("Scrobbler", "Auth success as " + name + ", key " + key);
}
public static void logout() {
AndroidUtils.edit()
.remove("username")
.remove("sessionKey")
.putBoolean("lastfm_enabled", false)
.apply();
SCROBBLED_UUIDS.clear();
AndroidUtils.sendToast(AndroidUtils.getString(R.string.lastfm_logout_success));
}
public static boolean needScrobbling(String uid) {
return isLoggedIn() && isScrobblingEnabled() && (SCROBBLED_UUIDS.isEmpty() || !SCROBBLED_UUIDS.get(SCROBBLED_UUIDS.size() - 1).contains(uid));
}
public static boolean isLoggedIn() {
return !TextUtils.isEmpty(getSessionKey());
}
public static boolean isScrobblingEnabled() {
return AndroidUtils.getPreferences().getBoolean("lastfm_enabled", false);
}
public static String getUserName() {
return AndroidUtils.getDefaultPrefs().getString("username", null);
}
private static String getSessionKey() {
return AndroidUtils.getDefaultPrefs().getString("sessionKey", null);
}
}
@@ -1,5 +1,6 @@
package ru.vtosters.lite.music;
import android.util.Log;
import com.vk.dto.music.MusicTrack;
import java.io.File;
@@ -15,67 +16,58 @@ import java.util.concurrent.atomic.AtomicInteger;
import javax.crypto.NoSuchPaddingException;
import java8.util.concurrent.CompletableFuture;
import okhttp3.Call;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import ru.vtosters.lite.di.singleton.VtOkHttpClient;
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;
public class M3UDownloader implements ITrackDownloader {
private static final OkHttpClient client = new OkHttpClient();
private static final OkHttpClient client = VtOkHttpClient.getInstance();
public static M3UDownloader getInstance() {
return Holder.INSTANCE;
}
public void downloadTrack(MusicTrack track, File outDir, Callback callback, boolean cache) {
var request = new Request.a()
.b(track.D)
.a();
client.a(request).a(new okhttp3.Callback() {
@Override
public void a(Call call, IOException e) {
callback.onFailure();
}
if (track.D.isEmpty()) {
Log.d("TrackDownloader", "link error: " + track.y1() + ", title: " + M3UDownloader.getTitle(track));
callback.onFailure();
return;
}
@Override
public void a(Call call, Response response) throws IOException {
parse(response.a().g(), outDir, callback, track, cache);
}
});
var request = new Request.a().b(track.D).a();
try {
var 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) {
if (cache)
try {
ThumbnailDownloader.downloadThumbnails(track);
} catch (IOException e) {
e.printStackTrace();
}
if (cache) try {
ThumbnailDownloader.downloadThumbnails(track);
} catch (IOException e) {
e.printStackTrace();
}
VKM3UParser parser = new VKM3UParser(payload);
List<TransportStream> tses = parser.getTransportStreams();
AtomicInteger progress = new AtomicInteger(0);
List<CompletableFuture<Void>> futures = new ArrayList<>();
List<CompletableFuture<Void>> futures = new ArrayList<>(tses.size());
var tsesDir = new File(outDir, String.valueOf(track.d));
var filesDir = AndroidUtils.getGlobalContext().getFilesDir();
var tsesDir = new File(filesDir, String.valueOf(track.d));
tsesDir.mkdirs();
var resultTs = new File(tsesDir, "result.ts");
String title = null;
if (track.f != null) {
title = track.f;
if (!track.g.isEmpty()) {
title += " (" + track.g + ")";
}
}
var fileName = IOUtils.getValidFileName((cache ? "track" : track.C + " - " + title) + ".mp3");
var fileName = IOUtils.getValidFileName((cache ? "track" : track.C + " - " + getTitle(track)) + ".mp3");
var resultMp3 = new File(outDir, fileName);
callback.onProgress(5);
@@ -96,28 +88,36 @@ public class M3UDownloader implements ITrackDownloader {
IOUtils.writeToFile(tsDump, content);
callback.onProgress(10 + Math.round(80.0f * progress.addAndGet(1) / tses.size()));
callback.onSizeReceived((long) content.length * tses.size(), parser.getHeapSize());
} catch (IOException | NoSuchPaddingException | NoSuchAlgorithmException |
InvalidAlgorithmParameterException | InvalidKeyException e) {
} catch (IOException | NoSuchPaddingException | NoSuchAlgorithmException | InvalidAlgorithmParameterException | InvalidKeyException e) {
callback.onFailure();
throw new RuntimeException(e);
}
}));
}
var future = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
future.join();
future.exceptionally(throwable -> {
callback.onFailure();
return null;
})
.thenApply(v -> TSMerger.merge(tsesDir, resultTs))
.thenApply(mergeResult -> mergeResult && FFMpeg.convert(resultTs, resultMp3.getAbsolutePath(), track))
.thenApply(convertResult -> {
if (convertResult)
callback.onProgress(10 + Math.round(80.0f * progress.addAndGet(1) / tses.size()));
else callback.onFailure();
return convertResult;
})
.thenRun(() -> IOUtils.deleteRecursive(tsesDir))
.thenRun(callback::onSuccess);
callback.onFailure();
return null;
}).thenApply(v -> TSMerger.merge(tsesDir, resultTs)).thenApply(mergeResult -> mergeResult && FFMpeg.convert(resultTs, resultMp3.getAbsolutePath(), track)).thenApply(convertResult -> {
if (convertResult)
callback.onProgress(10 + Math.round(80.0f * progress.addAndGet(1) / tses.size()));
else callback.onFailure();
return convertResult;
}).thenRun(() -> IOUtils.deleteRecursive(tsesDir)).thenRun(callback::onSuccess);
}
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;
}
// Initialization-on-demand
@@ -1,213 +0,0 @@
package ru.vtosters.lite.music;
import static java.lang.String.valueOf;
import static bruhcollective.itaysonlab.libvkx.client.LibVKXClient.getInstance;
import static ru.vtosters.lite.music.cache.CacheDatabaseDelegate.*;
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 static ru.vtosters.lite.utils.Preferences.autocache;
import android.os.RemoteException;
import android.util.Log;
import androidx.annotation.Nullable;
import com.vk.dto.music.MusicTrack;
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 bruhcollective.itaysonlab.libvkx.client.LibVKXClient;
import ru.vtosters.lite.downloaders.AudioDownloader;
import ru.vtosters.lite.music.cache.CacheDatabaseDelegate;
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.AndroidUtils;
public class Scrobbler {
private static final String key = "5965d63402414776c54c266db0211746";
private static final String keysecret = "d982180eed13275bb948e41cf225f88f";
private static final String url = "https://ws.audioscrobbler.com/2.0/";
private static final NetClient client = new NetClient.Builder().build();
private static final ArrayList<String> scrobbled = new ArrayList<>();
public static void scrobbleTrack(long duration, String artist, String title, String uid, @Nullable String album) {
var string = getDefaultPrefs().getString("sessionKey", null);
var timestamp = System.currentTimeMillis() / 1000;
Log.d("Scrobbler", "scrobbleTrack: " + title + " - " + artist + " - " + duration + " - " + uid);
if (!needToBeScrobble(uid) ||
string == null ||
string.isEmpty() ||
artist == null ||
artist.isEmpty() ||
title == null ||
title.isEmpty() ||
duration == 0) return;
scrobbled.add(uid); // add to scrobbled list
TreeMap<String, String> treeMap = new TreeMap<>(); // create map for request
treeMap.put("method", "track.scrobble");
treeMap.put("sk", string);
treeMap.put("artist[0]", artist);
treeMap.put("track[0]", title);
if (album != null && !album.isEmpty())
treeMap.put("album[0]", album);
treeMap.put("timestamp[0]", valueOf(timestamp));
treeMap.put("api_key", key);
treeMap.put("duration[0]", 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);
Log.d("Scrobbler", "Response scrobble: " + response.getDataString());
}
});
}
public static void grabMusicTrack(MusicTrack musictrack) {
var uid = musictrack.y1();
var artist = musictrack.C;
var title = musictrack.f;
var duration = musictrack.h;
var track_id = musictrack.d;
var owner_id = musictrack.e;
var access_key = musictrack.J;
var albumid = musictrack.I;
String album;
if (albumid != null) {
album = albumid.getTitle();
} else {
album = null;
}
if (uid.isEmpty() || artist == null || artist.isEmpty() || title == null || title.isEmpty() || duration == 0) {
Log.d("Scrobbler", "grabTrackInfo: " + "Empty track, info: " + artist + " - " + title + " - " + duration + " - " + uid);
return;
}
if (autocache() && !isCached(uid)) {
if (LibVKXClient.isIntegrationEnabled()){
getInstance().runOnService(service -> {
try {
service.addTrackToCache(track_id, owner_id, (access_key != null) ? access_key : "");
} catch (RemoteException e) {
e.printStackTrace();
}
});
} else {
AudioDownloader.cacheTrack(musictrack);
}
}
scrobbleTrack(duration, artist, title, uid, album);
}
private static String signMD5(Map<String, String> map) {
var sb = new StringBuilder();
for (Map.Entry<String, String> 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) {
if (login == null || pass == null) {
sendToast(AndroidUtils.getString("lastfm_credentials_empty"));
return;
}
TreeMap<String, String> treeMap = new TreeMap<>(); // create map for request
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(AndroidUtils.getString("auth_error_vtl"));
}
@Override
public void onResponse(NetCall call, NetResponse response) {
try {
var jSONObject = new JSONObject(response.getDataString());
sessionFetcher(jSONObject);
Log.d("Scrobbler", "Auth response " + response.getDataString());
} catch (JSONException e) {
Log.d("Scrobbler", "Auth failed", e);
}
}
});
}
public static void sessionFetcher(JSONObject jsonObject) throws JSONException {
if (!jsonObject.has("session")) {
Log.d("Scrobbler", "Auth failed - no session");
return;
}
var session = jsonObject.getJSONObject("session");
var name = session.getString("name");
var key = session.getString("key");
getDefaultPrefs().edit().putString("username", name).
putString("sessionKey", key).apply();
Log.d("Scrobbler", "Auth success as " + name + ", key " + key);
}
public static void logout() {
edit().remove("username").apply();
edit().remove("sessionKey").apply();
scrobbled.clear();
sendToast(AndroidUtils.getString("lastfm_logout_success"));
}
public static boolean needToBeScrobble(String uid) {
return isLoggedIn() && isScrobblingEnabled() && !scrobbled.contains(uid);
}
public static boolean isLoggedIn() {
return getDefaultPrefs().getString("sessionKey", null) != null;
}
public static boolean isScrobblingEnabled() {
return getPreferences().getBoolean("lastfm_enabled", false);
}
public static String getUserName() {
return getDefaultPrefs().getString("username", null);
}
}
@@ -45,7 +45,7 @@ public class VKM3UParser {
if (mTransportStreams.size() == i)
mTransportStreams.add(new TransportStream());
} else if (isM3U8(line)) {
mTransportStreams.get(i).setName(line);
mTransportStreams.get(i).setName(line.substring(line.lastIndexOf("/") + 1));
++i;
}
}
@@ -2,7 +2,6 @@ package ru.vtosters.lite.music.cache;
import static ru.vtosters.lite.music.cache.FileCacheImplementation.getCacheDir;
import static ru.vtosters.lite.utils.AndroidUtils.getGlobalContext;
import static ru.vtosters.lite.utils.AndroidUtils.getString;
import static ru.vtosters.lite.utils.AndroidUtils.sendToast;
import static ru.vtosters.lite.utils.IOUtils.deleteRecursive;
@@ -19,6 +18,7 @@ import androidx.annotation.Nullable;
import com.vk.core.util.ToastUtils;
import com.vk.dto.music.MusicTrack;
import com.vtosters.lite.R;
import org.json.JSONObject;
@@ -30,6 +30,7 @@ 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;
public class CacheDatabaseDelegate {
private static final int DB_VERSION = 0x1;
@@ -85,7 +86,7 @@ public class CacheDatabaseDelegate {
getHelper().getReadableDatabase().execSQL(formula);
} catch (Exception e) {
e.printStackTrace();
ToastUtils.a(getString("track_bd_insert_err") + " [" + e.getMessage() + "]", true);
ToastUtils.a(AndroidUtils.getString(R.string.track_bd_insert_err) + " [" + e.getMessage() + "]", true);
}
}
@@ -96,7 +97,7 @@ public class CacheDatabaseDelegate {
public static void clear() {
getHelper().getReadableDatabase().execSQL(String.format("DELETE FROM %s", TABLE_NAME));
deleteRecursive(getCacheDir());
sendToast(getString("cached_tracks_deleted"));
sendToast(AndroidUtils.getString(R.string.cached_tracks_deleted));
}
@NonNull
@@ -1,6 +1,5 @@
package ru.vtosters.lite.music.cache;
import android.content.Context;
import android.net.Uri;
import com.vk.dto.music.MusicTrack;
@@ -1,10 +1,9 @@
package ru.vtosters.lite.music.cache.helpers;
import static ru.vtosters.lite.utils.AccountManagerUtils.getUserId;
import static ru.vtosters.lite.utils.AccountManagerUtils.getUserPhoto;
import static ru.vtosters.lite.utils.AndroidUtils.getString;
import com.vk.dto.music.Playlist;
import com.vtosters.lite.R;
import org.json.JSONArray;
import org.json.JSONException;
@@ -15,7 +14,7 @@ import java.util.List;
import ru.vtosters.lite.music.cache.CacheDatabaseDelegate;
import ru.vtosters.lite.music.cache.FileCacheImplementation;
import ru.vtosters.lite.utils.AccountManagerUtils;
import ru.vtosters.lite.utils.AndroidUtils;
public class PlaylistHelper {
@@ -55,7 +54,7 @@ public class PlaylistHelper {
.put("type", 1)
.put("album_type", "main_only")
.put("title", title)
.put("description", albumId + "/ " + getString("cached_album_warning"))
.put("description", albumId + "/ " + AndroidUtils.getString(R.string.cached_album_warning))
.put("main_artists", new JSONArray()
.put(new JSONObject().put("name", artist)
.put("id", "-1")
@@ -92,8 +91,8 @@ public class PlaylistHelper {
.put("owner_id", getUserId())
.put("type", 0)
.put("album_type", "playlist")
.put("title", getString("cached_tracks_title"))
.put("description", getString("cached_tracks_desc"))
.put("title", AndroidUtils.getString(R.string.cached_tracks_title))
.put("description", AndroidUtils.getString(R.string.cached_tracks_desc))
.put("access_key", "cache")
.put("genres", new JSONArray())
.put("is_following", false)
@@ -135,7 +134,7 @@ public class PlaylistHelper {
.put("data_type", "none")
.put("layout", new JSONObject()
.put("name", "header")
.put("title", getString("cached_tracks_title")));
.put("title", AndroidUtils.getString(R.string.cached_tracks_title)));
}
public static JSONObject getCatalogSeparator() throws JSONException {
@@ -13,6 +13,8 @@ import org.json.JSONObject;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import bruhcollective.itaysonlab.libvkx.client.LibVKXClient;
import io.reactivex.Observable;
@@ -21,14 +23,13 @@ import ru.vtosters.lite.downloaders.AudioDownloader;
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";
private static final ExecutorService executor = Executors.newCachedThreadPool();
public static void injectDownloadPlaylist(Playlist playlist) {
AudioDownloader.cachePlaylist(playlist);
executor.submit(() -> AudioDownloader.cachePlaylist(playlist));
}
public static boolean eligibleForOfflineCaching() {
@@ -1,13 +1,18 @@
package ru.vtosters.lite.music.cache.injectors;
import static ru.vtosters.lite.music.cache.CacheDatabaseDelegate.hasTracks;
import static ru.vtosters.lite.utils.AndroidUtils.getString;
import static ru.vtosters.lite.music.cache.helpers.PlaylistHelper.getCatalogHeader;
import static ru.vtosters.lite.music.cache.helpers.PlaylistHelper.getCatalogPlaylist;
import static ru.vtosters.lite.music.cache.helpers.PlaylistHelper.getCatalogSeparator;
import static ru.vtosters.lite.music.cache.helpers.PlaylistHelper.getPlaylist;
import static ru.vtosters.lite.utils.AccountManagerUtils.getUserId;
import android.util.Log;
import com.vk.catalog2.core.CatalogParser;
import com.vk.catalog2.core.api.dto.CatalogResponse;
import com.vk.dto.music.MusicTrack;
import com.vtosters.lite.R;
import org.json.JSONArray;
import org.json.JSONException;
@@ -20,9 +25,9 @@ import java.util.UUID;
import bruhcollective.itaysonlab.libvkx.client.LibVKXClient;
import io.reactivex.Observable;
import io.reactivex.ObservableOnSubscribe;
import ru.vtosters.lite.music.cache.helpers.PlaylistHelper;
import ru.vtosters.lite.music.cache.CacheDatabaseDelegate;
import ru.vtosters.lite.music.cache.helpers.TracklistHelper;
import ru.vtosters.lite.utils.AccountManagerUtils;
import ru.vtosters.lite.utils.AndroidUtils;
import ru.vtosters.lite.utils.NetworkUtils;
public class TracklistInjector {
@@ -69,82 +74,73 @@ public class TracklistInjector {
}
private static JSONObject getEmptyCatalog() throws JSONException {
return new JSONObject("{\"catalog\":{\"default_section\":\"cache\",\"sections\":[{\"id\":\"cache\",\"title\":\"" + getString("cached_content_title") + "\",\"blocks\":[{\"data_type\":\"placeholder\",\"id\":\"0\",\"layout\":{\"name\":\"placeholder_big\"},\"placeholder_ids\":[\"_synth_trackEmpty\"]}]}]},\"audios\":[],\"playlists\":[],\"placeholders\":[{\"id\":\"_synth_trackEmpty\",\"title\":\"" + getString("cached_content_empty") + "\",\"text\":\"" + getString("cached_content_empty_desc") + "\",\"buttons\":[]}]}");
return new JSONObject("{\"catalog\":{\"default_section\":\"cache\",\"sections\":[{\"id\":\"cache\",\"title\":\"" + AndroidUtils.getString(R.string.cached_content_title) + "\",\"blocks\":[{\"data_type\":\"placeholder\",\"id\":\"0\",\"layout\":{\"name\":\"placeholder_big\"},\"placeholder_ids\":[\"_synth_trackEmpty\"]}]}]},\"audios\":[],\"playlists\":[],\"placeholders\":[{\"id\":\"_synth_trackEmpty\",\"title\":\"" + AndroidUtils.getString(R.string.cached_content_empty) + "\",\"text\":\"" + AndroidUtils.getString(R.string.cached_content_empty_desc) + "\",\"buttons\":[]}]}");
}
public static void injectIntoExistingCatalog(JSONObject catalogNode) {
Log.d("TracklistInjector", "injectIntoExistingCatalog");
if (!hasTracks()) return;
try {
var sectionsNode = catalogNode.getJSONObject("catalog")
.getJSONArray("sections");
var catalog = catalogNode.getJSONObject("catalog");
var sectionsNode = 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;
if (!firstSection.getString("url").equals("https://vk.com/audios" + getUserId() + "?section=all"))
return;
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 (CacheDatabaseDelegate.hasTracks() && !LibVKXClient.isIntegrationEnabled()) { // inj in playlist list
var noPlaylists = blocks.optJSONObject(0).optJSONArray("buttons").optJSONObject(0).optJSONObject("action").optString("type").equals("create_playlist");
if (noPlaylists) {
catalogNode.put("playlists", new JSONArray().put(getPlaylist()));
Log.d("catalogInjector", "added new pl");
var newBlocks = new JSONArray();
newBlocks
.put(getCatalogHeader())
.put(getCatalogPlaylist())
.put(getCatalogSeparator());
Log.d("catalogInjector", "added cache catalog playlist");
for (int i = 0; i < blocks.length(); i++) {
newBlocks.put(blocks.optJSONObject(i));
}
firstSection.put("blocks", newBlocks);
} else {
try {
catalogNode.optJSONArray("playlists").put(getPlaylist());
Log.d("catalogInjector", "added to exist pl");
for (int i = 0; i < blocks.length(); i++) {
var j = blocks.optJSONObject(i);
var type = j.optString("data_type");
if (type.equals("music_playlists") && j.has("playlists_ids")) {
var newarr = new JSONArray();
var playlists_ids = j.optJSONArray("playlists_ids");
newarr.put(getUserId() + "_-1");
for (int n = 0; n < playlists_ids.length(); n++) {
newarr.put(playlists_ids.optString(n));
}
j.put("playlists_ids", newarr);
break;
}
}
} catch (Exception e) {
Log.d("catalogInjector", e.toString());
}
}
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();
} catch (Exception e) {
Log.d("TracklistInjector", e.toString());
}
}
@@ -160,8 +156,8 @@ public class TracklistInjector {
var catalogBanner = new JSONObject()
.put("id", 1234)
.put("image_mode", "cover")
.put("title", getString("vkx_integration_title"))
.put("text", getString("vkx_integration_desc"))
.put("title", AndroidUtils.getString(R.string.vkx_integration_title))
.put("text", AndroidUtils.getString(R.string.vkx_integration_desc))
.put("click_action", new JSONObject().put("action", clickAction));
var catalogBanners = new JSONArray()
.put(catalogBanner);
@@ -174,7 +170,7 @@ public class TracklistInjector {
var layout = new JSONObject()
.put("name", "list")
.put("owner_id", AccountManagerUtils.getUserId());
.put("owner_id", getUserId());
audiosBlock.put("layout", layout);
@@ -200,7 +196,7 @@ public class TracklistInjector {
var section = new JSONObject()
.put("id", randomId)
.put("title", getString("saved_tracks_title"))
.put("title", AndroidUtils.getString(R.string.saved_tracks_title))
.put("url", "synth:cache")
.put("blocks", blocks);
@@ -1,14 +1,15 @@
package ru.vtosters.lite.music.callback;
import static ru.vtosters.lite.utils.AndroidUtils.getString;
import android.util.Log;
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.utils.AndroidUtils;
public class MusicCallbackBuilder {
private static final NotificationManagerCompat notificationManager = NotificationChannels.getNotificationManager();
@@ -35,7 +36,7 @@ public class MusicCallbackBuilder {
public void onSuccess() {
try {
notification
.setContentText(getString("player_download_finished"))
.setContentText(AndroidUtils.getString(R.string.player_download_finished))
.setProgress(0, 0, false)
.setSmallIcon(android.R.drawable.stat_sys_download_done);
notificationManager.notify(notificationId, notification.build());
@@ -48,7 +49,7 @@ public class MusicCallbackBuilder {
@Override
public void onFailure() {
notification.setContentText(getString("load_audio_error")).setProgress(0, 0, false);
notification.setContentText(AndroidUtils.getString(R.string.load_audio_error)).setProgress(0, 0, false);
notificationManager.notify(notificationId, notification.build());
}
@@ -66,7 +67,7 @@ public class MusicCallbackBuilder {
if (progress == playlistSize) {
onSuccess();
} else {
notification.setContentText(String.format(getString("playlist_download_notf"), progress, playlistSize));
notification.setContentText(String.format(AndroidUtils.getString(R.string.playlist_download_notf), progress, playlistSize));
notification.setProgress(playlistSize, progress, false);
}
notificationManager.notify(notificationId, notification.build());
@@ -74,13 +75,13 @@ public class MusicCallbackBuilder {
@Override
public void onSuccess() {
notification.setContentText(getString("player_download_finished"));
notification.setContentText(AndroidUtils.getString(R.string.player_download_finished));
notification.setProgress(0, 0, false);
}
@Override
public void onFailure() {
notification.setContentText(getString("load_audio_error")).setProgress(0, 0, false);
notification.setContentText(AndroidUtils.getString(R.string.load_audio_error)).setProgress(0, 0, false);
notificationManager.notify(notificationId, notification.build());
}
@@ -0,0 +1,296 @@
package ru.vtosters.lite.music.catalog.inject;
import android.util.Log;
import bruhcollective.itaysonlab.libvkx.client.LibVKXClient;
import com.vk.core.network.Network;
import com.vk.core.util.DeviceIdProvider;
import okhttp3.Headers;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import ru.vtosters.lite.di.singleton.VtOkHttpClient;
import ru.vtosters.lite.music.cache.CacheDatabaseDelegate;
import ru.vtosters.lite.utils.AccountManagerUtils;
import ru.vtosters.lite.utils.AndroidUtils;
import java.io.IOException;
import static ru.vtosters.lite.hooks.DateHook.getLocale;
import static ru.vtosters.lite.music.cache.helpers.PlaylistHelper.*;
import static ru.vtosters.lite.proxy.ProxyUtils.getApi;
import static ru.vtosters.lite.utils.AccountManagerUtils.getUserId;
import static ru.vtosters.lite.utils.Preferences.dev;
import static ru.vtosters.lite.utils.Preferences.getBoolValue;
public class CatalogJsonInjector {
private static final OkHttpClient mClient = VtOkHttpClient.getInstance();
private static boolean isLoaded;
public static JSONObject music(JSONObject json) throws JSONException {
var catalog = json.optJSONObject("catalog");
JSONArray oldItems = null;
if (catalog != null) {
oldItems = catalog.optJSONArray("sections");
}
var useOldAppVer = getBoolValue("useOldAppVer", false);
var isUsersCatalog = oldItems.optJSONObject(0).optString("url").equals("https://vk.com/audios" + getUserId() + "?section=" + (useOldAppVer ? "all" : "general"));
if (isUsersCatalog) {
var blocks = oldItems.optJSONObject(0).optJSONArray("blocks");
if (!useOldAppVer) {
if (blocks != null) {
for (int i = 0; i < blocks.length(); i++) {
var block = blocks.optJSONObject(i);
var layout = block.optJSONObject("layout");
if (layout != null) {
var name = layout.optString("name");
if (name.equals("header_extended")) {
if (layout.has("top_title")) blocks.remove(i);
try {
layout.put("name", "header");
} catch (JSONException e) {
e.printStackTrace();
}
}
}
}
}
var playlists = fetchCatalogId("https://vk.com/audio?section=my_playlists");
if (playlists != null) {
var catalogarr = playlists.optJSONObject("catalog").optJSONArray("sections").optJSONObject(0);
var title = catalogarr.optString("title");
var id = catalogarr.optString("id");
var url = catalogarr.optString("url");
if (dev()) Log.d("VKMusic", "Added " + title + " in music sections");
oldItems.put(new JSONObject().put("id", id).put("title", title).put("url", url));
}
var albums = fetchCatalogId("https://vk.com/audio?section=albums");
if (albums != null) {
var catalogarr = albums.optJSONObject("catalog").optJSONArray("sections").optJSONObject(0);
var title = catalogarr.optString("title");
var id = catalogarr.optString("id");
var url = catalogarr.optString("url");
if (dev()) Log.d("VKMusic", "Added " + title + " in music sections");
oldItems.put(new JSONObject().put("id", id).put("title", title).put("url", url));
}
for (int i = 0; i < oldItems.length(); i++) {
var item = oldItems.optJSONObject(i);
var title = item.optString("title");
var id = item.optString("id");
var url = item.optString("url");
var value = AndroidUtils.getPrefsValue("musicdefcatalog");
Log.d("VKMusic", "title: " + title + " id: " + id + " url: " + url + " value: " + value);
if (url.contains(value) && !value.isEmpty()) {
catalog.put("default_section", id);
if (dev()) Log.d("VKMusic", "Added " + title + " as default music section");
}
}
}
if (CacheDatabaseDelegate.hasTracks() && !LibVKXClient.isIntegrationEnabled()) { // inj in playlist list
var noPlaylists = !json.has("playlists");
if (noPlaylists) {
json.put("playlists", new JSONArray().put(getPlaylist()));
Log.d("catalogInjector", "added new pl");
} else {
try {
json.optJSONArray("playlists").put(getPlaylist());
Log.d("catalogInjector", "added to exist pl");
} catch (Exception e) {
Log.d("catalogInjector", e.toString());
}
}
if (blocks != null && (!useOldAppVer || noPlaylists)) {
var newBlocks = new JSONArray();
newBlocks
.put(getCatalogHeader())
.put(getCatalogPlaylist())
.put(getCatalogSeparator());
isLoaded = false;
Log.d("VKMusic", "added cache catalog playlist");
for (int i = 0; i < blocks.length(); i++) {
newBlocks.put(blocks.optJSONObject(i));
}
oldItems.optJSONObject(0).put("blocks", newBlocks);
}
}
}
return json;
}
public static JSONObject catalogInjector(JSONObject json) {
try {
var section = json.optJSONObject("section");
var useOldAppVer = getBoolValue("useOldAppVer", false);
var isUsersCatalog = section.optString("url").equals("https://vk.com/audios" + getUserId() + "?section=" + (useOldAppVer ? "all" : "general"));
if (isUsersCatalog && CacheDatabaseDelegate.hasTracks() && !LibVKXClient.isIntegrationEnabled()) {
var blocks = section.getJSONArray("blocks");
var noPlaylists = !json.has("playlists");
// Log.d("catalogInjector", "type: " + blocks.optJSONObject(0).optJSONArray("buttons").optJSONObject(0).optJSONObject("action").optString("type"));
if (noPlaylists) {
json.put("playlists", new JSONArray().put(getPlaylist()));
Log.d("catalogInjector", "added new pl");
} else {
try {
json.optJSONArray("playlists").put(getPlaylist());
Log.d("catalogInjector", "added to exist pl");
} catch (Exception e) {
Log.d("catalogInjector", e.toString());
}
}
if ((!useOldAppVer || noPlaylists) && !isLoaded) {
var newBlocks = new JSONArray();
newBlocks
.put(getCatalogHeader())
.put(getCatalogPlaylist())
.put(getCatalogSeparator());
isLoaded = true;
Log.d("catalogInjector", "added cache catalog playlist");
for (int i = 0; i < blocks.length(); i++) {
newBlocks.put(blocks.optJSONObject(i));
}
section.put("blocks", newBlocks);
} else {
for (int i = 0; i < blocks.length(); i++) {
var j = blocks.optJSONObject(i);
var type = j.optString("data_type");
if (type.equals("music_playlists") && j.has("playlists_ids")) {
var newarr = new JSONArray();
var playlists_ids = j.optJSONArray("playlists_ids");
newarr.put(getUserId() + "_-1");
for (int n = 0; n < playlists_ids.length(); n++) {
newarr.put(playlists_ids.optString(n));
}
Log.d("catalogInjector", "added to pl ids");
j.put("playlists_ids", newarr);
break;
}
}
}
}
} catch (JSONException e) {
Log.d("catalogInjector", e.toString());
}
return json;
}
public static JSONObject musiclink(JSONObject json) {
var links = json.optJSONArray("links");
if (links != null && !getBoolValue("useOldAppVer", false)) {
if (links.optJSONObject(0).optString("url").contains("?section=recent")) {
json.remove("links");
if (dev()) Log.d("VKMusic", "Removed links buttons");
}
}
try {
fixHeaders(json.getJSONObject("section"));
} catch (Exception e) {
e.printStackTrace();
}
return catalogInjector(json);
}
public static JSONObject fixArtists(JSONObject json) {
try {
fixHeaders(json.getJSONObject("catalog").getJSONArray("sections").getJSONObject(0));
} catch (Exception e) {
e.printStackTrace();
}
return json;
}
public static void fixHeaders(JSONObject json) {
var blocks = json.optJSONArray("blocks");
if (blocks != null) {
for (int i = 0; i < blocks.length(); i++) {
var block = blocks.optJSONObject(i);
var layout = block.optJSONObject("layout");
if (layout != null) {
var name = layout.optString("name");
if (name.equals("header_extended")) {
if (layout.has("top_title")) blocks.remove(i);
try {
layout.put("name", "header");
} catch (JSONException e) {
e.printStackTrace();
}
}
}
}
}
}
private static JSONObject fetchCatalogId(String section) {
if (section.isEmpty()) return null;
var request = new Request.a()
.b("https://" + getApi() + "/method/" + "catalog.getAudio"
+ "?v=5.119"
+ "&https=1"
+ "&need_blocks=1"
+ "&lang="
+ getLocale()
+ "&device_id="
+ DeviceIdProvider.d(AndroidUtils.getGlobalContext())
+ "&url="
+ section
+ "&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()).getJSONObject("response");
} catch (JSONException | IOException e) {
Log.d("VTLMusic", "Error: " + e.getMessage());
}
return null;
}
}
@@ -7,6 +7,7 @@ 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 java.io.File;
import java.io.FileOutputStream;
@@ -27,15 +28,13 @@ public class FFMpeg {
* @return True if success, else false
*/
public static boolean convert(File in, String out, MusicTrack track) {
checkFFMpegLibs();
// 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(" -hide_banner");
sb.append(" -write_id3v2 1");
String title = null;
@@ -59,6 +58,7 @@ public class FFMpeg {
if (track.I != null) {
sb.append(" -metadata album=\"").append(track.I.getTitle()).append("\"");
}
sb.append(" -write_xing 0");
sb.append(" -c copy \"").append(out).append("\"");
@@ -99,7 +99,7 @@ public class FFMpeg {
private static void checkFFMpegLibs() {
var lib = new File(AndroidUtils.getGlobalContext().getFilesDir(), "libffmpegkit_abidetect.so");
if (!lib.exists()) {
ToastUtils.a(AndroidUtils.getString("ffmpegkit_copy"));
ToastUtils.a(R.string.ffmpegkit_copy);
copyAssets();
}
}
@@ -7,6 +7,7 @@ import com.vk.dto.music.MusicTrack;
import java.io.File;
import bruhcollective.itaysonlab.libvkx.client.LibVKXClient;
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;
@@ -15,6 +16,12 @@ import ru.vtosters.lite.music.interfaces.ITrackDownloader;
public class TrackDownloader {
public static void downloadTrack(MusicTrack track, String path, Callback callback) {
if (track.D.isEmpty()) {
Log.d("TrackDownloader", "link error: " + track.y1() + ", title: " + M3UDownloader.getTitle(track));
callback.onFailure();
return;
}
download(track, path, callback, M3UDownloader.getInstance(), false);
}
@@ -22,6 +29,10 @@ public class TrackDownloader {
if (CacheDatabaseDelegate.isCached(track.y1())) {
callback.onSuccess();
return;
} else if (track.D.isEmpty()) {
Log.d("TrackDownloader", "link error: " + track.y1() + ", title: " + M3UDownloader.getTitle(track));
callback.onFailure();
return;
}
var path = FileCacheImplementation.getTrackFolder(LibVKXClient.asId(track)).getAbsolutePath();
@@ -36,6 +47,9 @@ public class TrackDownloader {
Log.v("TrackDownloader", "Directory created");
else
Log.e("TrackDownloader", "Directory creation failed");
downloader.downloadTrack(track, outDir, callback, cache);
VTExecutors.getMusicDownloadExecutor().execute(
() -> downloader.downloadTrack(track, outDir, callback, cache)
);
}
}
@@ -1,4 +1,4 @@
package ru.vtosters.lite.feature.music;
package ru.vtosters.lite.music.hook;
import android.content.Context;
@@ -1,4 +1,4 @@
package ru.vtosters.lite.hooks.ui;
package ru.vtosters.lite.music.hook;
import static bruhcollective.itaysonlab.libvkx.client.LibVKXClient.asId;
import static bruhcollective.itaysonlab.libvkx.client.LibVKXClient.getInstance;
@@ -19,16 +19,13 @@ import com.vk.music.common.MusicPlaybackLaunchContext;
import com.vtosters.lite.R;
import java.util.ArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import bruhcollective.itaysonlab.libvkx.client.LibVKXClientImpl;
import ru.vtosters.lite.downloaders.AudioDownloader;
import ru.vtosters.lite.music.cache.CacheDatabaseDelegate;
import ru.vtosters.lite.utils.AndroidUtils;
public class MusicBottomSheetHook {
private static final ExecutorService executor = Executors.newCachedThreadPool();
public static ArrayList<MusicAction> hook(ArrayList<MusicAction> actions, MusicTrack musicTrack) {
if (musicTrack.F1()) return actions;
@@ -41,9 +38,15 @@ public class MusicBottomSheetHook {
if (isIntegrationEnabled()) {
if (isVkxCached(trackid)) {
actions.add(getRemoveCacheTrackVkxAction());
} else {
} else if (isNetworkConnected()){
actions.add(addToCacheTrackVkxAction());
}
} else {
if (isCached(trackid)) {
actions.add(getRemoveCacheTrackAction());
} else if (isNetworkConnected()){
actions.add(addToCacheTrackAction());
}
}
if (isNetworkConnected()) {
@@ -61,18 +64,18 @@ public class MusicBottomSheetHook {
if (isCached(trackid)) {
actions.add(new MusicAction(
AndroidUtils.getIdentifier("music_action_toggle_download", "id"),
R.id.music_action_toggle_download,
milkshake() ? R.drawable.ic_delete_outline_28 : R.drawable.ic_delete_24,
AndroidUtils.getIdentifier("remove_from_cache", "string"),
R.string.remove_from_cache,
musicTrack.F1() ? R.color.music_action_button_gray_50_alpha : R.color.caption_gray,
R.string.music_talkback_empty,
false
));
} else {
actions.add(new MusicAction(
AndroidUtils.getIdentifier("music_action_toggle_download", "id"),
R.id.music_action_toggle_download,
milkshake() ? R.drawable.ic_download_outline_28 : R.drawable.ic_download_24,
AndroidUtils.getIdentifier("add_to_cache", "string"),
R.string.add_to_cache,
musicTrack.F1() ? R.color.music_action_button_gray_50_alpha : R.color.caption_gray,
R.string.music_talkback_empty,
false
@@ -90,10 +93,10 @@ public class MusicBottomSheetHook {
if (isIntegrationEnabled()) {
if (isVkxCached(playlist.a, playlist.b)) {
actions.add(getRemoveCacheTrackVkxAction());
} else {
} else if (isNetworkConnected()){
actions.add(addToCacheTrackVkxAction());
}
} else {
} else if (isNetworkConnected()){
actions.add(addToCacheTrackAction());
}
@@ -103,10 +106,10 @@ public class MusicBottomSheetHook {
}
public static boolean injectOnClick(int actionId, MusicTrack track, MusicPlaybackLaunchContext context, Playlist playlist) { // musictrack inj
if (actionId == AndroidUtils.getIdentifier("play_in_vkx", "id"))
if (actionId == R.id.play_in_vkx)
return tryPlayInVKX(track, context, playlist);
if (actionId == AndroidUtils.getIdentifier("remove_from_cache_vkx", "id")) {
if (actionId == R.id.remove_from_cache_vkx) {
getInstance().runOnService(service -> {
try {
service.deleteTrackFromCache(track.d, track.e);
@@ -115,7 +118,7 @@ public class MusicBottomSheetHook {
}
});
return true;
} else if (actionId == AndroidUtils.getIdentifier("add_to_cache_vkx", "id")) {
} else if (actionId == R.id.add_to_cache_vkx) {
getInstance().runOnService(service -> {
try {
service.addTrackToCache(track.d, track.e, (track.J != null) ? track.J : "");
@@ -126,7 +129,16 @@ public class MusicBottomSheetHook {
return true;
}
if (actionId == AndroidUtils.getIdentifier("download_mp3", "id")) {
if (actionId == R.id.remove_from_cache) {
CacheDatabaseDelegate.removeTrackFromCache(asId(track));
AndroidUtils.sendToast(AndroidUtils.getString("audio_deleted_from_cache"));
return true;
} else if (actionId == R.id.add_to_cache) {
AudioDownloader.cacheTrack(track);
return true;
}
if (actionId == R.id.download_mp3) {
if (isIntegrationEnabled()) {
getInstance().runOnService(service -> {
try {
@@ -146,15 +158,15 @@ public class MusicBottomSheetHook {
}
public static boolean injectOnClick(int actionId, Playlist playlist) { // playlist inj
if (actionId == AndroidUtils.getIdentifier("play_in_vkx", "id"))
if (actionId == R.id.play_in_vkx)
return tryPlayInVKX(null, null, playlist);
if (actionId == AndroidUtils.getIdentifier("add_to_cache", "id")) {
executor.submit(() -> AudioDownloader.cachePlaylist(playlist));
if (actionId == R.id.add_to_cache) {
AudioDownloader.cachePlaylist(playlist);
return true;
}
if (actionId == AndroidUtils.getIdentifier("remove_from_cache_vkx", "id")) {
if (actionId == R.id.remove_from_cache_vkx) {
getInstance().runOnService(service -> {
try {
service.deletePlaylistFromCache(playlist.a, playlist.b);
@@ -163,7 +175,7 @@ public class MusicBottomSheetHook {
}
});
return true;
} else if (actionId == AndroidUtils.getIdentifier("add_to_cache_vkx", "id")) {
} else if (actionId == R.id.add_to_cache_vkx) {
getInstance().runOnService(service -> {
try {
service.addPlaylistToCache(playlist.a, playlist.b, (playlist.Q != null) ? playlist.Q : "");
@@ -174,7 +186,7 @@ public class MusicBottomSheetHook {
return true;
}
if (actionId == AndroidUtils.getIdentifier("download_mp3", "id")) {
if (actionId == R.id.download_mp3) {
if (isIntegrationEnabled()) {
getInstance().runOnService(service -> {
try {
@@ -184,7 +196,7 @@ public class MusicBottomSheetHook {
}
});
} else {
executor.submit(() -> AudioDownloader.downloadPlaylist(playlist));
AudioDownloader.downloadPlaylist(playlist);
}
return true;
}
@@ -207,9 +219,9 @@ public class MusicBottomSheetHook {
private static MusicAction getPlayInVKXAction() {
return new MusicAction(
AndroidUtils.getIdentifier("play_in_vkx", "id"),
R.id.play_in_vkx,
milkshake() ? R.drawable.ic_music_outline_24 : R.drawable.ic_music_24,
AndroidUtils.getIdentifier("play_in_vkx", "string"),
R.string.play_in_vkx,
R.color.caption_gray,
R.string.music_talkback_empty,
false
@@ -218,9 +230,9 @@ public class MusicBottomSheetHook {
private static MusicAction getRemoveCacheTrackAction() {
return new MusicAction(
AndroidUtils.getIdentifier("remove_from_cache", "id"),
R.id.remove_from_cache,
milkshake() ? R.drawable.ic_delete_outline_28 : R.drawable.ic_delete_24,
AndroidUtils.getIdentifier("remove_from_cache", "string"),
R.string.remove_from_cache,
R.color.caption_gray,
R.string.music_talkback_empty,
false
@@ -229,9 +241,9 @@ public class MusicBottomSheetHook {
private static MusicAction addToCacheTrackAction() {
return new MusicAction(
AndroidUtils.getIdentifier("add_to_cache", "id"),
R.id.add_to_cache,
milkshake() ? R.drawable.ic_add_outline_28 : R.drawable.ic_add_24,
AndroidUtils.getIdentifier("add_to_cache", "string"),
R.string.add_to_cache,
R.color.caption_gray,
R.string.music_talkback_empty,
false
@@ -240,9 +252,9 @@ public class MusicBottomSheetHook {
private static MusicAction getRemoveCacheTrackVkxAction() {
return new MusicAction(
AndroidUtils.getIdentifier("remove_from_cache_vkx", "id"),
R.id.remove_from_cache_vkx,
milkshake() ? R.drawable.ic_delete_outline_28 : R.drawable.ic_delete_24,
AndroidUtils.getIdentifier("remove_from_cache_vkx", "string"),
R.string.remove_from_cache_vkx,
R.color.caption_gray,
R.string.music_talkback_empty,
false
@@ -251,9 +263,9 @@ public class MusicBottomSheetHook {
private static MusicAction addToCacheTrackVkxAction() {
return new MusicAction(
AndroidUtils.getIdentifier("add_to_cache_vkx", "id"),
R.id.add_to_cache_vkx,
milkshake() ? R.drawable.ic_add_outline_28 : R.drawable.ic_add_24,
AndroidUtils.getIdentifier("add_to_cache_vkx", "string"),
R.string.add_to_cache_vkx,
R.color.caption_gray,
R.string.music_talkback_empty,
false
@@ -262,9 +274,9 @@ public class MusicBottomSheetHook {
private static MusicAction downloadAsMp3Action() {
return new MusicAction(
AndroidUtils.getIdentifier("download_mp3", "id"),
R.id.download_mp3,
milkshake() ? R.drawable.ic_download_outline_28 : R.drawable.ic_download_24,
AndroidUtils.getIdentifier("download_mp3", "string"),
R.string.download_mp3,
R.color.caption_gray,
R.string.music_talkback_empty,
false
@@ -1,7 +1,5 @@
package ru.vtosters.lite.music.notification;
import static ru.vtosters.lite.utils.AndroidUtils.getString;
import android.app.Notification;
import android.os.Build;
@@ -9,6 +7,7 @@ import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
import com.vk.dto.music.MusicTrack;
import com.vtosters.lite.R;
import ru.vtosters.lite.downloaders.notifications.NotificationChannels;
import ru.vtosters.lite.utils.AccountManagerUtils;
@@ -20,7 +19,7 @@ public class MusicNotificationBuilder {
public static NotificationCompat.Builder buildDownloadNotification(MusicTrack track, int id) {
var notificationBuilder = new NotificationCompat.Builder(AndroidUtils.getGlobalContext(), NotificationChannels.MUSIC_DOWNLOAD_CHANNEL_ID)
.setSmallIcon(android.R.drawable.stat_sys_download)
.setContentTitle(getString("audio_downloading"))
.setContentTitle(AndroidUtils.getString(R.string.audio_downloading))
.setContentText(track.toString())
.setPriority(NotificationCompat.PRIORITY_LOW)
.setCategory(NotificationCompat.CATEGORY_PROGRESS)
@@ -33,7 +32,7 @@ public class MusicNotificationBuilder {
public static NotificationCompat.Builder buildPlaylistDownloadNotification(String playlistName, int id) {
var notificationBuilder = new NotificationCompat.Builder(AndroidUtils.getGlobalContext(), NotificationChannels.MUSIC_PLAYLIST_DOWNLOAD_CHANNEL_ID)
.setSmallIcon(android.R.drawable.stat_sys_download)
.setContentTitle(getString("playlist_downloading") + " «" + playlistName + "»")
.setContentTitle(AndroidUtils.getString(R.string.playlist_downloading) + " «" + playlistName + "»")
.setContentText(playlistName)
.setPriority(NotificationCompat.PRIORITY_LOW)
.setCategory(NotificationCompat.CATEGORY_PROGRESS)
@@ -46,8 +45,8 @@ public class MusicNotificationBuilder {
public static NotificationCompat.Builder buildAllAudiosDownloadNotification(int id) {
var notificationBuilder = new NotificationCompat.Builder(AndroidUtils.getGlobalContext(), NotificationChannels.MUSIC_PLAYLIST_DOWNLOAD_CHANNEL_ID)
.setSmallIcon(android.R.drawable.stat_sys_download)
.setContentTitle(getString("notf_audio_dl_user") + " " + AccountManagerUtils.getUsername())
.setContentText(getString("all_audio"))
.setContentTitle(AndroidUtils.getString(R.string.notf_audio_dl_user) + " " + AccountManagerUtils.getUsername())
.setContentText(AndroidUtils.getString(R.string.all_audio))
.setPriority(NotificationCompat.PRIORITY_LOW)
.setCategory(NotificationCompat.CATEGORY_PROGRESS)
.setAutoCancel(true)
@@ -66,7 +65,7 @@ public class MusicNotificationBuilder {
builder.setSmallIcon(com.vtosters.lite.R.drawable.ic_download_outline_28)
.setShowWhen(false)
.setContentTitle(getString("cached_track") + ": " + track.toString());
.setContentTitle(AndroidUtils.getString(R.string.cached_track) + ": " + track.toString());
notificationManager.notify(track.y1().hashCode(), builder.build());
}
@@ -45,27 +45,14 @@ public class NetClient {
private PasswordAuthentication auth;
public Builder connectTimeout(long l, TimeUnit unit) {
int m;
switch (unit) {
case SECONDS:
m = 1000;
break;
case MILLISECONDS:
m = 1;
break;
case HOURS:
m = 1000 * 60 * 60;
break;
case DAYS:
m = 1000 * 60 * 60 * 24;
break;
case MINUTES:
m = 1000 * 60;
break;
default:
m = 1;
break;
}
int m = switch (unit) {
case SECONDS -> 1000;
case MILLISECONDS -> 1;
case HOURS -> 1000 * 60 * 60;
case DAYS -> 1000 * 60 * 60 * 24;
case MINUTES -> 1000 * 60;
default -> 1;
};
timeout = l * m;
return this;
}
@@ -8,24 +8,32 @@ import java.util.Arrays;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import ru.vtosters.lite.di.singleton.VtOkHttpClient;
public class GithubList {
private static final OkHttpClient mClient = new OkHttpClient();
private static final OkHttpClient mClient = VtOkHttpClient.getInstance();
public static ArrayList<String> getProxy(String url, String proxyname) throws IOException {
Request link = new Request.a()
.b(url)
.a();
var response = mClient.a(link).execute().a().g();
try {
var response = mClient.a(link).execute().a().g();
if (!response.contains(":")) {
Log.d(proxyname, "No proxy found");
return null;
}
Log.d(proxyname, response);
return new ArrayList<>(Arrays.asList(response.split("\n")));
} catch (Exception e) {
Log.d(proxyname, "Failed to fetch data, link: " + link);
if (!response.contains(":")) {
Log.d(proxyname, "No proxy found");
return null;
}
Log.d(proxyname, response);
return new ArrayList<>(Arrays.asList(response.split("\n")));
}
}
@@ -134,24 +134,12 @@ public class ProxyUtils {
public static void setProxy() throws IOException {
switch (getPrefsValue("proxy")) {
case "zaborona":
Zaborona.loadProxy();
break;
case "randomproxy":
RandomProxy.loadProxy();
break;
case "socks":
CustomSocks.loadProxy();
break;
case "http":
CustomHttp.loadProxy();
break;
case "https":
CustomHttps.loadProxy();
break;
default:
resetProxy();
break;
case "zaborona" -> Zaborona.loadProxy();
case "randomproxy" -> RandomProxy.loadProxy();
case "socks" -> CustomSocks.loadProxy();
case "http" -> CustomHttp.loadProxy();
case "https" -> CustomHttps.loadProxy();
default -> resetProxy();
}
}
@@ -19,24 +19,23 @@ public class RandomProxy {
public static void loadProxy() throws IOException {
if (host.isEmpty()) {
getProxy();
loadProxy();
if (getProxy()) loadProxy();
return;
}
switch (type) {
case "socks":
case "socks" -> {
System.setProperty("socksProxyHost", host.split(":")[0]);
System.setProperty("socksProxyPort", host.split(":")[1]);
break;
case "http":
}
case "http" -> {
System.setProperty("http.proxyHost", host.split(":")[0]);
System.setProperty("http.proxyPort", host.split(":")[1]);
break;
case "https":
}
case "https" -> {
System.setProperty("https.proxyHost", host.split(":")[0]);
System.setProperty("https.proxyPort", host.split(":")[1]);
break;
}
}
}
@@ -51,34 +50,20 @@ public class RandomProxy {
edit().putString("proxy", "randomproxy").commit();
}
public static void getProxy() throws IOException {
public static boolean getProxy() throws IOException {
var random = new java.util.Random();
var randomInt = random.nextInt(7);
var randomInt = random.nextInt(8);
switch (randomInt) {
case 0:
Hookzof.loadProxy();
break;
case 1:
TheSpeedX.loadProxy();
break;
case 2:
Jetkai.loadProxy();
break;
case 3:
Roosterkid.loadProxy();
break;
case 4:
ru.vtosters.lite.proxy.http.Jetkai.loadProxy();
break;
case 5:
ru.vtosters.lite.proxy.http.TheSpeedX.loadProxy();
break;
case 6:
ru.vtosters.lite.proxy.https.Jetkai.loadProxy();
break;
case 7:
ru.vtosters.lite.proxy.https.Roosterkid.loadProxy();
break;
case 0 -> Hookzof.loadProxy();
case 1 -> TheSpeedX.loadProxy();
case 2 -> Jetkai.loadProxy();
case 3 -> Roosterkid.loadProxy();
case 4 -> ru.vtosters.lite.proxy.http.Jetkai.loadProxy();
case 5 -> ru.vtosters.lite.proxy.http.TheSpeedX.loadProxy();
case 6 -> ru.vtosters.lite.proxy.https.Jetkai.loadProxy();
case 7 -> ru.vtosters.lite.proxy.https.Roosterkid.loadProxy();
}
return true;
}
}
@@ -4,10 +4,11 @@ import static ru.vtosters.lite.proxy.GithubList.getProxy;
import static ru.vtosters.lite.proxy.ProxyUtils.forceProxyApplying;
import static ru.vtosters.lite.proxy.ProxyUtils.hasProxy;
import static ru.vtosters.lite.utils.AndroidUtils.edit;
import static ru.vtosters.lite.utils.AndroidUtils.getString;
import android.util.Log;
import com.vtosters.lite.R;
import java.io.IOException;
import java.util.ArrayList;
@@ -35,7 +36,7 @@ public class Jetkai {
Log.d(TAG, "Setting proxy: " + proxy.split(":")[0] + ":" + proxy.split(":")[1]);
if (RandomProxy.showProxyResult) {
AndroidUtils.sendToast(getString("proxy_setup_success") + " " + proxy);
AndroidUtils.sendToast(AndroidUtils.getString(R.string.proxy_setup_success) + " " + proxy);
RandomProxy.showProxyResult = false;
RandomProxy.showProxyResult = false;
}
@@ -4,10 +4,11 @@ import static ru.vtosters.lite.proxy.GithubList.getProxy;
import static ru.vtosters.lite.proxy.ProxyUtils.forceProxyApplying;
import static ru.vtosters.lite.proxy.ProxyUtils.hasProxy;
import static ru.vtosters.lite.utils.AndroidUtils.edit;
import static ru.vtosters.lite.utils.AndroidUtils.getString;
import android.util.Log;
import com.vtosters.lite.R;
import java.io.IOException;
import java.util.ArrayList;
@@ -35,7 +36,7 @@ public class TheSpeedX {
Log.d(TAG, "Setting proxy: " + proxy.split(":")[0] + ":" + proxy.split(":")[1]);
if (RandomProxy.showProxyResult) {
AndroidUtils.sendToast(getString("proxy_setup_success") + " " + proxy);
AndroidUtils.sendToast(AndroidUtils.getString(R.string.proxy_setup_success) + " " + proxy);
RandomProxy.showProxyResult = false;
}
@@ -4,10 +4,11 @@ import static ru.vtosters.lite.proxy.GithubList.getProxy;
import static ru.vtosters.lite.proxy.ProxyUtils.forceProxyApplying;
import static ru.vtosters.lite.proxy.ProxyUtils.hasProxy;
import static ru.vtosters.lite.utils.AndroidUtils.edit;
import static ru.vtosters.lite.utils.AndroidUtils.getString;
import android.util.Log;
import com.vtosters.lite.R;
import java.io.IOException;
import java.util.ArrayList;
@@ -35,7 +36,7 @@ public class Jetkai {
Log.d(TAG, "Setting proxy: " + proxy.split(":")[0] + ":" + proxy.split(":")[1]);
if (RandomProxy.showProxyResult) {
AndroidUtils.sendToast(getString("proxy_setup_success") + " " + proxy);
AndroidUtils.sendToast(AndroidUtils.getString(R.string.proxy_setup_success) + " " + proxy);
RandomProxy.showProxyResult = false;
}
@@ -4,10 +4,11 @@ import static ru.vtosters.lite.proxy.GithubList.getProxy;
import static ru.vtosters.lite.proxy.ProxyUtils.forceProxyApplying;
import static ru.vtosters.lite.proxy.ProxyUtils.hasProxy;
import static ru.vtosters.lite.utils.AndroidUtils.edit;
import static ru.vtosters.lite.utils.AndroidUtils.getString;
import android.util.Log;
import com.vtosters.lite.R;
import java.io.IOException;
import java.util.ArrayList;
@@ -35,7 +36,7 @@ public class Roosterkid {
Log.d(TAG, "Setting proxy: " + proxy.split(":")[0] + ":" + proxy.split(":")[1]);
if (RandomProxy.showProxyResult) {
AndroidUtils.sendToast(getString("proxy_setup_success") + " " + proxy);
AndroidUtils.sendToast(AndroidUtils.getString(R.string.proxy_setup_success) + " " + proxy);
RandomProxy.showProxyResult = false;
}
@@ -4,10 +4,11 @@ import static ru.vtosters.lite.proxy.GithubList.getProxy;
import static ru.vtosters.lite.proxy.ProxyUtils.forceProxyApplying;
import static ru.vtosters.lite.proxy.ProxyUtils.hasProxy;
import static ru.vtosters.lite.utils.AndroidUtils.edit;
import static ru.vtosters.lite.utils.AndroidUtils.getString;
import android.util.Log;
import com.vtosters.lite.R;
import java.io.IOException;
import java.util.ArrayList;
@@ -35,7 +36,7 @@ public class Hookzof {
Log.d(TAG, "Setting proxy: " + proxy.split(":")[0] + ":" + proxy.split(":")[1]);
if (RandomProxy.showProxyResult) {
AndroidUtils.sendToast(getString("proxy_setup_success") + " " + proxy);
AndroidUtils.sendToast(AndroidUtils.getString(R.string.proxy_setup_success) + " " + proxy);
RandomProxy.showProxyResult = false;
}
@@ -4,10 +4,11 @@ import static ru.vtosters.lite.proxy.GithubList.getProxy;
import static ru.vtosters.lite.proxy.ProxyUtils.forceProxyApplying;
import static ru.vtosters.lite.proxy.ProxyUtils.hasProxy;
import static ru.vtosters.lite.utils.AndroidUtils.edit;
import static ru.vtosters.lite.utils.AndroidUtils.getString;
import android.util.Log;
import com.vtosters.lite.R;
import java.io.IOException;
import java.util.ArrayList;
@@ -35,7 +36,7 @@ public class Jetkai {
Log.d(TAG, "Setting proxy: " + proxy.split(":")[0] + ":" + proxy.split(":")[1]);
if (RandomProxy.showProxyResult) {
AndroidUtils.sendToast(getString("proxy_setup_success") + " " + proxy);
AndroidUtils.sendToast(AndroidUtils.getString(R.string.proxy_setup_success) + " " + proxy);
RandomProxy.showProxyResult = false;
}
@@ -4,10 +4,11 @@ import static ru.vtosters.lite.proxy.GithubList.getProxy;
import static ru.vtosters.lite.proxy.ProxyUtils.forceProxyApplying;
import static ru.vtosters.lite.proxy.ProxyUtils.hasProxy;
import static ru.vtosters.lite.utils.AndroidUtils.edit;
import static ru.vtosters.lite.utils.AndroidUtils.getString;
import android.util.Log;
import com.vtosters.lite.R;
import java.io.IOException;
import java.util.ArrayList;
@@ -35,7 +36,7 @@ public class Roosterkid {
Log.d(TAG, "Setting proxy: " + proxy.split(":")[0] + ":" + proxy.split(":")[1]);
if (RandomProxy.showProxyResult) {
AndroidUtils.sendToast(getString("proxy_setup_success") + " " + proxy);
AndroidUtils.sendToast(AndroidUtils.getString(R.string.proxy_setup_success) + " " + proxy);
RandomProxy.showProxyResult = false;
}
@@ -4,10 +4,11 @@ import static ru.vtosters.lite.proxy.GithubList.getProxy;
import static ru.vtosters.lite.proxy.ProxyUtils.forceProxyApplying;
import static ru.vtosters.lite.proxy.ProxyUtils.hasProxy;
import static ru.vtosters.lite.utils.AndroidUtils.edit;
import static ru.vtosters.lite.utils.AndroidUtils.getString;
import android.util.Log;
import com.vtosters.lite.R;
import java.io.IOException;
import java.util.ArrayList;
@@ -35,7 +36,7 @@ public class TheSpeedX {
Log.d(TAG, "Setting proxy: " + proxy.split(":")[0] + ":" + proxy.split(":")[1]);
if (RandomProxy.showProxyResult) {
AndroidUtils.sendToast(getString("proxy_setup_success") + " " + proxy);
AndroidUtils.sendToast(AndroidUtils.getString(R.string.proxy_setup_success) + " " + proxy);
RandomProxy.showProxyResult = false;
}
@@ -14,6 +14,8 @@ import android.util.Log;
import androidx.annotation.Nullable;
import com.vtosters.lite.R;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -46,12 +48,12 @@ public class LogWriterService extends Service {
writeLog(intent, file);
} catch (Exception e) {
e.printStackTrace();
makeText(getGlobalContext(), AndroidUtils.getString("logfile_error"), LENGTH_SHORT).show();
makeText(getGlobalContext(), AndroidUtils.getString(R.string.logfile_error), LENGTH_SHORT).show();
}
var notificationId = intent.getIntExtra("notificationId", 1);
var manager = (NotificationManager) getGlobalContext().getSystemService(Context.NOTIFICATION_SERVICE);
manager.cancel(notificationId);
makeText(getGlobalContext(), AndroidUtils.getString("logfile_saved"), LENGTH_SHORT).show();
makeText(getGlobalContext(), AndroidUtils.getString(R.string.logfile_saved), LENGTH_SHORT).show();
stopSelf();
return START_NOT_STICKY;
@@ -0,0 +1,39 @@
package ru.vtosters.lite.ssfs;
import android.util.Log;
import okhttp3.Request;
import org.json.JSONException;
import org.json.JSONObject;
import ru.vtosters.lite.di.singleton.VtOkHttpClient;
import java.io.IOException;
public class Handler {
private static final String TAG = "SSFSHandler";
public static JSONObject getBanner(int id) {
Request request = new Request.a()
.b(Utils.getDomain() + "/api/getChatBanner" + "?user_id=" + id)
.a();
try {
return new JSONObject(VtOkHttpClient.getInstance().a(request).execute().a().g()).getJSONObject("response");
} catch (JSONException | IOException e) {
Log.d(TAG, e.getMessage());
return null;
}
}
public static String getDescription(int id) {
Request request = new Request.a()
.b(Utils.getDomain() + "/api/getServiceDescription" + "?user_id=" + id)
.a();
try {
return new JSONObject(VtOkHttpClient.getInstance().a(request).execute().a().g()).getString("response");
} catch (JSONException | IOException e) {
Log.d(TAG, e.getMessage());
return null;
}
}
}
@@ -0,0 +1,88 @@
package ru.vtosters.lite.ssfs;
import android.util.Log;
import com.vk.core.dialogs.alert.VkAlertDialog;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import com.vtosters.lite.R;
import ru.vtosters.lite.utils.AndroidUtils;
import ru.vtosters.lite.utils.LifecycleUtils;
import ru.vtosters.lite.utils.Preferences;
import static ru.vtosters.lite.utils.Preferences.getBoolValue;
public class ImBanner {
private static final String TAG = "ImBanner";
public static JSONObject convBar(JSONObject orig) throws JSONException {
var user_id = orig.getJSONObject("peer").getInt("id");
if (!UsersList.hasBanner(user_id) || user_id == 0) {
if (getBoolValue("convBarRecomm", false)) {
return null;
} else {
return orig.optJSONObject("conversation_bar");
}
}
try {
var jsonBanner = Handler.getBanner(user_id);
var pic = jsonBanner.optString("picture");
var text = jsonBanner.getString("text");
var link = jsonBanner.optString("link");
var link_text = jsonBanner.optString("link_text");
var hasIcon = !pic.isEmpty();
var hasButton = !link.isEmpty();
var isPicture = pic.endsWith(".png") || pic.endsWith(".jpg") || pic.endsWith(".jpeg") || pic.endsWith(".webp");
var json = new JSONObject();
var buttonsJson = new JSONObject();
buttonsJson.put("layout", "tertiary");
buttonsJson.put("text", link_text);
buttonsJson.put("type", "link");
buttonsJson.put("link", link);
var buttons = new JSONArray(); // max 3 buttons in array
if (hasButton) buttons.put(buttonsJson);
json.put("name", "group_admin_welcome");
json.put("text", text);
json.put("buttons", buttons);
if (hasIcon && isPicture) {
json.put("icon", pic);
}
return json;
} catch (JSONException e) {
Log.d(TAG, e.getMessage());
return null;
}
}
public static boolean showAlert() {
var context = LifecycleUtils.getCurrentActivity();
var bool = Preferences.getBoolValue("linkalert", false);
if (!bool) {
new VkAlertDialog.Builder(context)
.setTitle(R.string.warning)
.setMessage(AndroidUtils.getString("custom_links_warning"))
.setCancelable(false)
.setPositiveButton(R.string.continue_, (dialogInterface, i) -> {
Preferences.preferences.edit().putBoolean("linkalert", true).commit();
})
.setNeutralButton(R.string.cancel, (dialogInterface, i) -> {
dialogInterface.cancel();
})
.show();
}
return bool;
}
}

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