From ec6ba52b586254dec3381a8dd541632f96f49676 Mon Sep 17 00:00:00 2001 From: rhunk <101876869+rhunk@users.noreply.github.com> Date: Thu, 27 Jul 2023 17:12:33 +0200 Subject: [PATCH 01/81] core subproject --- app/build.gradle.kts | 28 ++--------- .../rhunk/snapenhance/manager/MainActivity.kt | 10 ++++ build.gradle.kts | 3 ++ core/.gitignore | 16 +++++++ core/build.gradle.kts | 44 ++++++++++++++++++ .../libs/LSPosed-api-1.0-SNAPSHOT-javadoc.jar | Bin .../libs/LSPosed-api-1.0-SNAPSHOT-sources.jar | Bin .../libs/LSPosed-api-1.0-SNAPSHOT.jar | Bin .../snapenhance/bridge/BridgeInterface.aidl | 0 .../snapenhance/bridge/DownloadCallback.aidl | 0 {app => core}/src/main/assets/lang/ar_SA.json | 0 {app => core}/src/main/assets/lang/en_US.json | 0 {app => core}/src/main/assets/lang/fr_FR.json | 0 {app => core}/src/main/assets/lang/hi_IN.json | 0 .../src/main/assets/web/export_template.html | 0 .../src/main/assets/web/rawinflate.js | 0 {app => core}/src/main/assets/xposed_init | 0 .../kotlin/me/rhunk/snapenhance/Constants.kt | 0 .../kotlin/me/rhunk/snapenhance/Logger.kt | 1 + .../kotlin/me/rhunk/snapenhance/ModContext.kt | 0 .../me/rhunk/snapenhance/SharedContext.kt | 0 .../me/rhunk/snapenhance/SnapEnhance.kt | 3 +- .../me/rhunk/snapenhance/XposedLoader.kt | 0 .../snapenhance/action/AbstractAction.kt | 0 .../action/impl/CheckForUpdates.kt | 0 .../snapenhance/action/impl/CleanCache.kt | 0 .../action/impl/ClearMessageLogger.kt | 0 .../action/impl/ExportChatMessages.kt | 0 .../rhunk/snapenhance/action/impl/OpenMap.kt | 4 +- .../action/impl/RefreshMappings.kt | 0 .../rhunk/snapenhance/bridge/BridgeClient.kt | 6 +-- .../rhunk/snapenhance/bridge/BridgeService.kt | 0 .../snapenhance/bridge/ForceStartActivity.kt | 0 .../bridge/types/BridgeFileType.kt | 0 .../bridge/wrapper/ConfigWrapper.kt | 0 .../bridge/wrapper/MessageLoggerWrapper.kt | 0 .../bridge/wrapper/TranslationWrapper.kt | 0 .../snapenhance/config/ConfigAccessor.kt | 0 .../snapenhance/config/ConfigCategory.kt | 0 .../snapenhance/config/ConfigProperty.kt | 0 .../rhunk/snapenhance/config/ConfigValue.kt | 0 .../config/impl/ConfigIntegerValue.kt | 0 .../config/impl/ConfigStateListValue.kt | 0 .../config/impl/ConfigStateSelection.kt | 0 .../config/impl/ConfigStateValue.kt | 0 .../config/impl/ConfigStringValue.kt | 0 .../me/rhunk/snapenhance/data/FileType.kt | 0 .../me/rhunk/snapenhance/data/LocalePair.kt | 0 .../rhunk/snapenhance/data/MessageSender.kt | 0 .../rhunk/snapenhance/data/SnapClassCache.kt | 0 .../me/rhunk/snapenhance/data/SnapEnums.kt | 0 .../data/wrapper/AbstractWrapper.kt | 0 .../data/wrapper/impl/FriendActionButton.kt | 0 .../snapenhance/data/wrapper/impl/Message.kt | 0 .../data/wrapper/impl/MessageContent.kt | 0 .../data/wrapper/impl/MessageDescriptor.kt | 0 .../data/wrapper/impl/MessageDestinations.kt | 0 .../data/wrapper/impl/MessageMetadata.kt | 0 .../snapenhance/data/wrapper/impl/ScSize.kt | 0 .../snapenhance/data/wrapper/impl/SnapUUID.kt | 0 .../data/wrapper/impl/UserIdToReaction.kt | 0 .../wrapper/impl/media/EncryptionWrapper.kt | 0 .../data/wrapper/impl/media/MediaInfo.kt | 0 .../media/dash/LongformVideoPlaylistItem.kt | 0 .../wrapper/impl/media/dash/SnapChapter.kt | 0 .../impl/media/dash/SnapPlaylistItem.kt | 0 .../data/wrapper/impl/media/opera/Layer.kt | 0 .../impl/media/opera/LayerController.kt | 0 .../data/wrapper/impl/media/opera/ParamMap.kt | 0 .../snapenhance/database/DatabaseAccess.kt | 0 .../snapenhance/database/DatabaseObject.kt | 0 .../database/objects/ConversationMessage.kt | 0 .../database/objects/FriendFeedInfo.kt | 0 .../database/objects/FriendInfo.kt | 0 .../database/objects/StoryEntry.kt | 0 .../database/objects/UserConversationLink.kt | 0 .../download/DownloadManagerClient.kt | 0 .../snapenhance/download/DownloadProcessor.kt | 0 .../download/DownloadTaskManager.kt | 0 .../download/data/DownloadMetadata.kt | 0 .../download/data/DownloadRequest.kt | 0 .../download/data/MediaEncryptionKeyPair.kt | 0 .../download/data/PendingDownload.kt | 0 .../download/data/SplitMediaAssetType.kt | 0 .../download/enums/DownloadMediaType.kt | 0 .../download/enums/DownloadStage.kt | 0 .../me/rhunk/snapenhance/event/EventBus.kt | 0 .../me/rhunk/snapenhance/event/Events.kt | 0 .../snapenhance/features/BridgeFileFeature.kt | 0 .../me/rhunk/snapenhance/features/Feature.kt | 0 .../snapenhance/features/FeatureLoadParams.kt | 0 .../snapenhance/features/impl/AutoUpdater.kt | 2 +- .../features/impl/ConfigEnumKeys.kt | 0 .../snapenhance/features/impl/Messaging.kt | 0 .../impl/downloader/AntiAutoDownload.kt | 0 .../impl/downloader/MediaDownloader.kt | 0 .../impl/experiments/AmoledDarkMode.kt | 0 .../features/impl/experiments/AppPasscode.kt | 0 .../impl/experiments/DeviceSpooferHook.kt | 0 .../impl/experiments/InfiniteStoryBoost.kt | 0 .../impl/experiments/MeoPasscodeBypass.kt | 0 .../impl/experiments/UnlimitedMultiSnap.kt | 0 .../features/impl/privacy/DisableMetrics.kt | 0 .../impl/privacy/PreventMessageSending.kt | 0 .../impl/spying/AnonymousStoryViewing.kt | 0 .../features/impl/spying/MessageLogger.kt | 0 .../impl/spying/PreventReadReceipts.kt | 0 .../features/impl/spying/StealthMode.kt | 0 .../features/impl/tweaks/AntiAutoSave.kt | 0 .../features/impl/tweaks/AutoSave.kt | 0 .../features/impl/tweaks/CameraTweaks.kt | 0 .../tweaks/DisableVideoLengthRestriction.kt | 0 .../impl/tweaks/GalleryMediaSendOverride.kt | 0 .../impl/tweaks/GooglePlayServicesDialogs.kt | 0 .../features/impl/tweaks/LocationSpoofer.kt | 0 .../impl/tweaks/MediaQualityLevelOverride.kt | 0 .../features/impl/tweaks/Notifications.kt | 0 .../features/impl/tweaks/SnapchatPlus.kt | 0 .../impl/tweaks/UnlimitedSnapViewTime.kt | 0 .../features/impl/ui/PinConversations.kt | 0 .../features/impl/ui/StartupPageOverride.kt | 0 .../snapenhance/features/impl/ui/UITweaks.kt | 0 .../me/rhunk/snapenhance/hook/HookAdapter.kt | 0 .../me/rhunk/snapenhance/hook/HookStage.kt | 0 .../me/rhunk/snapenhance/hook/Hooker.kt | 0 .../me/rhunk/snapenhance/manager/Manager.kt | 0 .../snapenhance/manager/impl/ActionManager.kt | 2 +- .../snapenhance/manager/impl/ConfigManager.kt | 0 .../manager/impl/FeatureManager.kt | 0 .../manager/impl/MappingManager.kt | 0 .../me/rhunk/snapenhance/ui/ItemHelper.kt | 2 +- .../snapenhance/ui/ViewAppearanceHelper.kt | 0 .../snapenhance/ui/config/ConfigActivity.kt | 4 +- .../download/DebugSettingsLayoutInflater.kt | 2 +- .../ui/download/DownloadListAdapter.kt | 2 +- .../ui/download/DownloadManagerActivity.kt | 4 +- .../snapenhance/ui/download/MediaFilter.kt | 0 .../rhunk/snapenhance/ui/map/MapActivity.kt | 2 +- .../rhunk/snapenhance/ui/menu/AbstractMenu.kt | 0 .../ui/menu/impl/ChatActionMenu.kt | 0 .../ui/menu/impl/FriendFeedInfoMenu.kt | 0 .../ui/menu/impl/MenuViewInjector.kt | 0 .../ui/menu/impl/OperaContextActionMenu.kt | 0 .../ui/menu/impl/SettingsGearInjector.kt | 4 +- .../snapenhance/ui/menu/impl/SettingsMenu.kt | 0 .../ui/spoof/DeviceSpooferActivity.kt | 2 +- .../util/ActivityResultCallback.kt | 0 .../util/AndroidCompatExtensions.kt | 0 .../rhunk/snapenhance/util/CallbackBuilder.kt | 0 .../snapenhance/util/ReflectionHelper.kt | 0 .../snapenhance/util/SQLiteDatabaseHelper.kt | 0 .../snapenhance/util/XposedHelperMacros.kt | 0 .../util/download/DownloadServer.kt | 0 .../util/download/RemoteMediaResolver.kt | 0 .../util/export/MessageExporter.kt | 4 +- .../snapenhance/util/protobuf/ProtoEditor.kt | 0 .../snapenhance/util/protobuf/ProtoReader.kt | 0 .../snapenhance/util/protobuf/ProtoWriter.kt | 0 .../snapenhance/util/snap/BitmojiSelfie.kt | 0 .../snapenhance/util/snap/EncryptionHelper.kt | 0 .../util/snap/MediaDownloaderHelper.kt | 0 .../snapenhance/util/snap/PreviewUtils.kt | 0 .../res/drawable/action_button_cancel.xml | 0 .../res/drawable/action_button_success.xml | 0 .../src/main/res/drawable/back_arrow.xml | 0 .../src/main/res/drawable/bitmoji_blank.xml | 0 .../main/res/drawable/debug_settings_icon.xml | 0 .../download_manager_item_background.xml | 0 .../src/main/res/drawable/settings_icon.xml | 0 .../src/main/res/font/avenir_next_bold.ttf | Bin .../src/main/res/font/avenir_next_medium.ttf | Bin .../res/layout/activity_default_header.xml | 0 .../src/main/res/layout/config_activity.xml | 0 .../res/layout/config_activity_debug_item.xml | 0 .../main/res/layout/config_activity_item.xml | 0 .../main/res/layout/debug_setting_item.xml | 0 .../main/res/layout/debug_settings_page.xml | 0 .../res/layout/device_spoofer_activity.xml | 0 .../res/layout/download_manager_activity.xml | 0 .../main/res/layout/download_manager_item.xml | 0 {app => core}/src/main/res/layout/map.xml | 0 .../res/layout/precise_location_dialog.xml | 0 .../res/mipmap-anydpi-v26/launcher_icon.xml | 0 .../mipmap-anydpi-v26/launcher_icon_round.xml | 0 .../mipmap-hdpi/launcher_icon_foreground.png | Bin .../res/mipmap-hdpi/launcher_icon_round.png | Bin .../mipmap-mdpi/launcher_icon_foreground.png | Bin .../res/mipmap-mdpi/launcher_icon_round.png | Bin .../mipmap-xhdpi/launcher_icon_foreground.png | Bin .../res/mipmap-xhdpi/launcher_icon_round.png | Bin .../launcher_icon_foreground.png | Bin .../res/mipmap-xxhdpi/launcher_icon_round.png | Bin .../launcher_icon_foreground.png | Bin .../mipmap-xxxhdpi/launcher_icon_round.png | Bin {app => core}/src/main/res/values/arrays.xml | 0 {app => core}/src/main/res/values/colors.xml | 0 {app => core}/src/main/res/values/dimens.xml | 0 .../res/values/launcher_icon_background.xml | 0 {app => core}/src/main/res/values/strings.xml | 0 {app => core}/src/main/res/values/themes.xml | 0 gradle/libs.versions.toml | 2 + settings.gradle.kts | 2 + 202 files changed, 103 insertions(+), 46 deletions(-) create mode 100644 app/src/main/kotlin/me/rhunk/snapenhance/manager/MainActivity.kt create mode 100644 core/.gitignore create mode 100644 core/build.gradle.kts rename {app => core}/libs/LSPosed-api-1.0-SNAPSHOT-javadoc.jar (100%) rename {app => core}/libs/LSPosed-api-1.0-SNAPSHOT-sources.jar (100%) rename {app => core}/libs/LSPosed-api-1.0-SNAPSHOT.jar (100%) rename {app => core}/src/main/aidl/me/rhunk/snapenhance/bridge/BridgeInterface.aidl (100%) rename {app => core}/src/main/aidl/me/rhunk/snapenhance/bridge/DownloadCallback.aidl (100%) rename {app => core}/src/main/assets/lang/ar_SA.json (100%) rename {app => core}/src/main/assets/lang/en_US.json (100%) rename {app => core}/src/main/assets/lang/fr_FR.json (100%) rename {app => core}/src/main/assets/lang/hi_IN.json (100%) rename {app => core}/src/main/assets/web/export_template.html (100%) rename {app => core}/src/main/assets/web/rawinflate.js (100%) rename {app => core}/src/main/assets/xposed_init (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/Constants.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/Logger.kt (95%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/ModContext.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/SharedContext.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/SnapEnhance.kt (97%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/XposedLoader.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/action/AbstractAction.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/action/impl/CheckForUpdates.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/action/impl/CleanCache.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/action/impl/ClearMessageLogger.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/action/impl/ExportChatMessages.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/action/impl/OpenMap.kt (84%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/action/impl/RefreshMappings.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/bridge/BridgeClient.kt (94%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/bridge/BridgeService.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/bridge/ForceStartActivity.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/bridge/types/BridgeFileType.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/ConfigWrapper.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/MessageLoggerWrapper.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/TranslationWrapper.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/config/ConfigAccessor.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/config/ConfigCategory.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/config/ConfigProperty.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/config/ConfigValue.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigIntegerValue.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigStateListValue.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigStateSelection.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigStateValue.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigStringValue.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/data/FileType.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/data/LocalePair.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/data/MessageSender.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/data/SnapClassCache.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/data/SnapEnums.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/AbstractWrapper.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/FriendActionButton.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/Message.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/MessageContent.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/MessageDescriptor.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/MessageDestinations.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/MessageMetadata.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/ScSize.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/SnapUUID.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/UserIdToReaction.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/media/EncryptionWrapper.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/media/MediaInfo.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/media/dash/LongformVideoPlaylistItem.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/media/dash/SnapChapter.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/media/dash/SnapPlaylistItem.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/media/opera/Layer.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/media/opera/LayerController.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/media/opera/ParamMap.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/database/DatabaseAccess.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/database/DatabaseObject.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/database/objects/ConversationMessage.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/database/objects/FriendFeedInfo.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/database/objects/FriendInfo.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/database/objects/StoryEntry.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/database/objects/UserConversationLink.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/download/DownloadManagerClient.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/download/DownloadProcessor.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/download/DownloadTaskManager.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/download/data/DownloadMetadata.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/download/data/DownloadRequest.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/download/data/MediaEncryptionKeyPair.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/download/data/PendingDownload.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/download/data/SplitMediaAssetType.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/download/enums/DownloadMediaType.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/download/enums/DownloadStage.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/event/EventBus.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/event/Events.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/features/BridgeFileFeature.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/features/Feature.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/features/FeatureLoadParams.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/features/impl/AutoUpdater.kt (99%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/features/impl/ConfigEnumKeys.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/features/impl/Messaging.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/features/impl/downloader/AntiAutoDownload.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/features/impl/downloader/MediaDownloader.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/AmoledDarkMode.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/AppPasscode.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/DeviceSpooferHook.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/InfiniteStoryBoost.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/MeoPasscodeBypass.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/UnlimitedMultiSnap.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/features/impl/privacy/DisableMetrics.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/features/impl/privacy/PreventMessageSending.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/AnonymousStoryViewing.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/MessageLogger.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/PreventReadReceipts.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/StealthMode.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/AntiAutoSave.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/AutoSave.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/CameraTweaks.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/DisableVideoLengthRestriction.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/GalleryMediaSendOverride.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/GooglePlayServicesDialogs.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/LocationSpoofer.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/MediaQualityLevelOverride.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/Notifications.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/SnapchatPlus.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/UnlimitedSnapViewTime.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/PinConversations.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/StartupPageOverride.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/UITweaks.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/hook/HookAdapter.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/hook/HookStage.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/hook/Hooker.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/manager/Manager.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/manager/impl/ActionManager.kt (96%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/manager/impl/ConfigManager.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/manager/impl/FeatureManager.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/manager/impl/MappingManager.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/ui/ItemHelper.kt (98%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/ui/ViewAppearanceHelper.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/ui/config/ConfigActivity.kt (99%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/ui/download/DebugSettingsLayoutInflater.kt (99%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadListAdapter.kt (99%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadManagerActivity.kt (99%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/ui/download/MediaFilter.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/ui/map/MapActivity.kt (99%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/ui/menu/AbstractMenu.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/ChatActionMenu.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/FriendFeedInfoMenu.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/MenuViewInjector.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/OperaContextActionMenu.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/SettingsGearInjector.kt (95%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/SettingsMenu.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/ui/spoof/DeviceSpooferActivity.kt (99%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/util/ActivityResultCallback.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/util/AndroidCompatExtensions.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/util/CallbackBuilder.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/util/ReflectionHelper.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/util/SQLiteDatabaseHelper.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/util/XposedHelperMacros.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/util/download/DownloadServer.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/util/download/RemoteMediaResolver.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/util/export/MessageExporter.kt (99%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/util/protobuf/ProtoEditor.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/util/protobuf/ProtoReader.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/util/protobuf/ProtoWriter.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/util/snap/BitmojiSelfie.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/util/snap/EncryptionHelper.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/util/snap/MediaDownloaderHelper.kt (100%) rename {app => core}/src/main/kotlin/me/rhunk/snapenhance/util/snap/PreviewUtils.kt (100%) rename {app => core}/src/main/res/drawable/action_button_cancel.xml (100%) rename {app => core}/src/main/res/drawable/action_button_success.xml (100%) rename {app => core}/src/main/res/drawable/back_arrow.xml (100%) rename {app => core}/src/main/res/drawable/bitmoji_blank.xml (100%) rename {app => core}/src/main/res/drawable/debug_settings_icon.xml (100%) rename {app => core}/src/main/res/drawable/download_manager_item_background.xml (100%) rename {app => core}/src/main/res/drawable/settings_icon.xml (100%) rename {app => core}/src/main/res/font/avenir_next_bold.ttf (100%) rename {app => core}/src/main/res/font/avenir_next_medium.ttf (100%) rename {app => core}/src/main/res/layout/activity_default_header.xml (100%) rename {app => core}/src/main/res/layout/config_activity.xml (100%) rename {app => core}/src/main/res/layout/config_activity_debug_item.xml (100%) rename {app => core}/src/main/res/layout/config_activity_item.xml (100%) rename {app => core}/src/main/res/layout/debug_setting_item.xml (100%) rename {app => core}/src/main/res/layout/debug_settings_page.xml (100%) rename {app => core}/src/main/res/layout/device_spoofer_activity.xml (100%) rename {app => core}/src/main/res/layout/download_manager_activity.xml (100%) rename {app => core}/src/main/res/layout/download_manager_item.xml (100%) rename {app => core}/src/main/res/layout/map.xml (100%) rename {app => core}/src/main/res/layout/precise_location_dialog.xml (100%) rename {app => core}/src/main/res/mipmap-anydpi-v26/launcher_icon.xml (100%) rename {app => core}/src/main/res/mipmap-anydpi-v26/launcher_icon_round.xml (100%) rename {app => core}/src/main/res/mipmap-hdpi/launcher_icon_foreground.png (100%) rename {app => core}/src/main/res/mipmap-hdpi/launcher_icon_round.png (100%) rename {app => core}/src/main/res/mipmap-mdpi/launcher_icon_foreground.png (100%) rename {app => core}/src/main/res/mipmap-mdpi/launcher_icon_round.png (100%) rename {app => core}/src/main/res/mipmap-xhdpi/launcher_icon_foreground.png (100%) rename {app => core}/src/main/res/mipmap-xhdpi/launcher_icon_round.png (100%) rename {app => core}/src/main/res/mipmap-xxhdpi/launcher_icon_foreground.png (100%) rename {app => core}/src/main/res/mipmap-xxhdpi/launcher_icon_round.png (100%) rename {app => core}/src/main/res/mipmap-xxxhdpi/launcher_icon_foreground.png (100%) rename {app => core}/src/main/res/mipmap-xxxhdpi/launcher_icon_round.png (100%) rename {app => core}/src/main/res/values/arrays.xml (100%) rename {app => core}/src/main/res/values/colors.xml (100%) rename {app => core}/src/main/res/values/dimens.xml (100%) rename {app => core}/src/main/res/values/launcher_icon_background.xml (100%) rename {app => core}/src/main/res/values/strings.xml (100%) rename {app => core}/src/main/res/values/themes.xml (100%) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 56ccc7e16..cd4cb3471 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -6,9 +6,6 @@ plugins { alias(libs.plugins.kotlinAndroid) } -val appVersionName = "1.1.0" -val appVersionCode = 7 - android { namespace = "me.rhunk.snapenhance" compileSdk = 33 @@ -22,9 +19,6 @@ android { minSdk = 28 //noinspection OldTargetApi targetSdk = 33 - - versionCode = appVersionCode - versionName = appVersionName multiDexEnabled = true } @@ -66,7 +60,7 @@ android { applicationVariants.all { outputs.map { it as BaseVariantOutputImpl }.forEach { variant -> - variant.outputFileName = "app-${appVersionName}-${variant.name}.apk" + variant.outputFileName = "app-${rootProject.ext["appVersionName"]}-${variant.name}.apk" } } @@ -81,24 +75,8 @@ android { } dependencies { - compileOnly(files("libs/LSPosed-api-1.0-SNAPSHOT.jar")) - implementation(libs.coroutines) - implementation(libs.kotlin.reflect) - implementation(libs.recyclerview) - implementation(libs.gson) - implementation(libs.ffmpeg.kit) - implementation(libs.osmdroid.android) - implementation(libs.okhttp) - implementation(libs.androidx.documentfile) - - implementation(project(":mapper")) -} - -tasks.register("getVersion") { - doLast { - val versionFile = File("app/build/version.txt") - versionFile.writeText(android.defaultConfig.versionName.toString()) - } + implementation(project(":core")) + implementation(libs.androidx.activity.ktx) } afterEvaluate { diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/MainActivity.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/MainActivity.kt new file mode 100644 index 000000000..1f284bc10 --- /dev/null +++ b/app/src/main/kotlin/me/rhunk/snapenhance/manager/MainActivity.kt @@ -0,0 +1,10 @@ +package me.rhunk.snapenhance.manager + +import android.os.Bundle +import androidx.activity.ComponentActivity + +class MainActivity : ComponentActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + } +} \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index fd8ca9441..3f0d75e4c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -5,4 +5,7 @@ plugins { alias(libs.plugins.kotlinAndroid) apply false } +rootProject.ext.set("appVersionName", "1.1.0") +rootProject.ext.set("appVersionCode", 7) + true // Needed to make the Suppress annotation work for the plugins block \ No newline at end of file diff --git a/core/.gitignore b/core/.gitignore new file mode 100644 index 000000000..c9db3d0b1 --- /dev/null +++ b/core/.gitignore @@ -0,0 +1,16 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +/.idea/ +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/core/build.gradle.kts b/core/build.gradle.kts new file mode 100644 index 000000000..8de69a2da --- /dev/null +++ b/core/build.gradle.kts @@ -0,0 +1,44 @@ +@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed +plugins { + id("com.android.library") + alias(libs.plugins.kotlinAndroid) +} +android { + namespace = "me.rhunk.snapenhance.core" + compileSdk = 33 + + buildFeatures { + aidl = true + } + + defaultConfig { + minSdk = 28 + buildConfigField("String", "VERSION_NAME", "\"${rootProject.ext["appVersionName"]}\"") + buildConfigField("int", "VERSION_CODE", "${rootProject.ext["appVersionCode"]}") + } + + kotlinOptions { + jvmTarget = "1.8" + } +} + +tasks.register("getVersion") { + doLast { + val versionFile = File("app/build/version.txt") + versionFile.writeText(android.defaultConfig.versionName.toString()) + } +} + +dependencies { + compileOnly(files("libs/LSPosed-api-1.0-SNAPSHOT.jar")) + implementation(libs.coroutines) + implementation(libs.kotlin.reflect) + implementation(libs.recyclerview) + implementation(libs.gson) + implementation(libs.ffmpeg.kit) + implementation(libs.osmdroid.android) + implementation(libs.okhttp) + implementation(libs.androidx.documentfile) + + implementation(project(":mapper")) +} \ No newline at end of file diff --git a/app/libs/LSPosed-api-1.0-SNAPSHOT-javadoc.jar b/core/libs/LSPosed-api-1.0-SNAPSHOT-javadoc.jar similarity index 100% rename from app/libs/LSPosed-api-1.0-SNAPSHOT-javadoc.jar rename to core/libs/LSPosed-api-1.0-SNAPSHOT-javadoc.jar diff --git a/app/libs/LSPosed-api-1.0-SNAPSHOT-sources.jar b/core/libs/LSPosed-api-1.0-SNAPSHOT-sources.jar similarity index 100% rename from app/libs/LSPosed-api-1.0-SNAPSHOT-sources.jar rename to core/libs/LSPosed-api-1.0-SNAPSHOT-sources.jar diff --git a/app/libs/LSPosed-api-1.0-SNAPSHOT.jar b/core/libs/LSPosed-api-1.0-SNAPSHOT.jar similarity index 100% rename from app/libs/LSPosed-api-1.0-SNAPSHOT.jar rename to core/libs/LSPosed-api-1.0-SNAPSHOT.jar diff --git a/app/src/main/aidl/me/rhunk/snapenhance/bridge/BridgeInterface.aidl b/core/src/main/aidl/me/rhunk/snapenhance/bridge/BridgeInterface.aidl similarity index 100% rename from app/src/main/aidl/me/rhunk/snapenhance/bridge/BridgeInterface.aidl rename to core/src/main/aidl/me/rhunk/snapenhance/bridge/BridgeInterface.aidl diff --git a/app/src/main/aidl/me/rhunk/snapenhance/bridge/DownloadCallback.aidl b/core/src/main/aidl/me/rhunk/snapenhance/bridge/DownloadCallback.aidl similarity index 100% rename from app/src/main/aidl/me/rhunk/snapenhance/bridge/DownloadCallback.aidl rename to core/src/main/aidl/me/rhunk/snapenhance/bridge/DownloadCallback.aidl diff --git a/app/src/main/assets/lang/ar_SA.json b/core/src/main/assets/lang/ar_SA.json similarity index 100% rename from app/src/main/assets/lang/ar_SA.json rename to core/src/main/assets/lang/ar_SA.json diff --git a/app/src/main/assets/lang/en_US.json b/core/src/main/assets/lang/en_US.json similarity index 100% rename from app/src/main/assets/lang/en_US.json rename to core/src/main/assets/lang/en_US.json diff --git a/app/src/main/assets/lang/fr_FR.json b/core/src/main/assets/lang/fr_FR.json similarity index 100% rename from app/src/main/assets/lang/fr_FR.json rename to core/src/main/assets/lang/fr_FR.json diff --git a/app/src/main/assets/lang/hi_IN.json b/core/src/main/assets/lang/hi_IN.json similarity index 100% rename from app/src/main/assets/lang/hi_IN.json rename to core/src/main/assets/lang/hi_IN.json diff --git a/app/src/main/assets/web/export_template.html b/core/src/main/assets/web/export_template.html similarity index 100% rename from app/src/main/assets/web/export_template.html rename to core/src/main/assets/web/export_template.html diff --git a/app/src/main/assets/web/rawinflate.js b/core/src/main/assets/web/rawinflate.js similarity index 100% rename from app/src/main/assets/web/rawinflate.js rename to core/src/main/assets/web/rawinflate.js diff --git a/app/src/main/assets/xposed_init b/core/src/main/assets/xposed_init similarity index 100% rename from app/src/main/assets/xposed_init rename to core/src/main/assets/xposed_init diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/Constants.kt b/core/src/main/kotlin/me/rhunk/snapenhance/Constants.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/Constants.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/Constants.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/Logger.kt b/core/src/main/kotlin/me/rhunk/snapenhance/Logger.kt similarity index 95% rename from app/src/main/kotlin/me/rhunk/snapenhance/Logger.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/Logger.kt index 8b7c9c8a2..9fcdf474d 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/Logger.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/Logger.kt @@ -2,6 +2,7 @@ package me.rhunk.snapenhance import android.util.Log import de.robv.android.xposed.XposedBridge +import me.rhunk.snapenhance.core.BuildConfig object Logger { private const val TAG = "SnapEnhance" diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ModContext.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ModContext.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/ModContext.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/ModContext.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/SharedContext.kt b/core/src/main/kotlin/me/rhunk/snapenhance/SharedContext.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/SharedContext.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/SharedContext.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/SnapEnhance.kt b/core/src/main/kotlin/me/rhunk/snapenhance/SnapEnhance.kt similarity index 97% rename from app/src/main/kotlin/me/rhunk/snapenhance/SnapEnhance.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/SnapEnhance.kt index b818d165a..e4fe79a4d 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/SnapEnhance.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/SnapEnhance.kt @@ -7,6 +7,7 @@ import android.content.pm.PackageManager import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext import me.rhunk.snapenhance.bridge.BridgeClient +import me.rhunk.snapenhance.core.BuildConfig import me.rhunk.snapenhance.data.SnapClassCache import me.rhunk.snapenhance.hook.HookStage import me.rhunk.snapenhance.hook.Hooker @@ -34,7 +35,7 @@ class SnapEnhance { //for lspatch builds, we need to check if the service is correctly installed runCatching { - appContext.androidContext.packageManager.getApplicationInfoCompat(BuildConfig.APPLICATION_ID, PackageManager.GET_META_DATA) + appContext.androidContext.packageManager.getApplicationInfoCompat(BuildConfig.LIBRARY_PACKAGE_NAME, PackageManager.GET_META_DATA) }.onFailure { appContext.crash("SnapEnhance bridge service is not installed. Please download stable version from https://github.com/rhunk/SnapEnhance/releases") return@hook diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/XposedLoader.kt b/core/src/main/kotlin/me/rhunk/snapenhance/XposedLoader.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/XposedLoader.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/XposedLoader.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/action/AbstractAction.kt b/core/src/main/kotlin/me/rhunk/snapenhance/action/AbstractAction.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/action/AbstractAction.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/action/AbstractAction.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/action/impl/CheckForUpdates.kt b/core/src/main/kotlin/me/rhunk/snapenhance/action/impl/CheckForUpdates.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/action/impl/CheckForUpdates.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/action/impl/CheckForUpdates.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/action/impl/CleanCache.kt b/core/src/main/kotlin/me/rhunk/snapenhance/action/impl/CleanCache.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/action/impl/CleanCache.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/action/impl/CleanCache.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/action/impl/ClearMessageLogger.kt b/core/src/main/kotlin/me/rhunk/snapenhance/action/impl/ClearMessageLogger.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/action/impl/ClearMessageLogger.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/action/impl/ClearMessageLogger.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/action/impl/ExportChatMessages.kt b/core/src/main/kotlin/me/rhunk/snapenhance/action/impl/ExportChatMessages.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/action/impl/ExportChatMessages.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/action/impl/ExportChatMessages.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/action/impl/OpenMap.kt b/core/src/main/kotlin/me/rhunk/snapenhance/action/impl/OpenMap.kt similarity index 84% rename from app/src/main/kotlin/me/rhunk/snapenhance/action/impl/OpenMap.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/action/impl/OpenMap.kt index 9ba340ca3..0909ce160 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/action/impl/OpenMap.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/action/impl/OpenMap.kt @@ -2,16 +2,16 @@ package me.rhunk.snapenhance.action.impl import android.content.Intent import android.os.Bundle -import me.rhunk.snapenhance.BuildConfig import me.rhunk.snapenhance.action.AbstractAction import me.rhunk.snapenhance.config.ConfigProperty +import me.rhunk.snapenhance.core.BuildConfig import me.rhunk.snapenhance.ui.map.MapActivity class OpenMap: AbstractAction("action.open_map", dependsOnProperty = ConfigProperty.LOCATION_SPOOF) { override fun run() { context.runOnUiThread { val mapActivityIntent = Intent() - mapActivityIntent.setClassName(BuildConfig.APPLICATION_ID, MapActivity::class.java.name) + mapActivityIntent.setClassName(BuildConfig.LIBRARY_PACKAGE_NAME, MapActivity::class.java.name) mapActivityIntent.putExtra("location", Bundle().apply { putDouble("latitude", context.config.string(ConfigProperty.LATITUDE).toDouble()) putDouble("longitude", context.config.string(ConfigProperty.LONGITUDE).toDouble()) diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/action/impl/RefreshMappings.kt b/core/src/main/kotlin/me/rhunk/snapenhance/action/impl/RefreshMappings.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/action/impl/RefreshMappings.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/action/impl/RefreshMappings.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/bridge/BridgeClient.kt b/core/src/main/kotlin/me/rhunk/snapenhance/bridge/BridgeClient.kt similarity index 94% rename from app/src/main/kotlin/me/rhunk/snapenhance/bridge/BridgeClient.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/bridge/BridgeClient.kt index 7885d926a..5e98c81da 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/bridge/BridgeClient.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/bridge/BridgeClient.kt @@ -10,7 +10,7 @@ import android.os.Handler import android.os.HandlerThread import android.os.IBinder import de.robv.android.xposed.XposedHelpers -import me.rhunk.snapenhance.BuildConfig +import me.rhunk.snapenhance.core.BuildConfig import me.rhunk.snapenhance.Logger.xposedLog import me.rhunk.snapenhance.ModContext import me.rhunk.snapenhance.bridge.types.BridgeFileType @@ -32,12 +32,12 @@ class BridgeClient( with(context.androidContext) { //ensure the remote process is running startActivity(Intent() - .setClassName(BuildConfig.APPLICATION_ID, ForceStartActivity::class.java.name) + .setClassName(BuildConfig.LIBRARY_PACKAGE_NAME, ForceStartActivity::class.java.name) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_MULTIPLE_TASK) ) val intent = Intent() - .setClassName(BuildConfig.APPLICATION_ID, BridgeService::class.java.name) + .setClassName(BuildConfig.LIBRARY_PACKAGE_NAME, BridgeService::class.java.name) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { bindService( intent, diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/bridge/BridgeService.kt b/core/src/main/kotlin/me/rhunk/snapenhance/bridge/BridgeService.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/bridge/BridgeService.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/bridge/BridgeService.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/bridge/ForceStartActivity.kt b/core/src/main/kotlin/me/rhunk/snapenhance/bridge/ForceStartActivity.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/bridge/ForceStartActivity.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/bridge/ForceStartActivity.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/bridge/types/BridgeFileType.kt b/core/src/main/kotlin/me/rhunk/snapenhance/bridge/types/BridgeFileType.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/bridge/types/BridgeFileType.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/bridge/types/BridgeFileType.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/ConfigWrapper.kt b/core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/ConfigWrapper.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/ConfigWrapper.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/ConfigWrapper.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/MessageLoggerWrapper.kt b/core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/MessageLoggerWrapper.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/MessageLoggerWrapper.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/MessageLoggerWrapper.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/TranslationWrapper.kt b/core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/TranslationWrapper.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/TranslationWrapper.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/TranslationWrapper.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/config/ConfigAccessor.kt b/core/src/main/kotlin/me/rhunk/snapenhance/config/ConfigAccessor.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/config/ConfigAccessor.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/config/ConfigAccessor.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/config/ConfigCategory.kt b/core/src/main/kotlin/me/rhunk/snapenhance/config/ConfigCategory.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/config/ConfigCategory.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/config/ConfigCategory.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/config/ConfigProperty.kt b/core/src/main/kotlin/me/rhunk/snapenhance/config/ConfigProperty.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/config/ConfigProperty.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/config/ConfigProperty.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/config/ConfigValue.kt b/core/src/main/kotlin/me/rhunk/snapenhance/config/ConfigValue.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/config/ConfigValue.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/config/ConfigValue.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigIntegerValue.kt b/core/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigIntegerValue.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigIntegerValue.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigIntegerValue.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigStateListValue.kt b/core/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigStateListValue.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigStateListValue.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigStateListValue.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigStateSelection.kt b/core/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigStateSelection.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigStateSelection.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigStateSelection.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigStateValue.kt b/core/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigStateValue.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigStateValue.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigStateValue.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigStringValue.kt b/core/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigStringValue.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigStringValue.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigStringValue.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/data/FileType.kt b/core/src/main/kotlin/me/rhunk/snapenhance/data/FileType.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/data/FileType.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/data/FileType.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/data/LocalePair.kt b/core/src/main/kotlin/me/rhunk/snapenhance/data/LocalePair.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/data/LocalePair.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/data/LocalePair.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/data/MessageSender.kt b/core/src/main/kotlin/me/rhunk/snapenhance/data/MessageSender.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/data/MessageSender.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/data/MessageSender.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/data/SnapClassCache.kt b/core/src/main/kotlin/me/rhunk/snapenhance/data/SnapClassCache.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/data/SnapClassCache.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/data/SnapClassCache.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/data/SnapEnums.kt b/core/src/main/kotlin/me/rhunk/snapenhance/data/SnapEnums.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/data/SnapEnums.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/data/SnapEnums.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/AbstractWrapper.kt b/core/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/AbstractWrapper.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/AbstractWrapper.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/AbstractWrapper.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/FriendActionButton.kt b/core/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/FriendActionButton.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/FriendActionButton.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/FriendActionButton.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/Message.kt b/core/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/Message.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/Message.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/Message.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/MessageContent.kt b/core/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/MessageContent.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/MessageContent.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/MessageContent.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/MessageDescriptor.kt b/core/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/MessageDescriptor.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/MessageDescriptor.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/MessageDescriptor.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/MessageDestinations.kt b/core/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/MessageDestinations.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/MessageDestinations.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/MessageDestinations.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/MessageMetadata.kt b/core/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/MessageMetadata.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/MessageMetadata.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/MessageMetadata.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/ScSize.kt b/core/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/ScSize.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/ScSize.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/ScSize.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/SnapUUID.kt b/core/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/SnapUUID.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/SnapUUID.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/SnapUUID.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/UserIdToReaction.kt b/core/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/UserIdToReaction.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/UserIdToReaction.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/UserIdToReaction.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/media/EncryptionWrapper.kt b/core/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/media/EncryptionWrapper.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/media/EncryptionWrapper.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/media/EncryptionWrapper.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/media/MediaInfo.kt b/core/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/media/MediaInfo.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/media/MediaInfo.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/media/MediaInfo.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/media/dash/LongformVideoPlaylistItem.kt b/core/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/media/dash/LongformVideoPlaylistItem.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/media/dash/LongformVideoPlaylistItem.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/media/dash/LongformVideoPlaylistItem.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/media/dash/SnapChapter.kt b/core/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/media/dash/SnapChapter.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/media/dash/SnapChapter.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/media/dash/SnapChapter.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/media/dash/SnapPlaylistItem.kt b/core/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/media/dash/SnapPlaylistItem.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/media/dash/SnapPlaylistItem.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/media/dash/SnapPlaylistItem.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/media/opera/Layer.kt b/core/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/media/opera/Layer.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/media/opera/Layer.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/media/opera/Layer.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/media/opera/LayerController.kt b/core/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/media/opera/LayerController.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/media/opera/LayerController.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/media/opera/LayerController.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/media/opera/ParamMap.kt b/core/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/media/opera/ParamMap.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/media/opera/ParamMap.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/media/opera/ParamMap.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/database/DatabaseAccess.kt b/core/src/main/kotlin/me/rhunk/snapenhance/database/DatabaseAccess.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/database/DatabaseAccess.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/database/DatabaseAccess.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/database/DatabaseObject.kt b/core/src/main/kotlin/me/rhunk/snapenhance/database/DatabaseObject.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/database/DatabaseObject.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/database/DatabaseObject.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/database/objects/ConversationMessage.kt b/core/src/main/kotlin/me/rhunk/snapenhance/database/objects/ConversationMessage.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/database/objects/ConversationMessage.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/database/objects/ConversationMessage.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/database/objects/FriendFeedInfo.kt b/core/src/main/kotlin/me/rhunk/snapenhance/database/objects/FriendFeedInfo.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/database/objects/FriendFeedInfo.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/database/objects/FriendFeedInfo.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/database/objects/FriendInfo.kt b/core/src/main/kotlin/me/rhunk/snapenhance/database/objects/FriendInfo.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/database/objects/FriendInfo.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/database/objects/FriendInfo.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/database/objects/StoryEntry.kt b/core/src/main/kotlin/me/rhunk/snapenhance/database/objects/StoryEntry.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/database/objects/StoryEntry.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/database/objects/StoryEntry.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/database/objects/UserConversationLink.kt b/core/src/main/kotlin/me/rhunk/snapenhance/database/objects/UserConversationLink.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/database/objects/UserConversationLink.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/database/objects/UserConversationLink.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/download/DownloadManagerClient.kt b/core/src/main/kotlin/me/rhunk/snapenhance/download/DownloadManagerClient.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/download/DownloadManagerClient.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/download/DownloadManagerClient.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/download/DownloadProcessor.kt b/core/src/main/kotlin/me/rhunk/snapenhance/download/DownloadProcessor.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/download/DownloadProcessor.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/download/DownloadProcessor.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/download/DownloadTaskManager.kt b/core/src/main/kotlin/me/rhunk/snapenhance/download/DownloadTaskManager.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/download/DownloadTaskManager.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/download/DownloadTaskManager.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/download/data/DownloadMetadata.kt b/core/src/main/kotlin/me/rhunk/snapenhance/download/data/DownloadMetadata.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/download/data/DownloadMetadata.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/download/data/DownloadMetadata.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/download/data/DownloadRequest.kt b/core/src/main/kotlin/me/rhunk/snapenhance/download/data/DownloadRequest.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/download/data/DownloadRequest.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/download/data/DownloadRequest.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/download/data/MediaEncryptionKeyPair.kt b/core/src/main/kotlin/me/rhunk/snapenhance/download/data/MediaEncryptionKeyPair.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/download/data/MediaEncryptionKeyPair.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/download/data/MediaEncryptionKeyPair.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/download/data/PendingDownload.kt b/core/src/main/kotlin/me/rhunk/snapenhance/download/data/PendingDownload.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/download/data/PendingDownload.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/download/data/PendingDownload.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/download/data/SplitMediaAssetType.kt b/core/src/main/kotlin/me/rhunk/snapenhance/download/data/SplitMediaAssetType.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/download/data/SplitMediaAssetType.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/download/data/SplitMediaAssetType.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/download/enums/DownloadMediaType.kt b/core/src/main/kotlin/me/rhunk/snapenhance/download/enums/DownloadMediaType.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/download/enums/DownloadMediaType.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/download/enums/DownloadMediaType.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/download/enums/DownloadStage.kt b/core/src/main/kotlin/me/rhunk/snapenhance/download/enums/DownloadStage.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/download/enums/DownloadStage.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/download/enums/DownloadStage.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/event/EventBus.kt b/core/src/main/kotlin/me/rhunk/snapenhance/event/EventBus.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/event/EventBus.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/event/EventBus.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/event/Events.kt b/core/src/main/kotlin/me/rhunk/snapenhance/event/Events.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/event/Events.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/event/Events.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/features/BridgeFileFeature.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/BridgeFileFeature.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/features/BridgeFileFeature.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/features/BridgeFileFeature.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/features/Feature.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/Feature.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/features/Feature.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/features/Feature.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/features/FeatureLoadParams.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/FeatureLoadParams.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/features/FeatureLoadParams.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/features/FeatureLoadParams.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/AutoUpdater.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/AutoUpdater.kt similarity index 99% rename from app/src/main/kotlin/me/rhunk/snapenhance/features/impl/AutoUpdater.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/features/impl/AutoUpdater.kt index 7e6996e38..c92ac5900 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/AutoUpdater.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/AutoUpdater.kt @@ -9,7 +9,7 @@ import android.content.Intent import android.content.IntentFilter import android.net.Uri import android.os.Environment -import me.rhunk.snapenhance.BuildConfig +import me.rhunk.snapenhance.core.BuildConfig import me.rhunk.snapenhance.Logger import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.features.Feature diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/ConfigEnumKeys.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/ConfigEnumKeys.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/features/impl/ConfigEnumKeys.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/features/impl/ConfigEnumKeys.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/Messaging.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/Messaging.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/features/impl/Messaging.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/features/impl/Messaging.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/downloader/AntiAutoDownload.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/downloader/AntiAutoDownload.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/features/impl/downloader/AntiAutoDownload.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/features/impl/downloader/AntiAutoDownload.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/downloader/MediaDownloader.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/downloader/MediaDownloader.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/features/impl/downloader/MediaDownloader.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/features/impl/downloader/MediaDownloader.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/AmoledDarkMode.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/AmoledDarkMode.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/AmoledDarkMode.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/AmoledDarkMode.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/AppPasscode.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/AppPasscode.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/AppPasscode.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/AppPasscode.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/DeviceSpooferHook.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/DeviceSpooferHook.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/DeviceSpooferHook.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/DeviceSpooferHook.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/InfiniteStoryBoost.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/InfiniteStoryBoost.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/InfiniteStoryBoost.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/InfiniteStoryBoost.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/MeoPasscodeBypass.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/MeoPasscodeBypass.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/MeoPasscodeBypass.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/MeoPasscodeBypass.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/UnlimitedMultiSnap.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/UnlimitedMultiSnap.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/UnlimitedMultiSnap.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/UnlimitedMultiSnap.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/privacy/DisableMetrics.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/privacy/DisableMetrics.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/features/impl/privacy/DisableMetrics.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/features/impl/privacy/DisableMetrics.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/privacy/PreventMessageSending.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/privacy/PreventMessageSending.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/features/impl/privacy/PreventMessageSending.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/features/impl/privacy/PreventMessageSending.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/AnonymousStoryViewing.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/AnonymousStoryViewing.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/AnonymousStoryViewing.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/AnonymousStoryViewing.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/MessageLogger.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/MessageLogger.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/MessageLogger.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/MessageLogger.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/PreventReadReceipts.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/PreventReadReceipts.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/PreventReadReceipts.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/PreventReadReceipts.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/StealthMode.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/StealthMode.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/StealthMode.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/StealthMode.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/AntiAutoSave.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/AntiAutoSave.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/AntiAutoSave.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/AntiAutoSave.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/AutoSave.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/AutoSave.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/AutoSave.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/AutoSave.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/CameraTweaks.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/CameraTweaks.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/CameraTweaks.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/CameraTweaks.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/DisableVideoLengthRestriction.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/DisableVideoLengthRestriction.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/DisableVideoLengthRestriction.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/DisableVideoLengthRestriction.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/GalleryMediaSendOverride.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/GalleryMediaSendOverride.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/GalleryMediaSendOverride.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/GalleryMediaSendOverride.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/GooglePlayServicesDialogs.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/GooglePlayServicesDialogs.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/GooglePlayServicesDialogs.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/GooglePlayServicesDialogs.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/LocationSpoofer.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/LocationSpoofer.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/LocationSpoofer.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/LocationSpoofer.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/MediaQualityLevelOverride.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/MediaQualityLevelOverride.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/MediaQualityLevelOverride.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/MediaQualityLevelOverride.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/Notifications.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/Notifications.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/Notifications.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/Notifications.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/SnapchatPlus.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/SnapchatPlus.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/SnapchatPlus.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/SnapchatPlus.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/UnlimitedSnapViewTime.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/UnlimitedSnapViewTime.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/UnlimitedSnapViewTime.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/UnlimitedSnapViewTime.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/PinConversations.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/PinConversations.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/PinConversations.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/PinConversations.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/StartupPageOverride.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/StartupPageOverride.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/StartupPageOverride.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/StartupPageOverride.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/UITweaks.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/UITweaks.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/UITweaks.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/UITweaks.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/hook/HookAdapter.kt b/core/src/main/kotlin/me/rhunk/snapenhance/hook/HookAdapter.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/hook/HookAdapter.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/hook/HookAdapter.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/hook/HookStage.kt b/core/src/main/kotlin/me/rhunk/snapenhance/hook/HookStage.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/hook/HookStage.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/hook/HookStage.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/hook/Hooker.kt b/core/src/main/kotlin/me/rhunk/snapenhance/hook/Hooker.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/hook/Hooker.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/hook/Hooker.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/Manager.kt b/core/src/main/kotlin/me/rhunk/snapenhance/manager/Manager.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/manager/Manager.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/manager/Manager.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/impl/ActionManager.kt b/core/src/main/kotlin/me/rhunk/snapenhance/manager/impl/ActionManager.kt similarity index 96% rename from app/src/main/kotlin/me/rhunk/snapenhance/manager/impl/ActionManager.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/manager/impl/ActionManager.kt index 0c7ed9ceb..4dfc34158 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/manager/impl/ActionManager.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/manager/impl/ActionManager.kt @@ -1,6 +1,6 @@ package me.rhunk.snapenhance.manager.impl -import me.rhunk.snapenhance.BuildConfig +import me.rhunk.snapenhance.core.BuildConfig import me.rhunk.snapenhance.ModContext import me.rhunk.snapenhance.action.AbstractAction import me.rhunk.snapenhance.action.impl.CheckForUpdates diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/impl/ConfigManager.kt b/core/src/main/kotlin/me/rhunk/snapenhance/manager/impl/ConfigManager.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/manager/impl/ConfigManager.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/manager/impl/ConfigManager.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/impl/FeatureManager.kt b/core/src/main/kotlin/me/rhunk/snapenhance/manager/impl/FeatureManager.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/manager/impl/FeatureManager.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/manager/impl/FeatureManager.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/impl/MappingManager.kt b/core/src/main/kotlin/me/rhunk/snapenhance/manager/impl/MappingManager.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/manager/impl/MappingManager.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/manager/impl/MappingManager.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/ItemHelper.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/ItemHelper.kt similarity index 98% rename from app/src/main/kotlin/me/rhunk/snapenhance/ui/ItemHelper.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/ui/ItemHelper.kt index c78ae4b28..c47437ff5 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/ItemHelper.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/ui/ItemHelper.kt @@ -7,7 +7,7 @@ import android.content.Intent import android.widget.EditText import android.widget.TextView import android.widget.Toast -import me.rhunk.snapenhance.R +import me.rhunk.snapenhance.core.R import me.rhunk.snapenhance.SharedContext import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.util.ActivityResultCallback diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/ViewAppearanceHelper.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/ViewAppearanceHelper.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/ui/ViewAppearanceHelper.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/ui/ViewAppearanceHelper.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/config/ConfigActivity.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/config/ConfigActivity.kt similarity index 99% rename from app/src/main/kotlin/me/rhunk/snapenhance/ui/config/ConfigActivity.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/ui/config/ConfigActivity.kt index 0f6093a63..7a47c87a3 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/config/ConfigActivity.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/ui/config/ConfigActivity.kt @@ -13,8 +13,8 @@ import android.widget.ImageButton import android.widget.Switch import android.widget.TextView import android.widget.Toast -import me.rhunk.snapenhance.BuildConfig -import me.rhunk.snapenhance.R +import me.rhunk.snapenhance.core.BuildConfig +import me.rhunk.snapenhance.core.R import me.rhunk.snapenhance.SharedContext import me.rhunk.snapenhance.config.ConfigCategory import me.rhunk.snapenhance.config.ConfigProperty diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/download/DebugSettingsLayoutInflater.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DebugSettingsLayoutInflater.kt similarity index 99% rename from app/src/main/kotlin/me/rhunk/snapenhance/ui/download/DebugSettingsLayoutInflater.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DebugSettingsLayoutInflater.kt index 404938706..888eb96d9 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/download/DebugSettingsLayoutInflater.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DebugSettingsLayoutInflater.kt @@ -9,7 +9,7 @@ import android.widget.ImageButton import android.widget.ListView import android.widget.TextView import android.widget.Toast -import me.rhunk.snapenhance.R +import me.rhunk.snapenhance.core.R import me.rhunk.snapenhance.SharedContext import me.rhunk.snapenhance.bridge.types.BridgeFileType import me.rhunk.snapenhance.ui.config.ConfigActivity diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadListAdapter.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadListAdapter.kt similarity index 99% rename from app/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadListAdapter.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadListAdapter.kt index e250db9a5..d7351d3e7 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadListAdapter.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadListAdapter.kt @@ -23,7 +23,7 @@ import kotlinx.coroutines.job import kotlinx.coroutines.launch import kotlinx.coroutines.withTimeout import me.rhunk.snapenhance.Logger -import me.rhunk.snapenhance.R +import me.rhunk.snapenhance.core.R import me.rhunk.snapenhance.SharedContext import me.rhunk.snapenhance.data.FileType import me.rhunk.snapenhance.download.data.PendingDownload diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadManagerActivity.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadManagerActivity.kt similarity index 99% rename from app/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadManagerActivity.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadManagerActivity.kt index 2ba923108..1472afd08 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadManagerActivity.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadManagerActivity.kt @@ -15,8 +15,8 @@ import android.widget.ImageButton import android.widget.TextView import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.RecyclerView -import me.rhunk.snapenhance.BuildConfig -import me.rhunk.snapenhance.R +import me.rhunk.snapenhance.core.BuildConfig +import me.rhunk.snapenhance.core.R import me.rhunk.snapenhance.SharedContext import me.rhunk.snapenhance.bridge.wrapper.TranslationWrapper import me.rhunk.snapenhance.download.data.PendingDownload diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/download/MediaFilter.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/MediaFilter.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/ui/download/MediaFilter.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/ui/download/MediaFilter.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/map/MapActivity.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/map/MapActivity.kt similarity index 99% rename from app/src/main/kotlin/me/rhunk/snapenhance/ui/map/MapActivity.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/ui/map/MapActivity.kt index 80beea011..af0a9f9d9 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/map/MapActivity.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/ui/map/MapActivity.kt @@ -8,7 +8,7 @@ import android.os.Bundle import android.view.MotionEvent import android.widget.Button import android.widget.EditText -import me.rhunk.snapenhance.R +import me.rhunk.snapenhance.core.R import org.osmdroid.config.Configuration import org.osmdroid.tileprovider.tilesource.TileSourceFactory import org.osmdroid.util.GeoPoint diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/menu/AbstractMenu.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/AbstractMenu.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/ui/menu/AbstractMenu.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/AbstractMenu.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/ChatActionMenu.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/ChatActionMenu.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/ChatActionMenu.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/ChatActionMenu.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/FriendFeedInfoMenu.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/FriendFeedInfoMenu.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/FriendFeedInfoMenu.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/FriendFeedInfoMenu.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/MenuViewInjector.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/MenuViewInjector.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/MenuViewInjector.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/MenuViewInjector.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/OperaContextActionMenu.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/OperaContextActionMenu.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/OperaContextActionMenu.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/OperaContextActionMenu.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/SettingsGearInjector.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/SettingsGearInjector.kt similarity index 95% rename from app/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/SettingsGearInjector.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/SettingsGearInjector.kt index c96683c20..a904e2c03 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/SettingsGearInjector.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/SettingsGearInjector.kt @@ -6,7 +6,7 @@ import android.view.View import android.view.ViewGroup import android.widget.FrameLayout import android.widget.ImageView -import me.rhunk.snapenhance.BuildConfig +import me.rhunk.snapenhance.core.BuildConfig import me.rhunk.snapenhance.Constants import me.rhunk.snapenhance.ui.config.ConfigActivity import me.rhunk.snapenhance.ui.menu.AbstractMenu @@ -48,7 +48,7 @@ class SettingsGearInjector : AbstractMenu() { setOnClickListener { val intent = Intent().apply { - setClassName(BuildConfig.APPLICATION_ID, ConfigActivity::class.java.name) + setClassName(BuildConfig.LIBRARY_PACKAGE_NAME, ConfigActivity::class.java.name) } intent.putExtra("lspatched", File(context.cacheDir, "lspatch/origin").exists()) context.startActivity(intent) diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/SettingsMenu.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/SettingsMenu.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/SettingsMenu.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/SettingsMenu.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/spoof/DeviceSpooferActivity.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/spoof/DeviceSpooferActivity.kt similarity index 99% rename from app/src/main/kotlin/me/rhunk/snapenhance/ui/spoof/DeviceSpooferActivity.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/ui/spoof/DeviceSpooferActivity.kt index 32cc5db06..c6b61abfa 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/spoof/DeviceSpooferActivity.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/ui/spoof/DeviceSpooferActivity.kt @@ -9,7 +9,7 @@ import android.view.ViewGroup import android.widget.ImageButton import android.widget.Switch import android.widget.TextView -import me.rhunk.snapenhance.R +import me.rhunk.snapenhance.core.R import me.rhunk.snapenhance.SharedContext import me.rhunk.snapenhance.config.ConfigCategory import me.rhunk.snapenhance.config.impl.ConfigIntegerValue diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/util/ActivityResultCallback.kt b/core/src/main/kotlin/me/rhunk/snapenhance/util/ActivityResultCallback.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/util/ActivityResultCallback.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/util/ActivityResultCallback.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/util/AndroidCompatExtensions.kt b/core/src/main/kotlin/me/rhunk/snapenhance/util/AndroidCompatExtensions.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/util/AndroidCompatExtensions.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/util/AndroidCompatExtensions.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/util/CallbackBuilder.kt b/core/src/main/kotlin/me/rhunk/snapenhance/util/CallbackBuilder.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/util/CallbackBuilder.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/util/CallbackBuilder.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/util/ReflectionHelper.kt b/core/src/main/kotlin/me/rhunk/snapenhance/util/ReflectionHelper.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/util/ReflectionHelper.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/util/ReflectionHelper.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/util/SQLiteDatabaseHelper.kt b/core/src/main/kotlin/me/rhunk/snapenhance/util/SQLiteDatabaseHelper.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/util/SQLiteDatabaseHelper.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/util/SQLiteDatabaseHelper.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/util/XposedHelperMacros.kt b/core/src/main/kotlin/me/rhunk/snapenhance/util/XposedHelperMacros.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/util/XposedHelperMacros.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/util/XposedHelperMacros.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/util/download/DownloadServer.kt b/core/src/main/kotlin/me/rhunk/snapenhance/util/download/DownloadServer.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/util/download/DownloadServer.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/util/download/DownloadServer.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/util/download/RemoteMediaResolver.kt b/core/src/main/kotlin/me/rhunk/snapenhance/util/download/RemoteMediaResolver.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/util/download/RemoteMediaResolver.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/util/download/RemoteMediaResolver.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/util/export/MessageExporter.kt b/core/src/main/kotlin/me/rhunk/snapenhance/util/export/MessageExporter.kt similarity index 99% rename from app/src/main/kotlin/me/rhunk/snapenhance/util/export/MessageExporter.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/util/export/MessageExporter.kt index d397e21a7..56675bef3 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/util/export/MessageExporter.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/util/export/MessageExporter.kt @@ -10,7 +10,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.withContext -import me.rhunk.snapenhance.BuildConfig +import me.rhunk.snapenhance.core.BuildConfig import me.rhunk.snapenhance.Logger import me.rhunk.snapenhance.ModContext import me.rhunk.snapenhance.data.ContentType @@ -191,7 +191,7 @@ class MessageExporter( runCatching { ZipFile( - context.androidContext.packageManager.getApplicationInfoCompat(BuildConfig.APPLICATION_ID, PackageManager.GET_META_DATA).publicSourceDir + context.androidContext.packageManager.getApplicationInfoCompat(BuildConfig.LIBRARY_PACKAGE_NAME, PackageManager.GET_META_DATA).publicSourceDir ).use { apkFile -> //export rawinflate.js apkFile.getEntry("assets/web/rawinflate.js").let { entry -> diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/util/protobuf/ProtoEditor.kt b/core/src/main/kotlin/me/rhunk/snapenhance/util/protobuf/ProtoEditor.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/util/protobuf/ProtoEditor.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/util/protobuf/ProtoEditor.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/util/protobuf/ProtoReader.kt b/core/src/main/kotlin/me/rhunk/snapenhance/util/protobuf/ProtoReader.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/util/protobuf/ProtoReader.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/util/protobuf/ProtoReader.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/util/protobuf/ProtoWriter.kt b/core/src/main/kotlin/me/rhunk/snapenhance/util/protobuf/ProtoWriter.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/util/protobuf/ProtoWriter.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/util/protobuf/ProtoWriter.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/util/snap/BitmojiSelfie.kt b/core/src/main/kotlin/me/rhunk/snapenhance/util/snap/BitmojiSelfie.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/util/snap/BitmojiSelfie.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/util/snap/BitmojiSelfie.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/util/snap/EncryptionHelper.kt b/core/src/main/kotlin/me/rhunk/snapenhance/util/snap/EncryptionHelper.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/util/snap/EncryptionHelper.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/util/snap/EncryptionHelper.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/util/snap/MediaDownloaderHelper.kt b/core/src/main/kotlin/me/rhunk/snapenhance/util/snap/MediaDownloaderHelper.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/util/snap/MediaDownloaderHelper.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/util/snap/MediaDownloaderHelper.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/util/snap/PreviewUtils.kt b/core/src/main/kotlin/me/rhunk/snapenhance/util/snap/PreviewUtils.kt similarity index 100% rename from app/src/main/kotlin/me/rhunk/snapenhance/util/snap/PreviewUtils.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/util/snap/PreviewUtils.kt diff --git a/app/src/main/res/drawable/action_button_cancel.xml b/core/src/main/res/drawable/action_button_cancel.xml similarity index 100% rename from app/src/main/res/drawable/action_button_cancel.xml rename to core/src/main/res/drawable/action_button_cancel.xml diff --git a/app/src/main/res/drawable/action_button_success.xml b/core/src/main/res/drawable/action_button_success.xml similarity index 100% rename from app/src/main/res/drawable/action_button_success.xml rename to core/src/main/res/drawable/action_button_success.xml diff --git a/app/src/main/res/drawable/back_arrow.xml b/core/src/main/res/drawable/back_arrow.xml similarity index 100% rename from app/src/main/res/drawable/back_arrow.xml rename to core/src/main/res/drawable/back_arrow.xml diff --git a/app/src/main/res/drawable/bitmoji_blank.xml b/core/src/main/res/drawable/bitmoji_blank.xml similarity index 100% rename from app/src/main/res/drawable/bitmoji_blank.xml rename to core/src/main/res/drawable/bitmoji_blank.xml diff --git a/app/src/main/res/drawable/debug_settings_icon.xml b/core/src/main/res/drawable/debug_settings_icon.xml similarity index 100% rename from app/src/main/res/drawable/debug_settings_icon.xml rename to core/src/main/res/drawable/debug_settings_icon.xml diff --git a/app/src/main/res/drawable/download_manager_item_background.xml b/core/src/main/res/drawable/download_manager_item_background.xml similarity index 100% rename from app/src/main/res/drawable/download_manager_item_background.xml rename to core/src/main/res/drawable/download_manager_item_background.xml diff --git a/app/src/main/res/drawable/settings_icon.xml b/core/src/main/res/drawable/settings_icon.xml similarity index 100% rename from app/src/main/res/drawable/settings_icon.xml rename to core/src/main/res/drawable/settings_icon.xml diff --git a/app/src/main/res/font/avenir_next_bold.ttf b/core/src/main/res/font/avenir_next_bold.ttf similarity index 100% rename from app/src/main/res/font/avenir_next_bold.ttf rename to core/src/main/res/font/avenir_next_bold.ttf diff --git a/app/src/main/res/font/avenir_next_medium.ttf b/core/src/main/res/font/avenir_next_medium.ttf similarity index 100% rename from app/src/main/res/font/avenir_next_medium.ttf rename to core/src/main/res/font/avenir_next_medium.ttf diff --git a/app/src/main/res/layout/activity_default_header.xml b/core/src/main/res/layout/activity_default_header.xml similarity index 100% rename from app/src/main/res/layout/activity_default_header.xml rename to core/src/main/res/layout/activity_default_header.xml diff --git a/app/src/main/res/layout/config_activity.xml b/core/src/main/res/layout/config_activity.xml similarity index 100% rename from app/src/main/res/layout/config_activity.xml rename to core/src/main/res/layout/config_activity.xml diff --git a/app/src/main/res/layout/config_activity_debug_item.xml b/core/src/main/res/layout/config_activity_debug_item.xml similarity index 100% rename from app/src/main/res/layout/config_activity_debug_item.xml rename to core/src/main/res/layout/config_activity_debug_item.xml diff --git a/app/src/main/res/layout/config_activity_item.xml b/core/src/main/res/layout/config_activity_item.xml similarity index 100% rename from app/src/main/res/layout/config_activity_item.xml rename to core/src/main/res/layout/config_activity_item.xml diff --git a/app/src/main/res/layout/debug_setting_item.xml b/core/src/main/res/layout/debug_setting_item.xml similarity index 100% rename from app/src/main/res/layout/debug_setting_item.xml rename to core/src/main/res/layout/debug_setting_item.xml diff --git a/app/src/main/res/layout/debug_settings_page.xml b/core/src/main/res/layout/debug_settings_page.xml similarity index 100% rename from app/src/main/res/layout/debug_settings_page.xml rename to core/src/main/res/layout/debug_settings_page.xml diff --git a/app/src/main/res/layout/device_spoofer_activity.xml b/core/src/main/res/layout/device_spoofer_activity.xml similarity index 100% rename from app/src/main/res/layout/device_spoofer_activity.xml rename to core/src/main/res/layout/device_spoofer_activity.xml diff --git a/app/src/main/res/layout/download_manager_activity.xml b/core/src/main/res/layout/download_manager_activity.xml similarity index 100% rename from app/src/main/res/layout/download_manager_activity.xml rename to core/src/main/res/layout/download_manager_activity.xml diff --git a/app/src/main/res/layout/download_manager_item.xml b/core/src/main/res/layout/download_manager_item.xml similarity index 100% rename from app/src/main/res/layout/download_manager_item.xml rename to core/src/main/res/layout/download_manager_item.xml diff --git a/app/src/main/res/layout/map.xml b/core/src/main/res/layout/map.xml similarity index 100% rename from app/src/main/res/layout/map.xml rename to core/src/main/res/layout/map.xml diff --git a/app/src/main/res/layout/precise_location_dialog.xml b/core/src/main/res/layout/precise_location_dialog.xml similarity index 100% rename from app/src/main/res/layout/precise_location_dialog.xml rename to core/src/main/res/layout/precise_location_dialog.xml diff --git a/app/src/main/res/mipmap-anydpi-v26/launcher_icon.xml b/core/src/main/res/mipmap-anydpi-v26/launcher_icon.xml similarity index 100% rename from app/src/main/res/mipmap-anydpi-v26/launcher_icon.xml rename to core/src/main/res/mipmap-anydpi-v26/launcher_icon.xml diff --git a/app/src/main/res/mipmap-anydpi-v26/launcher_icon_round.xml b/core/src/main/res/mipmap-anydpi-v26/launcher_icon_round.xml similarity index 100% rename from app/src/main/res/mipmap-anydpi-v26/launcher_icon_round.xml rename to core/src/main/res/mipmap-anydpi-v26/launcher_icon_round.xml diff --git a/app/src/main/res/mipmap-hdpi/launcher_icon_foreground.png b/core/src/main/res/mipmap-hdpi/launcher_icon_foreground.png similarity index 100% rename from app/src/main/res/mipmap-hdpi/launcher_icon_foreground.png rename to core/src/main/res/mipmap-hdpi/launcher_icon_foreground.png diff --git a/app/src/main/res/mipmap-hdpi/launcher_icon_round.png b/core/src/main/res/mipmap-hdpi/launcher_icon_round.png similarity index 100% rename from app/src/main/res/mipmap-hdpi/launcher_icon_round.png rename to core/src/main/res/mipmap-hdpi/launcher_icon_round.png diff --git a/app/src/main/res/mipmap-mdpi/launcher_icon_foreground.png b/core/src/main/res/mipmap-mdpi/launcher_icon_foreground.png similarity index 100% rename from app/src/main/res/mipmap-mdpi/launcher_icon_foreground.png rename to core/src/main/res/mipmap-mdpi/launcher_icon_foreground.png diff --git a/app/src/main/res/mipmap-mdpi/launcher_icon_round.png b/core/src/main/res/mipmap-mdpi/launcher_icon_round.png similarity index 100% rename from app/src/main/res/mipmap-mdpi/launcher_icon_round.png rename to core/src/main/res/mipmap-mdpi/launcher_icon_round.png diff --git a/app/src/main/res/mipmap-xhdpi/launcher_icon_foreground.png b/core/src/main/res/mipmap-xhdpi/launcher_icon_foreground.png similarity index 100% rename from app/src/main/res/mipmap-xhdpi/launcher_icon_foreground.png rename to core/src/main/res/mipmap-xhdpi/launcher_icon_foreground.png diff --git a/app/src/main/res/mipmap-xhdpi/launcher_icon_round.png b/core/src/main/res/mipmap-xhdpi/launcher_icon_round.png similarity index 100% rename from app/src/main/res/mipmap-xhdpi/launcher_icon_round.png rename to core/src/main/res/mipmap-xhdpi/launcher_icon_round.png diff --git a/app/src/main/res/mipmap-xxhdpi/launcher_icon_foreground.png b/core/src/main/res/mipmap-xxhdpi/launcher_icon_foreground.png similarity index 100% rename from app/src/main/res/mipmap-xxhdpi/launcher_icon_foreground.png rename to core/src/main/res/mipmap-xxhdpi/launcher_icon_foreground.png diff --git a/app/src/main/res/mipmap-xxhdpi/launcher_icon_round.png b/core/src/main/res/mipmap-xxhdpi/launcher_icon_round.png similarity index 100% rename from app/src/main/res/mipmap-xxhdpi/launcher_icon_round.png rename to core/src/main/res/mipmap-xxhdpi/launcher_icon_round.png diff --git a/app/src/main/res/mipmap-xxxhdpi/launcher_icon_foreground.png b/core/src/main/res/mipmap-xxxhdpi/launcher_icon_foreground.png similarity index 100% rename from app/src/main/res/mipmap-xxxhdpi/launcher_icon_foreground.png rename to core/src/main/res/mipmap-xxxhdpi/launcher_icon_foreground.png diff --git a/app/src/main/res/mipmap-xxxhdpi/launcher_icon_round.png b/core/src/main/res/mipmap-xxxhdpi/launcher_icon_round.png similarity index 100% rename from app/src/main/res/mipmap-xxxhdpi/launcher_icon_round.png rename to core/src/main/res/mipmap-xxxhdpi/launcher_icon_round.png diff --git a/app/src/main/res/values/arrays.xml b/core/src/main/res/values/arrays.xml similarity index 100% rename from app/src/main/res/values/arrays.xml rename to core/src/main/res/values/arrays.xml diff --git a/app/src/main/res/values/colors.xml b/core/src/main/res/values/colors.xml similarity index 100% rename from app/src/main/res/values/colors.xml rename to core/src/main/res/values/colors.xml diff --git a/app/src/main/res/values/dimens.xml b/core/src/main/res/values/dimens.xml similarity index 100% rename from app/src/main/res/values/dimens.xml rename to core/src/main/res/values/dimens.xml diff --git a/app/src/main/res/values/launcher_icon_background.xml b/core/src/main/res/values/launcher_icon_background.xml similarity index 100% rename from app/src/main/res/values/launcher_icon_background.xml rename to core/src/main/res/values/launcher_icon_background.xml diff --git a/app/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml similarity index 100% rename from app/src/main/res/values/strings.xml rename to core/src/main/res/values/strings.xml diff --git a/app/src/main/res/values/themes.xml b/core/src/main/res/values/themes.xml similarity index 100% rename from app/src/main/res/values/themes.xml rename to core/src/main/res/values/themes.xml diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e7e7f3b53..a45e52a0b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -11,6 +11,7 @@ osmdroid-android = "6.1.16" okhttp = "5.0.0-alpha.11" dexlib2 = "2.5.2" androidx-documentfile = "1.1.0-alpha01" +activity-ktx = "1.7.2" [libraries] @@ -24,6 +25,7 @@ osmdroid-android = { group = "org.osmdroid", name = "osmdroid-android", version. okhttp = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "okhttp" } dexlib2 = { group = "org.smali", name = "dexlib2", version.ref = "dexlib2" } androidx-documentfile = { group = "androidx.documentfile", name = "documentfile", version.ref = "androidx-documentfile" } +androidx-activity-ktx = { group = "androidx.activity", name = "activity-ktx", version.ref = "activity-ktx" } [plugins] diff --git a/settings.gradle.kts b/settings.gradle.kts index ee241784b..c62f1ff7f 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -15,6 +15,8 @@ dependencyResolutionManagement { } } + rootProject.name = "SnapEnhance" +include(":core") include(":app") include(":mapper") \ No newline at end of file From 304bd9fe943159ba2fc2db5ff9970b7bec698cf0 Mon Sep 17 00:00:00 2001 From: rhunk <101876869+rhunk@users.noreply.github.com> Date: Fri, 28 Jul 2023 02:03:39 +0200 Subject: [PATCH 02/81] compose test --- app/build.gradle.kts | 14 +- app/src/main/AndroidManifest.xml | 2 +- .../rhunk/snapenhance/manager/MainActivity.kt | 55 ++++++ .../rhunk/snapenhance/manager/Navigation.kt | 113 ++++++++++++ .../me/rhunk/snapenhance/manager/Theme.kt | 164 ++++++++++++++++++ .../manager/sections/NotImplemented.kt | 18 ++ build.gradle.kts | 1 + core/build.gradle.kts | 4 +- .../me/rhunk/snapenhance/SnapEnhance.kt | 2 +- .../rhunk/snapenhance/action/impl/OpenMap.kt | 2 +- .../rhunk/snapenhance/bridge/BridgeClient.kt | 4 +- .../ui/menu/impl/SettingsGearInjector.kt | 2 +- .../util/export/MessageExporter.kt | 2 +- gradle.properties | 1 - gradle/libs.versions.toml | 14 +- gradle/wrapper/gradle-wrapper.properties | 2 +- 16 files changed, 384 insertions(+), 16 deletions(-) create mode 100644 app/src/main/kotlin/me/rhunk/snapenhance/manager/Navigation.kt create mode 100644 app/src/main/kotlin/me/rhunk/snapenhance/manager/Theme.kt create mode 100644 app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/NotImplemented.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index cd4cb3471..91a385636 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -8,21 +8,25 @@ plugins { android { namespace = "me.rhunk.snapenhance" - compileSdk = 33 + compileSdk = 34 buildFeatures { aidl = true + compose = true + } + + composeOptions { + kotlinCompilerExtensionVersion = "1.4.8" } defaultConfig { - applicationId = "me.rhunk.snapenhance" + applicationId = rootProject.ext["applicationId"].toString() minSdk = 28 //noinspection OldTargetApi targetSdk = 33 multiDexEnabled = true } - buildTypes { release { isMinifyEnabled = false @@ -76,7 +80,11 @@ android { dependencies { implementation(project(":core")) + implementation(libs.androidx.material.icons.extended) + implementation(libs.androidx.navigation.compose) implementation(libs.androidx.activity.ktx) + implementation(libs.androidx.material3) + implementation(libs.androidx.material) } afterEvaluate { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a56d65c32..bb1de7db5 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -38,7 +38,7 @@ diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/MainActivity.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/MainActivity.kt index 1f284bc10..30f4dc291 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/manager/MainActivity.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/manager/MainActivity.kt @@ -1,10 +1,65 @@ package me.rhunk.snapenhance.manager +import android.annotation.SuppressLint import android.os.Bundle import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Text +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Settings +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.TopAppBar +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable +import androidx.navigation.compose.navigation +import androidx.navigation.compose.rememberNavController class MainActivity : ComponentActivity() { + @SuppressLint("UnusedMaterialScaffoldPaddingParameter") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + + setContent { + App() + } + } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun App() { + val navController = rememberNavController() + AppMaterialTheme { + Scaffold( + topBar = { + TopAppBar( + title = { Text(text = "SnapEnhance") }, + actions = { + IconButton(onClick = { /*TODO*/ }) { + Icon(Icons.Filled.Settings, contentDescription = "Settings") + } + } + ) + }, + containerColor = MaterialTheme.colorScheme.background, + bottomBar = { NavBar(navController = navController) } + ) { innerPadding -> + NavHost(navController, startDestination = "main", Modifier.padding(innerPadding)) { + navigation(MainSections.HOME.route, "main") { + MainSections.values().toList().forEach { section -> + composable(section.route) { + section.content() + } + } + } + } + } } } \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/Navigation.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/Navigation.kt new file mode 100644 index 000000000..18d7cce09 --- /dev/null +++ b/app/src/main/kotlin/me/rhunk/snapenhance/manager/Navigation.kt @@ -0,0 +1,113 @@ +package me.rhunk.snapenhance.manager + +import androidx.compose.animation.core.animateDpAsState +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.requiredWidth +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.BugReport +import androidx.compose.material.icons.filled.Download +import androidx.compose.material.icons.filled.Group +import androidx.compose.material.icons.filled.Home +import androidx.compose.material.icons.filled.Stars +import androidx.compose.material3.Icon +import androidx.compose.material3.NavigationBar +import androidx.compose.material3.NavigationBarItem +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.unit.dp +import androidx.navigation.NavController +import androidx.navigation.NavDestination.Companion.hierarchy +import androidx.navigation.NavGraph.Companion.findStartDestination +import androidx.navigation.compose.currentBackStackEntryAsState +import me.rhunk.snapenhance.manager.sections.NotImplemented + + +enum class MainSections( + val route: String, + val title: String, + val icon: ImageVector, + val content: @Composable () -> Unit +) { + DOWNLOADS( + route = "downloads", + title = "Downloads", + icon = Icons.Filled.Download, + content = { NotImplemented() } + ), + FEATURES( + route = "features", + title = "Features", + icon = Icons.Filled.Stars, + content = { NotImplemented() } + ), + HOME( + route = "home", + title = "Home", + icon = Icons.Filled.Home, + content = { NotImplemented() } + ), + FRIENDS( + route = "friends", + title = "Friends", + icon = Icons.Filled.Group, + content = { NotImplemented() } + ), + DEBUG( + route = "debug", + title = "Debug", + icon = Icons.Filled.BugReport, + content = { NotImplemented() } + ); +} + +@Composable +fun NavBar( + navController: NavController +) { + NavigationBar { + val navBackStackEntry by navController.currentBackStackEntryAsState() + val currentDestination = navBackStackEntry?.destination + MainSections.values().toList().forEach { section -> + fun selected() = currentDestination?.hierarchy?.any { it.route == section.route } == true + + NavigationBarItem( + modifier = Modifier + .requiredWidth(120.dp) + .fillMaxHeight(), + icon = { + val iconOffset by animateDpAsState( + if (selected()) 0.dp else 10.dp, + label = "" + ) + Icon( + imageVector = section.icon, + contentDescription = null, + modifier = Modifier.offset(y = iconOffset) + ) + }, + + label = { + val labelOffset by animateDpAsState( + if (selected()) 0.dp else (-5).dp, + label = "" + ) + Text(text = if (selected()) section.title else "", modifier = Modifier.offset(y = labelOffset)) + }, + selected = selected(), + onClick = { + navController.navigate(section.route) { + popUpTo(navController.graph.findStartDestination().id) { + saveState = true + } + launchSingleTop = true + restoreState = true + } + } + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/Theme.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/Theme.kt new file mode 100644 index 000000000..70df5a95e --- /dev/null +++ b/app/src/main/kotlin/me/rhunk/snapenhance/manager/Theme.kt @@ -0,0 +1,164 @@ +package me.rhunk.snapenhance.manager + +import android.os.Build +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.dynamicDarkColorScheme +import androidx.compose.material3.dynamicLightColorScheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext + +val md_theme_light_primary = Color(0xFF6750A4) +val md_theme_light_onPrimary = Color(0xFFFFFFFF) +val md_theme_light_primaryContainer = Color(0xFFE9DDFF) +val md_theme_light_onPrimaryContainer = Color(0xFF22005D) +val md_theme_light_secondary = Color(0xFF625B71) +val md_theme_light_onSecondary = Color(0xFFFFFFFF) +val md_theme_light_secondaryContainer = Color(0xFFE8DEF8) +val md_theme_light_onSecondaryContainer = Color(0xFF1E192B) +val md_theme_light_tertiary = Color(0xFF3C5BA9) +val md_theme_light_onTertiary = Color(0xFFFFFFFF) +val md_theme_light_tertiaryContainer = Color(0xFFDBE1FF) +val md_theme_light_onTertiaryContainer = Color(0xFF001849) +val md_theme_light_error = Color(0xFFBA1A1A) +val md_theme_light_errorContainer = Color(0xFFFFDAD6) +val md_theme_light_onError = Color(0xFFFFFFFF) +val md_theme_light_onErrorContainer = Color(0xFF410002) +val md_theme_light_background = Color(0xFFFFFBFF) +val md_theme_light_onBackground = Color(0xFF1C1B1E) +val md_theme_light_surface = Color(0xFFFFFBFF) +val md_theme_light_onSurface = Color(0xFF1C1B1E) +val md_theme_light_surfaceVariant = Color(0xFFE7E0EB) +val md_theme_light_onSurfaceVariant = Color(0xFF49454E) +val md_theme_light_outline = Color(0xFF7A757F) +val md_theme_light_inverseOnSurface = Color(0xFFF4EFF4) +val md_theme_light_inverseSurface = Color(0xFF313033) +val md_theme_light_inversePrimary = Color(0xFFCFBCFF) +val md_theme_light_surfaceTint = Color(0xFF6750A4) +val md_theme_light_outlineVariant = Color(0xFFCAC4CF) +val md_theme_light_scrim = Color(0xFF000000) + +val md_theme_dark_primary = Color(0xFFCFBCFF) +val md_theme_dark_onPrimary = Color(0xFF381E72) +val md_theme_dark_primaryContainer = Color(0xFF4F378A) +val md_theme_dark_onPrimaryContainer = Color(0xFFE9DDFF) +val md_theme_dark_secondary = Color(0xFFCBC2DB) +val md_theme_dark_onSecondary = Color(0xFF332D41) +val md_theme_dark_secondaryContainer = Color(0xFF4A4458) +val md_theme_dark_onSecondaryContainer = Color(0xFFE8DEF8) +val md_theme_dark_tertiary = Color(0xFFB3C5FF) +val md_theme_dark_onTertiary = Color(0xFF002B75) +val md_theme_dark_tertiaryContainer = Color(0xFF21428F) +val md_theme_dark_onTertiaryContainer = Color(0xFFDBE1FF) +val md_theme_dark_error = Color(0xFFFFB4AB) +val md_theme_dark_errorContainer = Color(0xFF93000A) +val md_theme_dark_onError = Color(0xFF690005) +val md_theme_dark_onErrorContainer = Color(0xFFFFDAD6) +val md_theme_dark_background = Color(0xFF1C1B1E) +val md_theme_dark_onBackground = Color(0xFFE6E1E6) +val md_theme_dark_surface = Color(0xFF1C1B1E) +val md_theme_dark_onSurface = Color(0xFFE6E1E6) +val md_theme_dark_surfaceVariant = Color(0xFF49454E) +val md_theme_dark_onSurfaceVariant = Color(0xFFCAC4CF) +val md_theme_dark_outline = Color(0xFF948F99) +val md_theme_dark_inverseOnSurface = Color(0xFF1C1B1E) +val md_theme_dark_inverseSurface = Color(0xFFE6E1E6) +val md_theme_dark_inversePrimary = Color(0xFF6750A4) +val md_theme_dark_surfaceTint = Color(0xFFCFBCFF) +val md_theme_dark_outlineVariant = Color(0xFF49454E) +val md_theme_dark_scrim = Color(0xFF000000) + + +val seed = Color(0xFF6750A4) + + + + +private val LightThemeColors = lightColorScheme( + primary = md_theme_light_primary, + onPrimary = md_theme_light_onPrimary, + primaryContainer = md_theme_light_primaryContainer, + onPrimaryContainer = md_theme_light_onPrimaryContainer, + secondary = md_theme_light_secondary, + onSecondary = md_theme_light_onSecondary, + secondaryContainer = md_theme_light_secondaryContainer, + onSecondaryContainer = md_theme_light_onSecondaryContainer, + tertiary = md_theme_light_tertiary, + onTertiary = md_theme_light_onTertiary, + tertiaryContainer = md_theme_light_tertiaryContainer, + onTertiaryContainer = md_theme_light_onTertiaryContainer, + error = md_theme_light_error, + onError = md_theme_light_onError, + errorContainer = md_theme_light_errorContainer, + onErrorContainer = md_theme_light_onErrorContainer, + background = md_theme_light_background, + onBackground = md_theme_light_onBackground, + surface = md_theme_light_surface, + onSurface = md_theme_light_onSurface, + surfaceVariant = md_theme_light_surfaceVariant, + onSurfaceVariant = md_theme_light_onSurfaceVariant, + outline = md_theme_light_outline, + inverseOnSurface = md_theme_light_inverseOnSurface, + inverseSurface = md_theme_light_inverseSurface, + inversePrimary = md_theme_light_inversePrimary, + surfaceTint = md_theme_light_surfaceTint, + outlineVariant = md_theme_light_outlineVariant, + scrim = md_theme_light_scrim +) + +private val DarkThemeColors = lightColorScheme( + primary = md_theme_dark_primary, + onPrimary = md_theme_dark_onPrimary, + primaryContainer = md_theme_dark_primaryContainer, + onPrimaryContainer = md_theme_dark_onPrimaryContainer, + secondary = md_theme_dark_secondary, + onSecondary = md_theme_dark_onSecondary, + secondaryContainer = md_theme_dark_secondaryContainer, + onSecondaryContainer = md_theme_dark_onSecondaryContainer, + tertiary = md_theme_dark_tertiary, + onTertiary = md_theme_dark_onTertiary, + tertiaryContainer = md_theme_dark_tertiaryContainer, + onTertiaryContainer = md_theme_dark_onTertiaryContainer, + error = md_theme_dark_error, + onError = md_theme_dark_onError, + errorContainer = md_theme_dark_errorContainer, + onErrorContainer = md_theme_dark_onErrorContainer, + background = md_theme_dark_background, + onBackground = md_theme_dark_onBackground, + surface = md_theme_dark_surface, + onSurface = md_theme_dark_onSurface, + surfaceVariant = md_theme_dark_surfaceVariant, + onSurfaceVariant = md_theme_dark_onSurfaceVariant, + outline = md_theme_dark_outline, + inverseOnSurface = md_theme_dark_inverseOnSurface, + inverseSurface = md_theme_dark_inverseSurface, + inversePrimary = md_theme_dark_inversePrimary, + surfaceTint = md_theme_dark_surfaceTint, + outlineVariant = md_theme_dark_outlineVariant, + scrim = md_theme_dark_scrim +) + +@Composable +fun AppMaterialTheme( + isDarkTheme: Boolean = isSystemInDarkTheme(), + content: @Composable () -> Unit +) { + val dynamicColor = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S + val colorScheme = when { + dynamicColor && isDarkTheme -> { + dynamicDarkColorScheme(LocalContext.current) + } + dynamicColor && !isDarkTheme -> { + dynamicLightColorScheme(LocalContext.current) + } + !isDarkTheme -> LightThemeColors + else -> DarkThemeColors + } + + MaterialTheme( + colorScheme = colorScheme, + content = content + ) +} \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/NotImplemented.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/NotImplemented.kt new file mode 100644 index 000000000..272ef5290 --- /dev/null +++ b/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/NotImplemented.kt @@ -0,0 +1,18 @@ +package me.rhunk.snapenhance.manager.sections + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier + +@Composable +fun NotImplemented() { + Column( + modifier = Modifier.fillMaxSize(), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Text(text = "Not Implemented") + } +} \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 3f0d75e4c..920a410e6 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,5 +7,6 @@ plugins { rootProject.ext.set("appVersionName", "1.1.0") rootProject.ext.set("appVersionCode", 7) +rootProject.ext.set("applicationId", "me.rhunk.snapenhance") true // Needed to make the Suppress annotation work for the plugins block \ No newline at end of file diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 8de69a2da..8c8528d93 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -5,16 +5,18 @@ plugins { } android { namespace = "me.rhunk.snapenhance.core" - compileSdk = 33 + compileSdk = 34 buildFeatures { aidl = true + buildConfig = true } defaultConfig { minSdk = 28 buildConfigField("String", "VERSION_NAME", "\"${rootProject.ext["appVersionName"]}\"") buildConfigField("int", "VERSION_CODE", "${rootProject.ext["appVersionCode"]}") + buildConfigField("String", "APPLICATION_ID", "\"${rootProject.ext["applicationId"]}\"") } kotlinOptions { diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/SnapEnhance.kt b/core/src/main/kotlin/me/rhunk/snapenhance/SnapEnhance.kt index e4fe79a4d..0c7b28c29 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/SnapEnhance.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/SnapEnhance.kt @@ -35,7 +35,7 @@ class SnapEnhance { //for lspatch builds, we need to check if the service is correctly installed runCatching { - appContext.androidContext.packageManager.getApplicationInfoCompat(BuildConfig.LIBRARY_PACKAGE_NAME, PackageManager.GET_META_DATA) + appContext.androidContext.packageManager.getApplicationInfoCompat(BuildConfig.APPLICATION_ID, PackageManager.GET_META_DATA) }.onFailure { appContext.crash("SnapEnhance bridge service is not installed. Please download stable version from https://github.com/rhunk/SnapEnhance/releases") return@hook diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/action/impl/OpenMap.kt b/core/src/main/kotlin/me/rhunk/snapenhance/action/impl/OpenMap.kt index 0909ce160..242c7db89 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/action/impl/OpenMap.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/action/impl/OpenMap.kt @@ -11,7 +11,7 @@ class OpenMap: AbstractAction("action.open_map", dependsOnProperty = ConfigPrope override fun run() { context.runOnUiThread { val mapActivityIntent = Intent() - mapActivityIntent.setClassName(BuildConfig.LIBRARY_PACKAGE_NAME, MapActivity::class.java.name) + mapActivityIntent.setClassName(BuildConfig.APPLICATION_ID, MapActivity::class.java.name) mapActivityIntent.putExtra("location", Bundle().apply { putDouble("latitude", context.config.string(ConfigProperty.LATITUDE).toDouble()) putDouble("longitude", context.config.string(ConfigProperty.LONGITUDE).toDouble()) diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/bridge/BridgeClient.kt b/core/src/main/kotlin/me/rhunk/snapenhance/bridge/BridgeClient.kt index 5e98c81da..3dc95eba9 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/bridge/BridgeClient.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/bridge/BridgeClient.kt @@ -32,12 +32,12 @@ class BridgeClient( with(context.androidContext) { //ensure the remote process is running startActivity(Intent() - .setClassName(BuildConfig.LIBRARY_PACKAGE_NAME, ForceStartActivity::class.java.name) + .setClassName(BuildConfig.APPLICATION_ID, ForceStartActivity::class.java.name) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_MULTIPLE_TASK) ) val intent = Intent() - .setClassName(BuildConfig.LIBRARY_PACKAGE_NAME, BridgeService::class.java.name) + .setClassName(BuildConfig.APPLICATION_ID, BridgeService::class.java.name) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { bindService( intent, diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/SettingsGearInjector.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/SettingsGearInjector.kt index a904e2c03..66ba715be 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/SettingsGearInjector.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/SettingsGearInjector.kt @@ -48,7 +48,7 @@ class SettingsGearInjector : AbstractMenu() { setOnClickListener { val intent = Intent().apply { - setClassName(BuildConfig.LIBRARY_PACKAGE_NAME, ConfigActivity::class.java.name) + setClassName(BuildConfig.APPLICATION_ID, ConfigActivity::class.java.name) } intent.putExtra("lspatched", File(context.cacheDir, "lspatch/origin").exists()) context.startActivity(intent) diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/util/export/MessageExporter.kt b/core/src/main/kotlin/me/rhunk/snapenhance/util/export/MessageExporter.kt index 56675bef3..53ceb4236 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/util/export/MessageExporter.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/util/export/MessageExporter.kt @@ -191,7 +191,7 @@ class MessageExporter( runCatching { ZipFile( - context.androidContext.packageManager.getApplicationInfoCompat(BuildConfig.LIBRARY_PACKAGE_NAME, PackageManager.GET_META_DATA).publicSourceDir + context.androidContext.packageManager.getApplicationInfoCompat(BuildConfig.APPLICATION_ID, PackageManager.GET_META_DATA).publicSourceDir ).use { apkFile -> //export rawinflate.js apkFile.getEntry("assets/web/rawinflate.js").let { entry -> diff --git a/gradle.properties b/gradle.properties index 022338b78..2a7ec6959 100644 --- a/gradle.properties +++ b/gradle.properties @@ -21,5 +21,4 @@ kotlin.code.style=official # resources declared in the library itself and none from the library's dependencies, # thereby reducing the size of the R class for that library android.nonTransitiveRClass=true -android.defaults.buildfeatures.buildconfig=true android.nonFinalResIds=false \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a45e52a0b..796de5bd6 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,10 +1,13 @@ [versions] -agp = "8.1.0" +agp = "8.2.0-alpha13" +androidx-material = "1.6.0-alpha02" junit = "4.13.2" -kotlin = "1.8.21" +kotlin = "1.8.22" kotlinx-coroutines-android = "1.7.2" kotlin-reflect = "1.8.21" -recyclerview = "1.3.0" +material-icons-extended = "1.6.0-alpha03" +navigation-compose = "2.6.0" +recyclerview = "1.3.1" gson = "2.10.1" ffmpeg-kit = "5.1.LTS" osmdroid-android = "6.1.16" @@ -12,9 +15,13 @@ okhttp = "5.0.0-alpha.11" dexlib2 = "2.5.2" androidx-documentfile = "1.1.0-alpha01" activity-ktx = "1.7.2" +material3 = "1.1.1" [libraries] +androidx-material = { module = "androidx.compose.material:material", version.ref = "androidx-material" } +androidx-material-icons-extended = { module = "androidx.compose.material:material-icons-extended", version.ref = "material-icons-extended" } +androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigation-compose" } coroutines = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "kotlinx-coroutines-android" } junit = { module = "junit:junit", version.ref = "junit" } kotlin-reflect = { group = "org.jetbrains.kotlin", name = "kotlin-reflect", version.ref = "kotlin-reflect" } @@ -26,6 +33,7 @@ okhttp = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "okhtt dexlib2 = { group = "org.smali", name = "dexlib2", version.ref = "dexlib2" } androidx-documentfile = { group = "androidx.documentfile", name = "documentfile", version.ref = "androidx-documentfile" } androidx-activity-ktx = { group = "androidx.activity", name = "activity-ktx", version.ref = "activity-ktx" } +androidx-material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "material3" } [plugins] diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 93de04965..2ec122dfa 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Fri May 12 21:23:16 CEST 2023 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-8.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME From e4cab94ec789e6eb52e3f683ab148e11bbec9435 Mon Sep 17 00:00:00 2001 From: rhunk <101876869+rhunk@users.noreply.github.com> Date: Fri, 28 Jul 2023 22:20:41 +0200 Subject: [PATCH 03/81] ui: home screen - monochrome launcher icon --- app/build.gradle.kts | 4 + app/src/main/AndroidManifest.xml | 12 +- .../rhunk/snapenhance/manager/MainActivity.kt | 32 +--- .../rhunk/snapenhance/manager/Navigation.kt | 157 ++++++++---------- .../me/rhunk/snapenhance/manager/Section.kt | 61 +++++++ .../me/rhunk/snapenhance/manager/Theme.kt | 2 - .../manager/data/InstallationSummary.kt | 17 ++ .../manager/data/ManagerContext.kt | 35 ++++ .../manager/sections/DebugSection.kt | 4 + .../manager/sections/FeaturesSection.kt | 52 ++++++ .../manager/sections/HomeSection.kt | 108 ++++++++++++ .../manager/sections/NotImplemented.kt | 16 +- .../res/drawable/launcher_icon_monochrome.xml | 26 +++ .../res/mipmap-anydpi-v26/launcher_icon.xml | 1 + .../mipmap-anydpi-v26/launcher_icon_round.xml | 0 .../mipmap-hdpi/launcher_icon_foreground.png | Bin .../res/mipmap-hdpi/launcher_icon_round.png | Bin .../mipmap-mdpi/launcher_icon_foreground.png | Bin .../res/mipmap-mdpi/launcher_icon_round.png | Bin .../mipmap-xhdpi/launcher_icon_foreground.png | Bin .../res/mipmap-xhdpi/launcher_icon_round.png | Bin .../launcher_icon_foreground.png | Bin .../res/mipmap-xxhdpi/launcher_icon_round.png | Bin .../launcher_icon_foreground.png | Bin .../mipmap-xxxhdpi/launcher_icon_round.png | Bin .../rhunk/snapenhance/bridge/BridgeClient.kt | 1 - .../snapenhance/bridge/FileLoaderWrapper.kt | 35 ++++ .../bridge/wrapper/ConfigWrapper.kt | 27 +-- .../bridge/wrapper/MappingsWrapper.kt | 157 ++++++++++++++++++ 29 files changed, 604 insertions(+), 143 deletions(-) create mode 100644 app/src/main/kotlin/me/rhunk/snapenhance/manager/Section.kt create mode 100644 app/src/main/kotlin/me/rhunk/snapenhance/manager/data/InstallationSummary.kt create mode 100644 app/src/main/kotlin/me/rhunk/snapenhance/manager/data/ManagerContext.kt create mode 100644 app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/DebugSection.kt create mode 100644 app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/FeaturesSection.kt create mode 100644 app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/HomeSection.kt create mode 100644 app/src/main/res/drawable/launcher_icon_monochrome.xml rename {core => app}/src/main/res/mipmap-anydpi-v26/launcher_icon.xml (78%) rename {core => app}/src/main/res/mipmap-anydpi-v26/launcher_icon_round.xml (100%) rename {core => app}/src/main/res/mipmap-hdpi/launcher_icon_foreground.png (100%) rename {core => app}/src/main/res/mipmap-hdpi/launcher_icon_round.png (100%) rename {core => app}/src/main/res/mipmap-mdpi/launcher_icon_foreground.png (100%) rename {core => app}/src/main/res/mipmap-mdpi/launcher_icon_round.png (100%) rename {core => app}/src/main/res/mipmap-xhdpi/launcher_icon_foreground.png (100%) rename {core => app}/src/main/res/mipmap-xhdpi/launcher_icon_round.png (100%) rename {core => app}/src/main/res/mipmap-xxhdpi/launcher_icon_foreground.png (100%) rename {core => app}/src/main/res/mipmap-xxhdpi/launcher_icon_round.png (100%) rename {core => app}/src/main/res/mipmap-xxxhdpi/launcher_icon_foreground.png (100%) rename {core => app}/src/main/res/mipmap-xxxhdpi/launcher_icon_round.png (100%) create mode 100644 core/src/main/kotlin/me/rhunk/snapenhance/bridge/FileLoaderWrapper.kt create mode 100644 core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/MappingsWrapper.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 91a385636..3bfb6c14d 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -85,6 +85,10 @@ dependencies { implementation(libs.androidx.activity.ktx) implementation(libs.androidx.material3) implementation(libs.androidx.material) + + debugImplementation("androidx.compose.ui:ui-tooling:1.4.3") + implementation("androidx.compose.ui:ui-tooling-preview:1.4.3") + implementation(kotlin("reflect")) } afterEvaluate { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index bb1de7db5..48d16183a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -7,16 +7,16 @@ - - - + + + + - NavHost(navController, startDestination = "main", Modifier.padding(innerPadding)) { - navigation(MainSections.HOME.route, "main") { - MainSections.values().toList().forEach { section -> - composable(section.route) { - section.content() - } - } - } - } + navigation.NavigationHost(navController = navController, innerPadding = innerPadding) } } } \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/Navigation.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/Navigation.kt index 18d7cce09..c93b41178 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/manager/Navigation.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/manager/Navigation.kt @@ -1,113 +1,100 @@ package me.rhunk.snapenhance.manager import androidx.compose.animation.core.animateDpAsState +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.requiredWidth -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.BugReport -import androidx.compose.material.icons.filled.Download -import androidx.compose.material.icons.filled.Group -import androidx.compose.material.icons.filled.Home -import androidx.compose.material.icons.filled.Stars import androidx.compose.material3.Icon import androidx.compose.material3.NavigationBar import androidx.compose.material3.NavigationBarItem import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.unit.dp import androidx.navigation.NavController import androidx.navigation.NavDestination.Companion.hierarchy import androidx.navigation.NavGraph.Companion.findStartDestination +import androidx.navigation.NavHostController +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable import androidx.navigation.compose.currentBackStackEntryAsState -import me.rhunk.snapenhance.manager.sections.NotImplemented +import me.rhunk.snapenhance.manager.data.ManagerContext -enum class MainSections( - val route: String, - val title: String, - val icon: ImageVector, - val content: @Composable () -> Unit +class Navigation( + private val context: ManagerContext ) { - DOWNLOADS( - route = "downloads", - title = "Downloads", - icon = Icons.Filled.Download, - content = { NotImplemented() } - ), - FEATURES( - route = "features", - title = "Features", - icon = Icons.Filled.Stars, - content = { NotImplemented() } - ), - HOME( - route = "home", - title = "Home", - icon = Icons.Filled.Home, - content = { NotImplemented() } - ), - FRIENDS( - route = "friends", - title = "Friends", - icon = Icons.Filled.Group, - content = { NotImplemented() } - ), - DEBUG( - route = "debug", - title = "Debug", - icon = Icons.Filled.BugReport, - content = { NotImplemented() } - ); -} + @Composable + fun NavigationHost( + navController: NavHostController, + innerPadding: PaddingValues + ) { + val sections = remember { EnumSection.values().toList().map { + it to it.section.constructors.first().call() + }.onEach { (_, instance) -> + instance.manager = context + instance.navController = navController + } } + val homeSection = EnumSection.HOME -@Composable -fun NavBar( - navController: NavController -) { - NavigationBar { - val navBackStackEntry by navController.currentBackStackEntryAsState() - val currentDestination = navBackStackEntry?.destination - MainSections.values().toList().forEach { section -> - fun selected() = currentDestination?.hierarchy?.any { it.route == section.route } == true + NavHost(navController, startDestination = homeSection.route, Modifier.padding(innerPadding)) { + sections.forEach { (section, instance) -> + composable(section.route) { + instance.Content() + } + } + } + } + + @Composable + fun NavBar( + navController: NavController + ) { + NavigationBar { + val navBackStackEntry by navController.currentBackStackEntryAsState() + val currentDestination = navBackStackEntry?.destination + EnumSection.values().toList().forEach { section -> + fun selected() = currentDestination?.hierarchy?.any { it.route == section.route } == true - NavigationBarItem( - modifier = Modifier - .requiredWidth(120.dp) - .fillMaxHeight(), - icon = { - val iconOffset by animateDpAsState( - if (selected()) 0.dp else 10.dp, - label = "" - ) - Icon( - imageVector = section.icon, - contentDescription = null, - modifier = Modifier.offset(y = iconOffset) - ) - }, + NavigationBarItem( + modifier = Modifier + .requiredWidth(120.dp) + .fillMaxHeight(), + icon = { + val iconOffset by animateDpAsState( + if (selected()) 0.dp else 10.dp, + label = "" + ) + Icon( + imageVector = section.icon, + contentDescription = null, + modifier = Modifier.offset(y = iconOffset) + ) + }, - label = { - val labelOffset by animateDpAsState( - if (selected()) 0.dp else (-5).dp, - label = "" - ) - Text(text = if (selected()) section.title else "", modifier = Modifier.offset(y = labelOffset)) - }, - selected = selected(), - onClick = { - navController.navigate(section.route) { - popUpTo(navController.graph.findStartDestination().id) { - saveState = true + label = { + val labelOffset by animateDpAsState( + if (selected()) 0.dp else (-5).dp, + label = "" + ) + Text(text = if (selected()) section.title else "", modifier = Modifier.offset(y = labelOffset)) + }, + selected = selected(), + onClick = { + navController.navigate(section.route) { + popUpTo(navController.graph.findStartDestination().id) { + saveState = true + } + launchSingleTop = true + restoreState = true } - launchSingleTop = true - restoreState = true } - } - ) + ) + } } } -} \ No newline at end of file +} diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/Section.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/Section.kt new file mode 100644 index 000000000..17162e244 --- /dev/null +++ b/app/src/main/kotlin/me/rhunk/snapenhance/manager/Section.kt @@ -0,0 +1,61 @@ +package me.rhunk.snapenhance.manager + +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.BugReport +import androidx.compose.material.icons.filled.Download +import androidx.compose.material.icons.filled.Group +import androidx.compose.material.icons.filled.Home +import androidx.compose.material.icons.filled.Stars +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.navigation.NavController +import me.rhunk.snapenhance.manager.data.ManagerContext +import me.rhunk.snapenhance.manager.sections.FeaturesSection +import me.rhunk.snapenhance.manager.sections.HomeSection +import me.rhunk.snapenhance.manager.sections.NotImplemented +import kotlin.reflect.KClass + +enum class EnumSection( + val route: String, + val title: String, + val icon: ImageVector, + val section: KClass = NotImplemented::class +) { + DOWNLOADS( + route = "downloads", + title = "Downloads", + icon = Icons.Filled.Download + ), + FEATURES( + route = "features", + title = "Features", + icon = Icons.Filled.Stars, + section = FeaturesSection::class + ), + HOME( + route = "home", + title = "Home", + icon = Icons.Filled.Home, + section = HomeSection::class + ), + FRIENDS( + route = "friends", + title = "Friends", + icon = Icons.Filled.Group + ), + DEBUG( + route = "debug", + title = "Debug", + icon = Icons.Filled.BugReport + ); +} + + + +open class Section { + lateinit var manager: ManagerContext + lateinit var navController: NavController + + @Composable + open fun Content() { NotImplemented() } +} \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/Theme.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/Theme.kt index 70df5a95e..eedc6b140 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/manager/Theme.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/manager/Theme.kt @@ -74,8 +74,6 @@ val md_theme_dark_scrim = Color(0xFF000000) val seed = Color(0xFF6750A4) - - private val LightThemeColors = lightColorScheme( primary = md_theme_light_primary, onPrimary = md_theme_light_onPrimary, diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/data/InstallationSummary.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/data/InstallationSummary.kt new file mode 100644 index 000000000..f7b0150da --- /dev/null +++ b/app/src/main/kotlin/me/rhunk/snapenhance/manager/data/InstallationSummary.kt @@ -0,0 +1,17 @@ +package me.rhunk.snapenhance.manager.data + + +data class SnapchatAppInfo( + val version: String, + val versionCode: Long +) + +data class ModMappingsInfo( + val generatedSnapchatVersion: Long, + val isOutdated: Boolean +) + +data class InstallationSummary( + val snapchatInfo: SnapchatAppInfo?, + val mappingsInfo: ModMappingsInfo? +) diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/data/ManagerContext.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/data/ManagerContext.kt new file mode 100644 index 000000000..487a79074 --- /dev/null +++ b/app/src/main/kotlin/me/rhunk/snapenhance/manager/data/ManagerContext.kt @@ -0,0 +1,35 @@ +package me.rhunk.snapenhance.manager.data + +import android.content.Context +import me.rhunk.snapenhance.bridge.wrapper.ConfigWrapper +import me.rhunk.snapenhance.bridge.wrapper.MappingsWrapper +import me.rhunk.snapenhance.bridge.wrapper.TranslationWrapper + +class ManagerContext( + private val context: Context +) { + private val config = ConfigWrapper() + private val translation = TranslationWrapper() + private val mappings = MappingsWrapper(context) + + init { + config.loadFromContext(context) + translation.loadFromContext(context) + mappings.apply { loadFromContext(context) }.init() + } + + fun getInstallationSummary() = InstallationSummary( + snapchatInfo = mappings.getSnapchatPackageInfo()?.let { + SnapchatAppInfo( + version = it.versionName, + versionCode = it.longVersionCode + ) + }, + mappingsInfo = if (mappings.isMappingsLoaded()) { + ModMappingsInfo( + generatedSnapchatVersion = mappings.getGeneratedBuildNumber(), + isOutdated = mappings.isMappingsOutdated() + ) + } else null + ) +} \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/DebugSection.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/DebugSection.kt new file mode 100644 index 000000000..b9b0f6065 --- /dev/null +++ b/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/DebugSection.kt @@ -0,0 +1,4 @@ +package me.rhunk.snapenhance.manager.sections + +class DebugSection { +} \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/FeaturesSection.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/FeaturesSection.kt new file mode 100644 index 000000000..e1968dcd8 --- /dev/null +++ b/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/FeaturesSection.kt @@ -0,0 +1,52 @@ +package me.rhunk.snapenhance.manager.sections + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material3.OutlinedCard +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import me.rhunk.snapenhance.manager.Section + +class FeaturesSection : Section() { + @Composable + @Preview + override fun Content() { + Column { + Text( + text = "Features", + modifier = Modifier.padding(all = 15.dp), + fontSize = 20.sp + ) + LazyColumn( + modifier = Modifier + .fillMaxSize(), + verticalArrangement = Arrangement.Center + ) { + items(100) { index -> + OutlinedCard( + modifier = Modifier + .fillMaxWidth() + .padding(all = 10.dp) + .height(70.dp) + ) { + Column( + modifier = Modifier.fillMaxSize(), + verticalArrangement = Arrangement.Center + ) { + Text(text = "Feature $index", modifier = Modifier.padding(all = 15.dp)) + } + } + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/HomeSection.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/HomeSection.kt new file mode 100644 index 000000000..5e89be9bf --- /dev/null +++ b/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/HomeSection.kt @@ -0,0 +1,108 @@ +package me.rhunk.snapenhance.manager.sections + +import androidx.compose.foundation.ScrollState +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ExperimentalLayoutApi +import androidx.compose.foundation.layout.FlowRow +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Map +import androidx.compose.material.icons.filled.Refresh +import androidx.compose.material3.Button +import androidx.compose.material3.Icon +import androidx.compose.material3.OutlinedCard +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import me.rhunk.snapenhance.manager.Section +import me.rhunk.snapenhance.manager.data.InstallationSummary + +class HomeSection : Section() { + companion object { + val cardMargin = 10.dp + } + + @OptIn(ExperimentalLayoutApi::class) + @Composable + private fun SummaryCards(installationSummary: InstallationSummary) { + //installation summary + OutlinedCard( + modifier = Modifier + .padding(all = cardMargin) + .fillMaxWidth() + ) { + Column(modifier = Modifier.padding(all = 16.dp)) { + if (installationSummary.snapchatInfo != null) { + Text("Snapchat version: ${installationSummary.snapchatInfo.version}") + Text("Snapchat version code: ${installationSummary.snapchatInfo.versionCode}") + } else { + Text("Snapchat not installed/detected") + } + } + } + + OutlinedCard( + modifier = Modifier + .padding(all = cardMargin) + .fillMaxWidth() + ) { + FlowRow( + modifier = Modifier.padding(all = 16.dp), + horizontalArrangement = Arrangement.SpaceBetween + ) { + Icon( + Icons.Filled.Map, + contentDescription = "Mappings", + modifier = Modifier + .padding(end = 10.dp) + .align(Alignment.CenterVertically) + ) + + Text(text = if (installationSummary.mappingsInfo == null || installationSummary.mappingsInfo.isOutdated) { + "Mappings ${if (installationSummary.mappingsInfo == null) "not generated" else "outdated"}" + } else { + "Mappings version ${installationSummary.mappingsInfo.generatedSnapchatVersion}" + }, modifier = Modifier.weight(1f) + .align(Alignment.CenterVertically) + ) + + //inline button + Button(onClick = {}, modifier = Modifier.height(40.dp)) { + Icon(Icons.Filled.Refresh, contentDescription = "Refresh") + } + } + } + } + + @Composable + @Preview + override fun Content() { + Column( + modifier = Modifier + .fillMaxSize() + .verticalScroll(ScrollState(0)) + ) { + Text( + "SnapEnhance", + fontSize = 32.sp, + modifier = Modifier.padding(32.dp) + ) + + Text( + text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec euismod, nisl eget ultricies ultrices, nunc nisl aliquam nunc, quis aliquam nisl nunc eu nisl. Donec euismod, nisl eget ultricies ultrices, nunc nisl aliquam nunc, quis aliquam nisl nunc eu nisl.", + modifier = Modifier.padding(16.dp) + ) + + SummaryCards(manager.getInstallationSummary()) + } + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/NotImplemented.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/NotImplemented.kt index 272ef5290..d5f2f65e4 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/NotImplemented.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/NotImplemented.kt @@ -1,18 +1,20 @@ package me.rhunk.snapenhance.manager.sections +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import me.rhunk.snapenhance.manager.Section -@Composable -fun NotImplemented() { - Column( - modifier = Modifier.fillMaxSize(), - horizontalAlignment = Alignment.CenterHorizontally, - ) { - Text(text = "Not Implemented") +class NotImplemented : Section() { + @Composable + override fun Content() { + Column { + Text(text = "Not implemented yet!") + } } } \ No newline at end of file diff --git a/app/src/main/res/drawable/launcher_icon_monochrome.xml b/app/src/main/res/drawable/launcher_icon_monochrome.xml new file mode 100644 index 000000000..d98ea0b70 --- /dev/null +++ b/app/src/main/res/drawable/launcher_icon_monochrome.xml @@ -0,0 +1,26 @@ + + + + + + + + + + diff --git a/core/src/main/res/mipmap-anydpi-v26/launcher_icon.xml b/app/src/main/res/mipmap-anydpi-v26/launcher_icon.xml similarity index 78% rename from core/src/main/res/mipmap-anydpi-v26/launcher_icon.xml rename to app/src/main/res/mipmap-anydpi-v26/launcher_icon.xml index ae6391b6d..0a1cdd2d3 100644 --- a/core/src/main/res/mipmap-anydpi-v26/launcher_icon.xml +++ b/app/src/main/res/mipmap-anydpi-v26/launcher_icon.xml @@ -2,4 +2,5 @@ + \ No newline at end of file diff --git a/core/src/main/res/mipmap-anydpi-v26/launcher_icon_round.xml b/app/src/main/res/mipmap-anydpi-v26/launcher_icon_round.xml similarity index 100% rename from core/src/main/res/mipmap-anydpi-v26/launcher_icon_round.xml rename to app/src/main/res/mipmap-anydpi-v26/launcher_icon_round.xml diff --git a/core/src/main/res/mipmap-hdpi/launcher_icon_foreground.png b/app/src/main/res/mipmap-hdpi/launcher_icon_foreground.png similarity index 100% rename from core/src/main/res/mipmap-hdpi/launcher_icon_foreground.png rename to app/src/main/res/mipmap-hdpi/launcher_icon_foreground.png diff --git a/core/src/main/res/mipmap-hdpi/launcher_icon_round.png b/app/src/main/res/mipmap-hdpi/launcher_icon_round.png similarity index 100% rename from core/src/main/res/mipmap-hdpi/launcher_icon_round.png rename to app/src/main/res/mipmap-hdpi/launcher_icon_round.png diff --git a/core/src/main/res/mipmap-mdpi/launcher_icon_foreground.png b/app/src/main/res/mipmap-mdpi/launcher_icon_foreground.png similarity index 100% rename from core/src/main/res/mipmap-mdpi/launcher_icon_foreground.png rename to app/src/main/res/mipmap-mdpi/launcher_icon_foreground.png diff --git a/core/src/main/res/mipmap-mdpi/launcher_icon_round.png b/app/src/main/res/mipmap-mdpi/launcher_icon_round.png similarity index 100% rename from core/src/main/res/mipmap-mdpi/launcher_icon_round.png rename to app/src/main/res/mipmap-mdpi/launcher_icon_round.png diff --git a/core/src/main/res/mipmap-xhdpi/launcher_icon_foreground.png b/app/src/main/res/mipmap-xhdpi/launcher_icon_foreground.png similarity index 100% rename from core/src/main/res/mipmap-xhdpi/launcher_icon_foreground.png rename to app/src/main/res/mipmap-xhdpi/launcher_icon_foreground.png diff --git a/core/src/main/res/mipmap-xhdpi/launcher_icon_round.png b/app/src/main/res/mipmap-xhdpi/launcher_icon_round.png similarity index 100% rename from core/src/main/res/mipmap-xhdpi/launcher_icon_round.png rename to app/src/main/res/mipmap-xhdpi/launcher_icon_round.png diff --git a/core/src/main/res/mipmap-xxhdpi/launcher_icon_foreground.png b/app/src/main/res/mipmap-xxhdpi/launcher_icon_foreground.png similarity index 100% rename from core/src/main/res/mipmap-xxhdpi/launcher_icon_foreground.png rename to app/src/main/res/mipmap-xxhdpi/launcher_icon_foreground.png diff --git a/core/src/main/res/mipmap-xxhdpi/launcher_icon_round.png b/app/src/main/res/mipmap-xxhdpi/launcher_icon_round.png similarity index 100% rename from core/src/main/res/mipmap-xxhdpi/launcher_icon_round.png rename to app/src/main/res/mipmap-xxhdpi/launcher_icon_round.png diff --git a/core/src/main/res/mipmap-xxxhdpi/launcher_icon_foreground.png b/app/src/main/res/mipmap-xxxhdpi/launcher_icon_foreground.png similarity index 100% rename from core/src/main/res/mipmap-xxxhdpi/launcher_icon_foreground.png rename to app/src/main/res/mipmap-xxxhdpi/launcher_icon_foreground.png diff --git a/core/src/main/res/mipmap-xxxhdpi/launcher_icon_round.png b/app/src/main/res/mipmap-xxxhdpi/launcher_icon_round.png similarity index 100% rename from core/src/main/res/mipmap-xxxhdpi/launcher_icon_round.png rename to app/src/main/res/mipmap-xxxhdpi/launcher_icon_round.png diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/bridge/BridgeClient.kt b/core/src/main/kotlin/me/rhunk/snapenhance/bridge/BridgeClient.kt index 3dc95eba9..6c9dd3f09 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/bridge/BridgeClient.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/bridge/BridgeClient.kt @@ -91,7 +91,6 @@ class BridgeClient( fun deleteFile(fileType: BridgeFileType) = service.deleteFile(fileType.value) - fun isFileExists(fileType: BridgeFileType) = service.isFileExists(fileType.value) fun getLoggedMessageIds(conversationId: String, limit: Int): LongArray = service.getLoggedMessageIds(conversationId, limit) diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/bridge/FileLoaderWrapper.kt b/core/src/main/kotlin/me/rhunk/snapenhance/bridge/FileLoaderWrapper.kt new file mode 100644 index 000000000..a98e597a5 --- /dev/null +++ b/core/src/main/kotlin/me/rhunk/snapenhance/bridge/FileLoaderWrapper.kt @@ -0,0 +1,35 @@ +package me.rhunk.snapenhance.bridge + +import android.content.Context +import me.rhunk.snapenhance.bridge.types.BridgeFileType + +open class FileLoaderWrapper( + private val fileType: BridgeFileType, + private val defaultContent: ByteArray +) { + lateinit var isFileExists: () -> Boolean + lateinit var write: (ByteArray) -> Unit + lateinit var read: () -> ByteArray + lateinit var delete: () -> Unit + + fun loadFromContext(context: Context) { + val file = fileType.resolve(context) + isFileExists = { file.exists() } + read = { + if (!file.exists()) { + file.createNewFile() + file.writeBytes("{}".toByteArray(Charsets.UTF_8)) + } + file.readBytes() + } + write = { file.writeBytes(it) } + delete = { file.delete() } + } + + fun loadFromBridge(bridgeClient: BridgeClient) { + isFileExists = { bridgeClient.isFileExists(fileType) } + read = { bridgeClient.createAndReadFile(fileType, defaultContent) } + write = { bridgeClient.writeFile(fileType, it) } + delete = { bridgeClient.deleteFile(fileType) } + } +} \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/ConfigWrapper.kt b/core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/ConfigWrapper.kt index df338ac4a..4b9fc6c0d 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/ConfigWrapper.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/ConfigWrapper.kt @@ -5,6 +5,7 @@ import com.google.gson.GsonBuilder import com.google.gson.JsonObject import me.rhunk.snapenhance.Logger import me.rhunk.snapenhance.bridge.BridgeClient +import me.rhunk.snapenhance.bridge.FileLoaderWrapper import me.rhunk.snapenhance.bridge.types.BridgeFileType import me.rhunk.snapenhance.config.ConfigAccessor import me.rhunk.snapenhance.config.ConfigProperty @@ -14,16 +15,14 @@ class ConfigWrapper: ConfigAccessor() { private val gson = GsonBuilder().setPrettyPrinting().create() } - private lateinit var isFileExistsAction: () -> Boolean - private lateinit var writeFileAction: (ByteArray) -> Unit - private lateinit var readFileAction: () -> ByteArray + private val file = FileLoaderWrapper(BridgeFileType.CONFIG, "{}".toByteArray(Charsets.UTF_8)) fun load() { ConfigProperty.sortedByCategory().forEach { key -> set(key, key.valueContainer) } - if (!isFileExistsAction()) { + if (!file.isFileExists()) { writeConfig() return } @@ -37,7 +36,7 @@ class ConfigWrapper: ConfigAccessor() { } private fun loadConfig() { - val configContent = readFileAction() + val configContent = file.read() val configObject: JsonObject = gson.fromJson( configContent.toString(Charsets.UTF_8), @@ -54,27 +53,17 @@ class ConfigWrapper: ConfigAccessor() { entries().forEach { (key, value) -> configObject.addProperty(key.name, value.read()) } - writeFileAction(gson.toJson(configObject).toByteArray(Charsets.UTF_8)) + + file.write(gson.toJson(configObject).toByteArray(Charsets.UTF_8)) } fun loadFromContext(context: Context) { - val configFile = BridgeFileType.CONFIG.resolve(context) - isFileExistsAction = { configFile.exists() } - readFileAction = { - if (!configFile.exists()) { - configFile.createNewFile() - configFile.writeBytes("{}".toByteArray(Charsets.UTF_8)) - } - configFile.readBytes() - } - writeFileAction = { configFile.writeBytes(it) } + file.loadFromContext(context) load() } fun loadFromBridge(bridgeClient: BridgeClient) { - isFileExistsAction = { bridgeClient.isFileExists(BridgeFileType.CONFIG) } - readFileAction = { bridgeClient.createAndReadFile(BridgeFileType.CONFIG, "{}".toByteArray(Charsets.UTF_8)) } - writeFileAction = { bridgeClient.writeFile(BridgeFileType.CONFIG, it) } + file.loadFromBridge(bridgeClient) load() } } \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/MappingsWrapper.kt b/core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/MappingsWrapper.kt new file mode 100644 index 000000000..ecf08d1fe --- /dev/null +++ b/core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/MappingsWrapper.kt @@ -0,0 +1,157 @@ +package me.rhunk.snapenhance.bridge.wrapper + +import android.content.Context +import com.google.gson.GsonBuilder +import com.google.gson.JsonElement +import com.google.gson.JsonParser +import me.rhunk.snapenhance.Constants +import me.rhunk.snapenhance.Logger +import me.rhunk.snapenhance.bridge.FileLoaderWrapper +import me.rhunk.snapenhance.bridge.types.BridgeFileType +import me.rhunk.snapmapper.Mapper +import me.rhunk.snapmapper.impl.BCryptClassMapper +import me.rhunk.snapmapper.impl.CallbackMapper +import me.rhunk.snapmapper.impl.DefaultMediaItemMapper +import me.rhunk.snapmapper.impl.EnumMapper +import me.rhunk.snapmapper.impl.FriendsFeedEventDispatcherMapper +import me.rhunk.snapmapper.impl.MediaQualityLevelProviderMapper +import me.rhunk.snapmapper.impl.OperaPageViewControllerMapper +import me.rhunk.snapmapper.impl.PlatformAnalyticsCreatorMapper +import me.rhunk.snapmapper.impl.PlusSubscriptionMapper +import me.rhunk.snapmapper.impl.ScCameraSettingsMapper +import me.rhunk.snapmapper.impl.StoryBoostStateMapper +import java.util.concurrent.ConcurrentHashMap +import kotlin.system.measureTimeMillis + +class MappingsWrapper( + private val context: Context, +) : FileLoaderWrapper(BridgeFileType.MAPPINGS, "{}".toByteArray(Charsets.UTF_8)) { + companion object { + private val gson = GsonBuilder().setPrettyPrinting().create() + private val mappers = arrayOf( + BCryptClassMapper::class, + CallbackMapper::class, + DefaultMediaItemMapper::class, + MediaQualityLevelProviderMapper::class, + EnumMapper::class, + OperaPageViewControllerMapper::class, + PlatformAnalyticsCreatorMapper::class, + PlusSubscriptionMapper::class, + ScCameraSettingsMapper::class, + StoryBoostStateMapper::class, + FriendsFeedEventDispatcherMapper::class + ) + } + + private val mappings = ConcurrentHashMap() + private var snapBuildNumber: Long = 0 + + @Suppress("deprecation") + fun init() { + snapBuildNumber = getSnapchatVersionCode() + + if (isFileExists()) { + runCatching { + loadCached() + }.onFailure { + delete() + } + } + } + + fun getSnapchatPackageInfo() = runCatching { + context.packageManager.getPackageInfo( + Constants.SNAPCHAT_PACKAGE_NAME, + 0 + ) + }.getOrNull() + + fun getSnapchatVersionCode() = getSnapchatPackageInfo()?.longVersionCode ?: -1 + fun getApplicationSourceDir() = getSnapchatPackageInfo()?.applicationInfo?.sourceDir + fun getGeneratedBuildNumber() = snapBuildNumber + + fun isMappingsOutdated(): Boolean { + return snapBuildNumber != getSnapchatVersionCode() || isMappingsLoaded().not() + } + + fun isMappingsLoaded(): Boolean { + return mappings.isNotEmpty() + } + + private fun loadCached() { + if (!isFileExists()) { + throw Exception("Mappings file does not exist") + } + val mappingsObject = JsonParser.parseString(read().toString(Charsets.UTF_8)).asJsonObject.also { + snapBuildNumber = it["snap_build_number"].asLong + } + + mappingsObject.entrySet().forEach { (key, value): Map.Entry -> + if (value.isJsonArray) { + mappings[key] = gson.fromJson(value, ArrayList::class.java) + return@forEach + } + if (value.isJsonObject) { + mappings[key] = gson.fromJson(value, ConcurrentHashMap::class.java) + return@forEach + } + mappings[key] = value.asString + } + } + + fun refresh() { + val mapper = Mapper(*mappers) + + runCatching { + mapper.loadApk(getApplicationSourceDir() ?: throw Exception("Failed to get APK")) + }.onFailure { + throw Exception("Failed to load APK", it) + } + + measureTimeMillis { + val result = mapper.start().apply { + addProperty("snap_build_number", snapBuildNumber) + } + write(result.toString().toByteArray()) + }.also { + Logger.xposedLog("Generated mappings in $it ms") + } + } + + fun getMappedObject(key: String): Any { + if (mappings.containsKey(key)) { + return mappings[key]!! + } + throw Exception("No mapping found for $key") + } + + fun getMappedObjectNullable(key: String): Any? { + return mappings[key] + } + + fun getMappedClass(className: String): Class<*> { + return context.classLoader.loadClass(getMappedObject(className) as String) + } + + fun getMappedClass(key: String, subKey: String): Class<*> { + return context.classLoader.loadClass(getMappedValue(key, subKey)) + } + + fun getMappedValue(key: String): String { + return getMappedObject(key) as String + } + + @Suppress("UNCHECKED_CAST") + fun getMappedList(key: String): List { + return listOf(getMappedObject(key) as List).flatten() + } + + fun getMappedValue(key: String, subKey: String): String { + return getMappedMap(key)[subKey] as String + } + + @Suppress("UNCHECKED_CAST") + fun getMappedMap(key: String): Map { + return getMappedObject(key) as Map + } +} \ No newline at end of file From d04ce42c7dd6618fcd7efff86196fdec6742da20 Mon Sep 17 00:00:00 2001 From: rhunk <101876869+rhunk@users.noreply.github.com> Date: Sat, 29 Jul 2023 00:31:55 +0200 Subject: [PATCH 04/81] fix: remove askForPermissions --- core/src/main/kotlin/me/rhunk/snapenhance/SharedContext.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/SharedContext.kt b/core/src/main/kotlin/me/rhunk/snapenhance/SharedContext.kt index 34da6bf49..2b1904ac5 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/SharedContext.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/SharedContext.kt @@ -80,6 +80,6 @@ object SharedContext { config = ConfigWrapper().apply { loadFromContext(context) } } - askForPermissions(context) + //askForPermissions(context) } } \ No newline at end of file From 5e41ccffd7d15351597959c3f5e253b4c67ff81d Mon Sep 17 00:00:00 2001 From: rhunk <101876869+rhunk@users.noreply.github.com> Date: Sat, 29 Jul 2023 00:33:09 +0200 Subject: [PATCH 05/81] ui: feature section preview --- .../rhunk/snapenhance/manager/MainActivity.kt | 3 +- .../manager/data/ManagerContext.kt | 6 +- .../manager/sections/FeaturesSection.kt | 139 ++++++++++++++++-- .../bridge/wrapper/TranslationWrapper.kt | 9 ++ 4 files changed, 137 insertions(+), 20 deletions(-) diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/MainActivity.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/MainActivity.kt index 775a00618..5c40d6ee3 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/manager/MainActivity.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/manager/MainActivity.kt @@ -27,9 +27,10 @@ class MainActivity : ComponentActivity() { @SuppressLint("UnusedMaterialScaffoldPaddingParameter") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + val managerContext = ManagerContext(this) setContent { - App(ManagerContext(this)) + App(managerContext) } } } diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/data/ManagerContext.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/data/ManagerContext.kt index 487a79074..30702a755 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/manager/data/ManagerContext.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/manager/data/ManagerContext.kt @@ -8,9 +8,9 @@ import me.rhunk.snapenhance.bridge.wrapper.TranslationWrapper class ManagerContext( private val context: Context ) { - private val config = ConfigWrapper() - private val translation = TranslationWrapper() - private val mappings = MappingsWrapper(context) + val config = ConfigWrapper() + val translation = TranslationWrapper() + val mappings = MappingsWrapper(context) init { config.loadFromContext(context) diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/FeaturesSection.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/FeaturesSection.kt index e1968dcd8..12960acd2 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/FeaturesSection.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/FeaturesSection.kt @@ -1,25 +1,144 @@ package me.rhunk.snapenhance.manager.sections +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ExperimentalLayoutApi +import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.material3.OutlinedCard +import androidx.compose.foundation.lazy.items +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.OpenInNew +import androidx.compose.material3.Card +import androidx.compose.material3.FilledIconButton +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Switch import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import me.rhunk.snapenhance.config.ConfigProperty +import me.rhunk.snapenhance.config.impl.ConfigIntegerValue +import me.rhunk.snapenhance.config.impl.ConfigStateListValue +import me.rhunk.snapenhance.config.impl.ConfigStateSelection +import me.rhunk.snapenhance.config.impl.ConfigStateValue +import me.rhunk.snapenhance.config.impl.ConfigStringValue import me.rhunk.snapenhance.manager.Section +typealias ClickCallback = (Boolean) -> Unit +typealias AddClickCallback = (ClickCallback) -> ClickCallback + class FeaturesSection : Section() { + @Composable + private fun PropertyAction(item: ConfigProperty, clickCallback: AddClickCallback) { + when (val configValueContainer = remember { item.valueContainer }) { + is ConfigStateValue -> { + val state = remember { + mutableStateOf(configValueContainer.value()) + } + + Switch( + checked = state.value, + onCheckedChange = clickCallback { + state.value = !state.value + configValueContainer.writeFrom(state.value.toString()) + } + ) + } + + is ConfigStateSelection -> { + Text( + text = configValueContainer.value().let { + it.substring(0, it.length.coerceAtMost(20)) + } + ) + } + + is ConfigStateListValue -> { + IconButton(onClick = { }) { + Icon(Icons.Filled.OpenInNew, contentDescription = null) + } + } + + is ConfigIntegerValue -> { + FilledIconButton(onClick = { }) { + Text(text = configValueContainer.value().toString()) + } + } + + is ConfigStringValue -> { + Text( + text = configValueContainer.value().let { + it.substring(0, it.length.coerceAtMost(20)) + } + ) + } + } + } + + @OptIn(ExperimentalLayoutApi::class) + @Composable + private fun PropertyCard(item: ConfigProperty) { + val clickCallback = remember { mutableStateOf(null) } + Card( + modifier = Modifier + .fillMaxWidth() + .clickable { + clickCallback.value?.invoke(true) + } + .padding(start = 10.dp, end = 10.dp, top = 5.dp, bottom = 5.dp) + ) { + FlowRow( + modifier = Modifier + .fillMaxSize() + .padding(all = 10.dp), + horizontalArrangement = Arrangement.SpaceBetween + ) { + Column( + modifier = Modifier + .align(Alignment.CenterVertically) + .weight(1f, fill = true) + .padding(all = 10.dp) + ) { + Text(text = manager.translation.propertyName(item), fontSize = 16.sp) + Text( + text = manager.translation.propertyDescription(item), + fontSize = 12.sp, + lineHeight = 15.sp + ) + } + + Column( + modifier = Modifier + .align(Alignment.CenterVertically) + .padding(all = 10.dp) + ) { + PropertyAction(item, clickCallback = { callback -> + clickCallback.value = callback + callback + }) + } + } + } + } + + + @Composable @Preview override fun Content() { + val configItems = remember { + ConfigProperty.sortedByCategory() + } Column { Text( text = "Features", @@ -31,20 +150,8 @@ class FeaturesSection : Section() { .fillMaxSize(), verticalArrangement = Arrangement.Center ) { - items(100) { index -> - OutlinedCard( - modifier = Modifier - .fillMaxWidth() - .padding(all = 10.dp) - .height(70.dp) - ) { - Column( - modifier = Modifier.fillMaxSize(), - verticalArrangement = Arrangement.Center - ) { - Text(text = "Feature $index", modifier = Modifier.padding(all = 15.dp)) - } - } + items(configItems) { item -> + PropertyCard(item) } } } diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/TranslationWrapper.kt b/core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/TranslationWrapper.kt index 9bd2cc671..d8c915724 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/TranslationWrapper.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/TranslationWrapper.kt @@ -5,6 +5,7 @@ import com.google.gson.JsonObject import com.google.gson.JsonParser import me.rhunk.snapenhance.Logger import me.rhunk.snapenhance.bridge.BridgeClient +import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.data.LocalePair import java.util.Locale @@ -79,6 +80,14 @@ class TranslationWrapper { return translationMap[key] ?: key.also { Logger.debug("Missing translation for $key") } } + fun propertyName(property: ConfigProperty): String { + return get("property.${property.translationKey}.name") + } + + fun propertyDescription(property: ConfigProperty): String { + return get("property.${property.translationKey}.description") + } + fun format(key: String, vararg args: Pair): String { return args.fold(get(key)) { acc, pair -> acc.replace("{${pair.first}}", pair.second) From 5278c912e0ada1080d3b37ba43ec9f65067a01bf Mon Sep 17 00:00:00 2001 From: rhunk <101876869+rhunk@users.noreply.github.com> Date: Sat, 29 Jul 2023 01:39:29 +0200 Subject: [PATCH 06/81] ui: save config --- .../manager/sections/FeaturesSection.kt | 70 ++++++++++++++----- .../bridge/wrapper/ConfigWrapper.kt | 4 ++ 2 files changed, 58 insertions(+), 16 deletions(-) diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/FeaturesSection.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/FeaturesSection.kt index 12960acd2..37fbeb6e6 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/FeaturesSection.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/FeaturesSection.kt @@ -10,22 +10,32 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.MaterialTheme +import androidx.compose.material.SnackbarHost import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.OpenInNew +import androidx.compose.material.icons.rounded.Save +import androidx.compose.material.rememberScaffoldState import androidx.compose.material3.Card import androidx.compose.material3.FilledIconButton +import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.Icon import androidx.compose.material3.IconButton +import androidx.compose.material3.Scaffold import androidx.compose.material3.Switch import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import kotlinx.coroutines.launch import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.config.impl.ConfigIntegerValue import me.rhunk.snapenhance.config.impl.ConfigStateListValue @@ -109,7 +119,7 @@ class FeaturesSection : Section() { .weight(1f, fill = true) .padding(all = 10.dp) ) { - Text(text = manager.translation.propertyName(item), fontSize = 16.sp) + Text(text = manager.translation.propertyName(item), fontSize = 16.sp, fontWeight = FontWeight.Bold) Text( text = manager.translation.propertyDescription(item), fontSize = 12.sp, @@ -132,28 +142,56 @@ class FeaturesSection : Section() { } - @Composable @Preview override fun Content() { val configItems = remember { ConfigProperty.sortedByCategory() } - Column { - Text( - text = "Features", - modifier = Modifier.padding(all = 15.dp), - fontSize = 20.sp - ) - LazyColumn( - modifier = Modifier - .fillMaxSize(), - verticalArrangement = Arrangement.Center - ) { - items(configItems) { item -> - PropertyCard(item) + val scope = rememberCoroutineScope() + val scaffoldState = rememberScaffoldState() + Scaffold( + snackbarHost = { SnackbarHost(scaffoldState.snackbarHostState) }, + floatingActionButton = { + FloatingActionButton( + onClick = { + manager.config.save() + scope.launch { + scaffoldState.snackbarHostState.showSnackbar("Saved") + } + }, + containerColor = MaterialTheme.colors.primary, + shape = RoundedCornerShape(16.dp), + ) { + Icon( + imageVector = Icons.Rounded.Save, + contentDescription = null + ) + } + }, + modifier = Modifier.fillMaxSize(), + content = { innerPadding -> + Column( + modifier = Modifier + .fillMaxSize() + .padding(innerPadding) + ) { + Text( + text = "Features", + modifier = Modifier.padding(all = 10.dp), + fontSize = 20.sp + ) + LazyColumn( + modifier = Modifier + .fillMaxSize(), + verticalArrangement = Arrangement.Center + ) { + items(configItems) { item -> + PropertyCard(item) + } + } } } - } + ) } } \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/ConfigWrapper.kt b/core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/ConfigWrapper.kt index 4b9fc6c0d..47c1bc212 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/ConfigWrapper.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/ConfigWrapper.kt @@ -35,6 +35,10 @@ class ConfigWrapper: ConfigAccessor() { } } + fun save() { + writeConfig() + } + private fun loadConfig() { val configContent = file.read() From d3434a4be26c7b894ace3914c5aa12afd6f7e430 Mon Sep 17 00:00:00 2001 From: rhunk <101876869+rhunk@users.noreply.github.com> Date: Sun, 30 Jul 2023 17:28:48 +0200 Subject: [PATCH 07/81] ui(features): dialogs & save button --- .../me/rhunk/snapenhance/manager/Dialogs.kt | 176 ++++++++++++++++++ .../rhunk/snapenhance/manager/Navigation.kt | 3 +- .../manager/sections/FeaturesSection.kt | 85 ++++++--- 3 files changed, 231 insertions(+), 33 deletions(-) create mode 100644 app/src/main/kotlin/me/rhunk/snapenhance/manager/Dialogs.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/Dialogs.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/Dialogs.kt new file mode 100644 index 000000000..dcb2276cd --- /dev/null +++ b/app/src/main/kotlin/me/rhunk/snapenhance/manager/Dialogs.kt @@ -0,0 +1,176 @@ +package me.rhunk.snapenhance.manager + +import androidx.compose.foundation.ScrollState +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Button +import androidx.compose.material3.Card +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.RadioButton +import androidx.compose.material3.Switch +import androidx.compose.material3.Text +import androidx.compose.material3.TextField +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import me.rhunk.snapenhance.config.ConfigProperty +import me.rhunk.snapenhance.config.impl.ConfigIntegerValue +import me.rhunk.snapenhance.config.impl.ConfigStateListValue +import me.rhunk.snapenhance.config.impl.ConfigStateSelection +import me.rhunk.snapenhance.config.impl.ConfigStringValue + + +@Composable +@Preview +fun TestPreview() { + KeyboardInputDialog(config = ConfigProperty.SAVE_FOLDER) +} + +@Composable +fun DefaultDialogCard(content: @Composable ColumnScope.() -> Unit) { + Card( + shape = MaterialTheme.shapes.medium, + modifier = Modifier + .padding(10.dp, 5.dp, 10.dp, 10.dp), + ) { + Column( + modifier = Modifier + .padding(10.dp, 10.dp, 10.dp, 10.dp) + .verticalScroll(ScrollState(0)), + ) { content() } + } +} + +@Composable +fun DefaultEntryText(text: String, modifier: Modifier = Modifier) { + Text( + text = text, + modifier = Modifier + .padding(10.dp, 10.dp, 10.dp, 10.dp) + .then(modifier) + ) +} + +@Composable +fun StateSelectionDialog(config: ConfigProperty) { + assert(config.valueContainer is ConfigStateSelection) + val keys = (config.valueContainer as ConfigStateSelection).keys() + val selectedValue = remember { + mutableStateOf(config.valueContainer.value()) + } + DefaultDialogCard { + keys.forEach { item -> + fun select() { + selectedValue.value = item + config.valueContainer.writeFrom(item) + } + + Row( + modifier = Modifier.clickable { select() }, + verticalAlignment = Alignment.CenterVertically + ) { + DefaultEntryText( + text = item, + modifier = Modifier.weight(1f) + ) + RadioButton( + selected = selectedValue.value == item, + onClick = { select() } + ) + } + } + } +} + +@Composable +fun KeyboardInputDialog(config: ConfigProperty, dismiss: () -> Unit = {}) { + val focusRequester = remember { FocusRequester() } + + DefaultDialogCard { + val fieldValue = remember { mutableStateOf(config.valueContainer.read()) } + TextField( + modifier = Modifier.fillMaxWidth().padding(all = 10.dp).focusRequester(focusRequester), + value = fieldValue.value, onValueChange = { + fieldValue.value = it + }, + keyboardOptions = when (config.valueContainer) { + is ConfigIntegerValue -> { + KeyboardOptions(keyboardType = KeyboardType.Number) + } + else -> { + KeyboardOptions(keyboardType = KeyboardType.Text) + } + }, + singleLine = true + ) + + Row( + modifier = Modifier.padding(top = 10.dp).fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceEvenly, + ) { + Button(onClick = { dismiss() }) { + Text(text = "Cancel") + } + Button(onClick = { + config.valueContainer.writeFrom(fieldValue.value) + dismiss() + }) { + Text(text = "Ok") + } + } + } + + LaunchedEffect(Unit) { + focusRequester.requestFocus() + } +} + +@Composable +fun StateListDialog(config: ConfigProperty) { + assert(config.valueContainer is ConfigStateListValue) + val stateList = (config.valueContainer as ConfigStateListValue).value() + DefaultDialogCard { + stateList.keys.forEach { key -> + val state = remember { + mutableStateOf(stateList[key] ?: false) + } + + fun toggle(value: Boolean? = null) { + state.value = value ?: !state.value + stateList[key] = state.value + } + + Row( + modifier = Modifier.clickable { toggle() }, + verticalAlignment = Alignment.CenterVertically + ) { + DefaultEntryText( + text = key, + modifier = Modifier + .weight(1f) + ) + Switch( + checked = state.value, + onCheckedChange = { + toggle(it) + } + ) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/Navigation.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/Navigation.kt index c93b41178..3d49da177 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/manager/Navigation.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/manager/Navigation.kt @@ -39,9 +39,8 @@ class Navigation( instance.manager = context instance.navController = navController } } - val homeSection = EnumSection.HOME - NavHost(navController, startDestination = homeSection.route, Modifier.padding(innerPadding)) { + NavHost(navController, startDestination = EnumSection.FEATURES.route, Modifier.padding(innerPadding)) { sections.forEach { (section, instance) -> composable(section.route) { instance.Content() diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/FeaturesSection.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/FeaturesSection.kt index 37fbeb6e6..8f9ae8f1c 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/FeaturesSection.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/FeaturesSection.kt @@ -3,8 +3,7 @@ package me.rhunk.snapenhance.manager.sections import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.ExperimentalLayoutApi -import androidx.compose.foundation.layout.FlowRow +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding @@ -35,6 +34,8 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import androidx.compose.ui.window.Dialog +import androidx.compose.ui.window.DialogProperties import kotlinx.coroutines.launch import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.config.impl.ConfigIntegerValue @@ -42,60 +43,81 @@ import me.rhunk.snapenhance.config.impl.ConfigStateListValue import me.rhunk.snapenhance.config.impl.ConfigStateSelection import me.rhunk.snapenhance.config.impl.ConfigStateValue import me.rhunk.snapenhance.config.impl.ConfigStringValue +import me.rhunk.snapenhance.manager.StateListDialog import me.rhunk.snapenhance.manager.Section +import me.rhunk.snapenhance.manager.StateSelectionDialog +import me.rhunk.snapenhance.manager.KeyboardInputDialog typealias ClickCallback = (Boolean) -> Unit -typealias AddClickCallback = (ClickCallback) -> ClickCallback +typealias RegisterClickCallback = (ClickCallback) -> ClickCallback class FeaturesSection : Section() { @Composable - private fun PropertyAction(item: ConfigProperty, clickCallback: AddClickCallback) { - when (val configValueContainer = remember { item.valueContainer }) { - is ConfigStateValue -> { - val state = remember { - mutableStateOf(configValueContainer.value()) - } + private fun PropertyAction(item: ConfigProperty, registerClickCallback: RegisterClickCallback) { + val showDialog = remember { mutableStateOf(false) } + val dialogComposable = remember { mutableStateOf<@Composable () -> Unit>({})} + + fun registerDialogOnClickCallback() = registerClickCallback { + showDialog.value = true + } + if (showDialog.value) { + Dialog(onDismissRequest = { showDialog.value = false }, properties = DialogProperties()) { + dialogComposable.value() + } + } + + when (val container = remember { item.valueContainer }) { + is ConfigStateValue -> { + val state = remember { mutableStateOf(container.value()) } Switch( checked = state.value, - onCheckedChange = clickCallback { - state.value = !state.value - configValueContainer.writeFrom(state.value.toString()) + onCheckedChange = registerClickCallback { + state.value = state.value.not() + container.writeFrom(state.value.toString()) } ) } is ConfigStateSelection -> { + registerDialogOnClickCallback() + dialogComposable.value = { + StateSelectionDialog(item) + } Text( - text = configValueContainer.value().let { + text = container.value().let { it.substring(0, it.length.coerceAtMost(20)) } ) } - is ConfigStateListValue -> { - IconButton(onClick = { }) { - Icon(Icons.Filled.OpenInNew, contentDescription = null) - } - } - - is ConfigIntegerValue -> { - FilledIconButton(onClick = { }) { - Text(text = configValueContainer.value().toString()) + is ConfigStateListValue, is ConfigStringValue, is ConfigIntegerValue -> { + dialogComposable.value = { + when (container) { + is ConfigStateListValue -> { + StateListDialog(item) + } + is ConfigStringValue, is ConfigIntegerValue -> { + KeyboardInputDialog(item) { showDialog.value = false } + } + } } - } - is ConfigStringValue -> { - Text( - text = configValueContainer.value().let { - it.substring(0, it.length.coerceAtMost(20)) + registerDialogOnClickCallback().let { { it.invoke(true) } }.also { + if (container is ConfigIntegerValue) { + FilledIconButton(onClick = it) { + Text(text = container.value().toString()) + } + } else { + IconButton(onClick = it) { + Icon(Icons.Filled.OpenInNew, contentDescription = null) + } } - ) + } } } } - @OptIn(ExperimentalLayoutApi::class) @Composable private fun PropertyCard(item: ConfigProperty) { val clickCallback = remember { mutableStateOf(null) } @@ -107,7 +129,7 @@ class FeaturesSection : Section() { } .padding(start = 10.dp, end = 10.dp, top = 5.dp, bottom = 5.dp) ) { - FlowRow( + Row( modifier = Modifier .fillMaxSize() .padding(all = 10.dp), @@ -132,7 +154,7 @@ class FeaturesSection : Section() { .align(Alignment.CenterVertically) .padding(all = 10.dp) ) { - PropertyAction(item, clickCallback = { callback -> + PropertyAction(item, registerClickCallback = { callback -> clickCallback.value = callback callback }) @@ -161,6 +183,7 @@ class FeaturesSection : Section() { } }, containerColor = MaterialTheme.colors.primary, + contentColor = MaterialTheme.colors.onPrimary, shape = RoundedCornerShape(16.dp), ) { Icon( From 5d4e2aacb1dfc682e8fa0f9adbd9183c7b023104 Mon Sep 17 00:00:00 2001 From: rhunk <101876869+rhunk@users.noreply.github.com> Date: Mon, 31 Jul 2023 00:53:55 +0200 Subject: [PATCH 08/81] ui(features): dialogs & translations --- app/build.gradle.kts | 12 +- .../me/rhunk/snapenhance/manager/Dialogs.kt | 254 +++++++++--------- .../rhunk/snapenhance/manager/MainActivity.kt | 42 +-- .../rhunk/snapenhance/manager/Navigation.kt | 3 +- .../me/rhunk/snapenhance/manager/Section.kt | 6 + .../manager/sections/FeaturesSection.kt | 32 ++- .../manager/sections/NotImplemented.kt | 5 - .../bridge/wrapper/TranslationWrapper.kt | 4 + .../ui/menu/impl/SettingsGearInjector.kt | 5 +- 9 files changed, 186 insertions(+), 177 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 3bfb6c14d..ad029fc34 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -22,8 +22,7 @@ android { defaultConfig { applicationId = rootProject.ext["applicationId"].toString() minSdk = 28 - //noinspection OldTargetApi - targetSdk = 33 + targetSdk = 34 multiDexEnabled = true } @@ -37,11 +36,12 @@ android { flavorDimensions += "abi" productFlavors { + + create("armv8") { ndk { abiFilters.add("arm64-v8a") } - dimension = "abi" } @@ -81,13 +81,13 @@ android { dependencies { implementation(project(":core")) implementation(libs.androidx.material.icons.extended) - implementation(libs.androidx.navigation.compose) - implementation(libs.androidx.activity.ktx) implementation(libs.androidx.material3) implementation(libs.androidx.material) + implementation(libs.androidx.activity.ktx) + implementation(libs.androidx.navigation.compose) debugImplementation("androidx.compose.ui:ui-tooling:1.4.3") - implementation("androidx.compose.ui:ui-tooling-preview:1.4.3") + debugImplementation("androidx.compose.ui:ui-tooling-preview:1.4.3") implementation(kotlin("reflect")) } diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/Dialogs.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/Dialogs.kt index dcb2276cd..586c9b667 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/manager/Dialogs.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/manager/Dialogs.kt @@ -18,159 +18,173 @@ import androidx.compose.material3.Switch import androidx.compose.material3.Text import androidx.compose.material3.TextField import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester +import androidx.compose.ui.layout.onGloballyPositioned +import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.input.KeyboardType -import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.unit.dp import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.config.impl.ConfigIntegerValue import me.rhunk.snapenhance.config.impl.ConfigStateListValue import me.rhunk.snapenhance.config.impl.ConfigStateSelection -import me.rhunk.snapenhance.config.impl.ConfigStringValue +import me.rhunk.snapenhance.manager.data.ManagerContext -@Composable -@Preview -fun TestPreview() { - KeyboardInputDialog(config = ConfigProperty.SAVE_FOLDER) -} +class Dialogs( + private val context: ManagerContext +) { + @Composable + fun DefaultDialogCard(content: @Composable ColumnScope.() -> Unit) { + Card( + shape = MaterialTheme.shapes.medium, + modifier = Modifier + .padding(10.dp, 5.dp, 10.dp, 10.dp), + ) { + Column( + modifier = Modifier + .padding(10.dp, 10.dp, 10.dp, 10.dp) + .verticalScroll(ScrollState(0)), + ) { content() } + } + } -@Composable -fun DefaultDialogCard(content: @Composable ColumnScope.() -> Unit) { - Card( - shape = MaterialTheme.shapes.medium, - modifier = Modifier - .padding(10.dp, 5.dp, 10.dp, 10.dp), - ) { - Column( + @Composable + fun DefaultEntryText(text: String, modifier: Modifier = Modifier) { + Text( + text = text, modifier = Modifier .padding(10.dp, 10.dp, 10.dp, 10.dp) - .verticalScroll(ScrollState(0)), - ) { content() } + .then(modifier) + ) } -} - -@Composable -fun DefaultEntryText(text: String, modifier: Modifier = Modifier) { - Text( - text = text, - modifier = Modifier - .padding(10.dp, 10.dp, 10.dp, 10.dp) - .then(modifier) - ) -} -@Composable -fun StateSelectionDialog(config: ConfigProperty) { - assert(config.valueContainer is ConfigStateSelection) - val keys = (config.valueContainer as ConfigStateSelection).keys() - val selectedValue = remember { - mutableStateOf(config.valueContainer.value()) - } - DefaultDialogCard { - keys.forEach { item -> - fun select() { - selectedValue.value = item - config.valueContainer.writeFrom(item) - } + @Composable + fun StateSelectionDialog(config: ConfigProperty) { + assert(config.valueContainer is ConfigStateSelection) + val keys = (config.valueContainer as ConfigStateSelection).keys() + val selectedValue = remember { + mutableStateOf(config.valueContainer.value()) + } + DefaultDialogCard { + keys.forEach { item -> + fun select() { + selectedValue.value = item + config.valueContainer.writeFrom(item) + } - Row( - modifier = Modifier.clickable { select() }, - verticalAlignment = Alignment.CenterVertically - ) { - DefaultEntryText( - text = item, - modifier = Modifier.weight(1f) - ) - RadioButton( - selected = selectedValue.value == item, - onClick = { select() } - ) + Row( + modifier = Modifier.clickable { select() }, + verticalAlignment = Alignment.CenterVertically + ) { + DefaultEntryText( + text = if (config.disableValueLocalization) + item + else context.translation.propertyOption(config, item), + modifier = Modifier.weight(1f) + ) + RadioButton( + selected = selectedValue.value == item, + onClick = { select() } + ) + } } } } -} -@Composable -fun KeyboardInputDialog(config: ConfigProperty, dismiss: () -> Unit = {}) { - val focusRequester = remember { FocusRequester() } + @Composable + fun KeyboardInputDialog(config: ConfigProperty, dismiss: () -> Unit = {}) { + val focusRequester = remember { FocusRequester() } + + DefaultDialogCard { + val fieldValue = remember { + mutableStateOf(config.valueContainer.read().let { + TextFieldValue( + text = it, + selection = TextRange(it.length) + ) + }) + } - DefaultDialogCard { - val fieldValue = remember { mutableStateOf(config.valueContainer.read()) } - TextField( - modifier = Modifier.fillMaxWidth().padding(all = 10.dp).focusRequester(focusRequester), - value = fieldValue.value, onValueChange = { - fieldValue.value = it - }, - keyboardOptions = when (config.valueContainer) { - is ConfigIntegerValue -> { - KeyboardOptions(keyboardType = KeyboardType.Number) + TextField( + modifier = Modifier + .fillMaxWidth() + .padding(all = 10.dp) + .onGloballyPositioned { + focusRequester.requestFocus() + } + .focusRequester(focusRequester), + value = fieldValue.value, + onValueChange = { + fieldValue.value = it + }, + keyboardOptions = when (config.valueContainer) { + is ConfigIntegerValue -> { + KeyboardOptions(keyboardType = KeyboardType.Number) + } + else -> { + KeyboardOptions(keyboardType = KeyboardType.Text) + } + }, + singleLine = true + ) + + Row( + modifier = Modifier.padding(top = 10.dp).fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceEvenly, + ) { + Button(onClick = { dismiss() }) { + Text(text = "Cancel") } - else -> { - KeyboardOptions(keyboardType = KeyboardType.Text) + Button(onClick = { + config.valueContainer.writeFrom(fieldValue.value.text) + dismiss() + }) { + Text(text = "Ok") } - }, - singleLine = true - ) - - Row( - modifier = Modifier.padding(top = 10.dp).fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceEvenly, - ) { - Button(onClick = { dismiss() }) { - Text(text = "Cancel") - } - Button(onClick = { - config.valueContainer.writeFrom(fieldValue.value) - dismiss() - }) { - Text(text = "Ok") } } } - LaunchedEffect(Unit) { - focusRequester.requestFocus() - } -} - -@Composable -fun StateListDialog(config: ConfigProperty) { - assert(config.valueContainer is ConfigStateListValue) - val stateList = (config.valueContainer as ConfigStateListValue).value() - DefaultDialogCard { - stateList.keys.forEach { key -> - val state = remember { - mutableStateOf(stateList[key] ?: false) - } + @Composable + fun StateListDialog(config: ConfigProperty) { + assert(config.valueContainer is ConfigStateListValue) + val stateList = (config.valueContainer as ConfigStateListValue).value() + DefaultDialogCard { + stateList.keys.forEach { key -> + val state = remember { + mutableStateOf(stateList[key] ?: false) + } - fun toggle(value: Boolean? = null) { - state.value = value ?: !state.value - stateList[key] = state.value - } + fun toggle(value: Boolean? = null) { + state.value = value ?: !state.value + stateList[key] = state.value + } - Row( - modifier = Modifier.clickable { toggle() }, - verticalAlignment = Alignment.CenterVertically - ) { - DefaultEntryText( - text = key, - modifier = Modifier - .weight(1f) - ) - Switch( - checked = state.value, - onCheckedChange = { - toggle(it) - } - ) + Row( + modifier = Modifier.clickable { toggle() }, + verticalAlignment = Alignment.CenterVertically + ) { + DefaultEntryText( + text = if (config.disableValueLocalization) + key + else context.translation.propertyOption(config, key), + modifier = Modifier + .weight(1f) + ) + Switch( + checked = state.value, + onCheckedChange = { + toggle(it) + } + ) + } } } } -} \ No newline at end of file +} diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/MainActivity.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/MainActivity.kt index 5c40d6ee3..94876e9b3 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/manager/MainActivity.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/manager/MainActivity.kt @@ -4,22 +4,8 @@ import android.annotation.SuppressLint import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton -import androidx.compose.material3.Text -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Settings -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.TopAppBar import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.tooling.preview.Preview -import androidx.navigation.compose.NavHost -import androidx.navigation.compose.composable -import androidx.navigation.compose.navigation import androidx.navigation.compose.rememberNavController import me.rhunk.snapenhance.manager.data.ManagerContext @@ -27,26 +13,20 @@ class MainActivity : ComponentActivity() { @SuppressLint("UnusedMaterialScaffoldPaddingParameter") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + val startDestination = intent.getStringExtra("route")?.let { EnumSection.fromRoute(it) } ?: EnumSection.HOME val managerContext = ManagerContext(this) setContent { - App(managerContext) + val navController = rememberNavController() + val navigation = Navigation(managerContext) + AppMaterialTheme { + Scaffold( + containerColor = MaterialTheme.colorScheme.background, + bottomBar = { navigation.NavBar(navController = navController) } + ) { innerPadding -> + navigation.NavigationHost(navController = navController, innerPadding = innerPadding, startDestination = startDestination) + } + } } } } - -@Composable -fun App( - context: ManagerContext -) { - val navController = rememberNavController() - val navigation = Navigation(context) - AppMaterialTheme { - Scaffold( - containerColor = MaterialTheme.colorScheme.background, - bottomBar = { navigation.NavBar(navController = navController) } - ) { innerPadding -> - navigation.NavigationHost(navController = navController, innerPadding = innerPadding) - } - } -} \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/Navigation.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/Navigation.kt index 3d49da177..924be663c 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/manager/Navigation.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/manager/Navigation.kt @@ -30,6 +30,7 @@ class Navigation( ) { @Composable fun NavigationHost( + startDestination: EnumSection, navController: NavHostController, innerPadding: PaddingValues ) { @@ -40,7 +41,7 @@ class Navigation( instance.navController = navController } } - NavHost(navController, startDestination = EnumSection.FEATURES.route, Modifier.padding(innerPadding)) { + NavHost(navController, startDestination = startDestination.route, Modifier.padding(innerPadding)) { sections.forEach { (section, instance) -> composable(section.route) { instance.Content() diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/Section.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/Section.kt index 17162e244..8cf1d2268 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/manager/Section.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/manager/Section.kt @@ -48,6 +48,12 @@ enum class EnumSection( title = "Debug", icon = Icons.Filled.BugReport ); + + companion object { + fun fromRoute(route: String): EnumSection { + return values().first { it.route == route } + } + } } diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/FeaturesSection.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/FeaturesSection.kt index 8f9ae8f1c..1d47255ed 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/FeaturesSection.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/FeaturesSection.kt @@ -4,9 +4,12 @@ import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.widthIn +import androidx.compose.foundation.layout.wrapContentWidth import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.RoundedCornerShape @@ -31,6 +34,7 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp @@ -43,15 +47,15 @@ import me.rhunk.snapenhance.config.impl.ConfigStateListValue import me.rhunk.snapenhance.config.impl.ConfigStateSelection import me.rhunk.snapenhance.config.impl.ConfigStateValue import me.rhunk.snapenhance.config.impl.ConfigStringValue -import me.rhunk.snapenhance.manager.StateListDialog +import me.rhunk.snapenhance.manager.Dialogs import me.rhunk.snapenhance.manager.Section -import me.rhunk.snapenhance.manager.StateSelectionDialog -import me.rhunk.snapenhance.manager.KeyboardInputDialog typealias ClickCallback = (Boolean) -> Unit typealias RegisterClickCallback = (ClickCallback) -> ClickCallback class FeaturesSection : Section() { + private val dialogs by lazy { Dialogs(manager) } + @Composable private fun PropertyAction(item: ConfigProperty, registerClickCallback: RegisterClickCallback) { val showDialog = remember { mutableStateOf(false) } @@ -82,12 +86,15 @@ class FeaturesSection : Section() { is ConfigStateSelection -> { registerDialogOnClickCallback() dialogComposable.value = { - StateSelectionDialog(item) + dialogs.StateSelectionDialog(item) } Text( - text = container.value().let { - it.substring(0, it.length.coerceAtMost(20)) - } + overflow = TextOverflow.Ellipsis, + maxLines = 1, + modifier = Modifier.widthIn(0.dp, 120.dp), + text = if (item.disableValueLocalization) container.value() else { + manager.translation.propertyOption(item, container.value()) + }, ) } @@ -95,10 +102,10 @@ class FeaturesSection : Section() { dialogComposable.value = { when (container) { is ConfigStateListValue -> { - StateListDialog(item) + dialogs.StateListDialog(item) } is ConfigStringValue, is ConfigIntegerValue -> { - KeyboardInputDialog(item) { showDialog.value = false } + dialogs.KeyboardInputDialog(item) { showDialog.value = false } } } } @@ -106,7 +113,7 @@ class FeaturesSection : Section() { registerDialogOnClickCallback().let { { it.invoke(true) } }.also { if (container is ConfigIntegerValue) { FilledIconButton(onClick = it) { - Text(text = container.value().toString()) + Text(text = container.value().toString(), modifier = Modifier.wrapContentWidth(), overflow = TextOverflow.Ellipsis) } } else { IconButton(onClick = it) { @@ -132,7 +139,7 @@ class FeaturesSection : Section() { Row( modifier = Modifier .fillMaxSize() - .padding(all = 10.dp), + .padding(all = 4.dp), horizontalArrangement = Arrangement.SpaceBetween ) { Column( @@ -206,10 +213,11 @@ class FeaturesSection : Section() { ) LazyColumn( modifier = Modifier - .fillMaxSize(), + .fillMaxHeight(), verticalArrangement = Arrangement.Center ) { items(configItems) { item -> + if (item.shouldAppearInSettings.not()) return@items PropertyCard(item) } } diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/NotImplemented.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/NotImplemented.kt index d5f2f65e4..be57ff21f 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/NotImplemented.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/NotImplemented.kt @@ -1,13 +1,8 @@ package me.rhunk.snapenhance.manager.sections -import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.tooling.preview.Preview import me.rhunk.snapenhance.manager.Section class NotImplemented : Section() { diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/TranslationWrapper.kt b/core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/TranslationWrapper.kt index d8c915724..035d47f89 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/TranslationWrapper.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/TranslationWrapper.kt @@ -88,6 +88,10 @@ class TranslationWrapper { return get("property.${property.translationKey}.description") } + fun propertyOption(property: ConfigProperty, item: String): String { + return get(property.getOptionTranslationKey(item)) + } + fun format(key: String, vararg args: Pair): String { return args.fold(get(key)) { acc, pair -> acc.replace("{${pair.first}}", pair.second) diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/SettingsGearInjector.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/SettingsGearInjector.kt index 66ba715be..36e0e6d14 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/SettingsGearInjector.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/SettingsGearInjector.kt @@ -48,9 +48,10 @@ class SettingsGearInjector : AbstractMenu() { setOnClickListener { val intent = Intent().apply { - setClassName(BuildConfig.APPLICATION_ID, ConfigActivity::class.java.name) + setClassName(BuildConfig.APPLICATION_ID, "me.rhunk.snapenhance.manager.MainActivity") + putExtra("route", "features") + putExtra("lspatched", File(context.cacheDir, "lspatch/origin").exists()) } - intent.putExtra("lspatched", File(context.cacheDir, "lspatch/origin").exists()) context.startActivity(intent) } From 1e02e0a61cd8db04fc729840c22c33ae1f0c188d Mon Sep 17 00:00:00 2001 From: rhunk <101876869+rhunk@users.noreply.github.com> Date: Tue, 1 Aug 2023 01:14:04 +0200 Subject: [PATCH 09/81] refactor: config system --- .../rhunk/snapenhance/manager/MainActivity.kt | 16 ++++ .../manager/data/ManagerContext.kt | 3 +- .../manager/sections/FeaturesSection.kt | 35 ++++--- .../manager/util/SaveFolderChecker.kt | 43 +++++++++ build.gradle.kts | 1 + core/build.gradle.kts | 2 +- .../kotlin/me/rhunk/snapenhance/ModContext.kt | 13 ++- .../me/rhunk/snapenhance/SnapEnhance.kt | 4 +- .../rhunk/snapenhance/action/impl/OpenMap.kt | 4 +- .../core/config/ConfigContainer.kt | 78 +++++++++++++++ .../snapenhance/core/config/ConfigObjects.kt | 40 ++++++++ .../snapenhance/core/config/DataProcessors.kt | 94 +++++++++++++++++++ .../snapenhance/core/config/ModConfig.kt | 57 +++++++++++ .../snapenhance/core/config/impl/Camera.kt | 15 +++ .../core/config/impl/DownloaderConfig.kt | 24 +++++ .../core/config/impl/Experimental.kt | 11 +++ .../snapenhance/core/config/impl/Global.kt | 16 ++++ .../core/config/impl/MessagingTweaks.kt | 25 +++++ .../core/config/impl/RootConfig.kt | 15 +++ .../snapenhance/core/config/impl/Spoof.kt | 17 ++++ .../core/config/impl/UserInterfaceTweaks.kt | 34 +++++++ .../snapenhance/download/DownloadProcessor.kt | 5 +- .../snapenhance/features/impl/AutoUpdater.kt | 22 ++--- .../features/impl/ConfigEnumKeys.kt | 20 ++-- .../snapenhance/features/impl/Messaging.kt | 7 +- .../impl/downloader/MediaDownloader.kt | 31 +++--- .../impl/experiments/AmoledDarkMode.kt | 2 +- .../features/impl/experiments/AppPasscode.kt | 6 +- .../impl/experiments/DeviceSpooferHook.kt | 34 +++---- .../impl/experiments/InfiniteStoryBoost.kt | 2 +- .../impl/experiments/MeoPasscodeBypass.kt | 2 +- .../impl/experiments/UnlimitedMultiSnap.kt | 2 +- .../features/impl/privacy/DisableMetrics.kt | 10 +- .../impl/privacy/PreventMessageSending.kt | 12 +-- .../impl/spying/AnonymousStoryViewing.kt | 3 +- .../features/impl/spying/MessageLogger.kt | 10 +- .../impl/spying/PreventReadReceipts.kt | 3 +- .../features/impl/tweaks/AutoSave.kt | 8 +- .../features/impl/tweaks/CameraTweaks.kt | 16 ++-- .../tweaks/DisableVideoLengthRestriction.kt | 5 +- .../impl/tweaks/GalleryMediaSendOverride.kt | 2 +- .../impl/tweaks/GooglePlayServicesDialogs.kt | 2 +- .../features/impl/tweaks/LocationSpoofer.kt | 54 ++++------- .../impl/tweaks/MediaQualityLevelOverride.kt | 4 +- .../features/impl/tweaks/Notifications.kt | 16 ++-- .../features/impl/tweaks/SnapchatPlus.kt | 2 +- .../impl/tweaks/UnlimitedSnapViewTime.kt | 5 +- .../features/impl/ui/StartupPageOverride.kt | 4 +- .../snapenhance/features/impl/ui/UITweaks.kt | 32 +++---- .../me/rhunk/snapenhance/hook/Hooker.kt | 49 +++++----- .../me/rhunk/snapenhance/ui/ItemHelper.kt | 2 + .../ui/menu/impl/ChatActionMenu.kt | 4 +- .../ui/menu/impl/FriendFeedInfoMenu.kt | 24 ++--- .../ui/menu/impl/MenuViewInjector.kt | 5 +- 54 files changed, 712 insertions(+), 240 deletions(-) create mode 100644 app/src/main/kotlin/me/rhunk/snapenhance/manager/util/SaveFolderChecker.kt create mode 100644 core/src/main/kotlin/me/rhunk/snapenhance/core/config/ConfigContainer.kt create mode 100644 core/src/main/kotlin/me/rhunk/snapenhance/core/config/ConfigObjects.kt create mode 100644 core/src/main/kotlin/me/rhunk/snapenhance/core/config/DataProcessors.kt create mode 100644 core/src/main/kotlin/me/rhunk/snapenhance/core/config/ModConfig.kt create mode 100644 core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/Camera.kt create mode 100644 core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/DownloaderConfig.kt create mode 100644 core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/Experimental.kt create mode 100644 core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/Global.kt create mode 100644 core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/MessagingTweaks.kt create mode 100644 core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/RootConfig.kt create mode 100644 core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/Spoof.kt create mode 100644 core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/UserInterfaceTweaks.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/MainActivity.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/MainActivity.kt index 94876e9b3..03b0ba157 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/manager/MainActivity.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/manager/MainActivity.kt @@ -1,21 +1,37 @@ package me.rhunk.snapenhance.manager import android.annotation.SuppressLint +import android.content.Intent import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent +import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.navigation.compose.rememberNavController +import me.rhunk.snapenhance.Logger import me.rhunk.snapenhance.manager.data.ManagerContext +import me.rhunk.snapenhance.manager.util.SaveFolderChecker +import me.rhunk.snapenhance.util.ActivityResultCallback class MainActivity : ComponentActivity() { + private val activityResultCallbacks = mutableMapOf() + @SuppressLint("UnusedMaterialScaffoldPaddingParameter") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + val startDestination = intent.getStringExtra("route")?.let { EnumSection.fromRoute(it) } ?: EnumSection.HOME val managerContext = ManagerContext(this) + //FIXME: temporary save folder + SaveFolderChecker.askForFolder( + this, + managerContext.config.root.downloader.saveFolder) + { + managerContext.config.writeConfig() + } + setContent { val navController = rememberNavController() val navigation = Navigation(managerContext) diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/data/ManagerContext.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/data/ManagerContext.kt index 30702a755..ab6863e21 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/manager/data/ManagerContext.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/manager/data/ManagerContext.kt @@ -4,11 +4,12 @@ import android.content.Context import me.rhunk.snapenhance.bridge.wrapper.ConfigWrapper import me.rhunk.snapenhance.bridge.wrapper.MappingsWrapper import me.rhunk.snapenhance.bridge.wrapper.TranslationWrapper +import me.rhunk.snapenhance.core.config.ModConfig class ManagerContext( private val context: Context ) { - val config = ConfigWrapper() + val config = ModConfig() val translation = TranslationWrapper() val mappings = MappingsWrapper(context) diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/FeaturesSection.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/FeaturesSection.kt index 1d47255ed..b0c60b7ba 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/FeaturesSection.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/FeaturesSection.kt @@ -41,6 +41,7 @@ import androidx.compose.ui.unit.sp import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.DialogProperties import kotlinx.coroutines.launch +import me.rhunk.snapenhance.Logger import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.config.impl.ConfigIntegerValue import me.rhunk.snapenhance.config.impl.ConfigStateListValue @@ -170,13 +171,28 @@ class FeaturesSection : Section() { } } + @Composable + private fun PropertyContainer() { + val properties = remember { + val items by manager.config + items.properties.map { it.key to it.value } + } + + LazyColumn( + modifier = Modifier + .fillMaxHeight(), + verticalArrangement = Arrangement.Center + ) { + items(properties) { (key, value) -> + // Logger.debug("key: $key, value: $value") + } + } + } + @Composable @Preview override fun Content() { - val configItems = remember { - ConfigProperty.sortedByCategory() - } val scope = rememberCoroutineScope() val scaffoldState = rememberScaffoldState() Scaffold( @@ -184,7 +200,7 @@ class FeaturesSection : Section() { floatingActionButton = { FloatingActionButton( onClick = { - manager.config.save() + //manager.config.writeConfig() scope.launch { scaffoldState.snackbarHostState.showSnackbar("Saved") } @@ -211,16 +227,7 @@ class FeaturesSection : Section() { modifier = Modifier.padding(all = 10.dp), fontSize = 20.sp ) - LazyColumn( - modifier = Modifier - .fillMaxHeight(), - verticalArrangement = Arrangement.Center - ) { - items(configItems) { item -> - if (item.shouldAppearInSettings.not()) return@items - PropertyCard(item) - } - } + PropertyContainer() } } ) diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/util/SaveFolderChecker.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/util/SaveFolderChecker.kt new file mode 100644 index 000000000..757b903b2 --- /dev/null +++ b/app/src/main/kotlin/me/rhunk/snapenhance/manager/util/SaveFolderChecker.kt @@ -0,0 +1,43 @@ +package me.rhunk.snapenhance.manager.util + +import android.app.Activity +import android.app.AlertDialog +import android.content.Intent +import android.widget.Toast +import androidx.activity.ComponentActivity +import androidx.activity.result.contract.ActivityResultContracts +import me.rhunk.snapenhance.Logger +import me.rhunk.snapenhance.core.config.PropertyValue +import kotlin.system.exitProcess + +object SaveFolderChecker { + fun askForFolder(activity: ComponentActivity, property: PropertyValue, saveConfig: () -> Unit) { + if (property.get().isEmpty() || !property.get().startsWith("content://")) { + val startActivity = activity.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) result@{ + if (it.resultCode != Activity.RESULT_OK) return@result + val uri = it.data?.data ?: return@result + val value = uri.toString() + activity.contentResolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION) + property.set(value) + saveConfig() + Toast.makeText(activity, "save folder set!", Toast.LENGTH_SHORT).show() + activity.finish() + } + + AlertDialog.Builder(activity) + .setTitle("Save folder") + .setMessage("Please select a folder where you want to save downloaded files.") + .setPositiveButton("Select") { _, _ -> + startActivity.launch( + Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) + .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + .addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION) + ) + } + .setNegativeButton("Cancel") { _, _ -> + exitProcess(0) + } + .show() + } + } +} \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 920a410e6..7fa667cfb 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,6 +2,7 @@ @Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed plugins { alias(libs.plugins.androidApplication) apply false + alias(libs.plugins.androidLibrary) apply false alias(libs.plugins.kotlinAndroid) apply false } diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 8c8528d93..ff127df76 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -1,6 +1,6 @@ @Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed plugins { - id("com.android.library") + alias(libs.plugins.androidLibrary) alias(libs.plugins.kotlinAndroid) } android { diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ModContext.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ModContext.kt index 42886b914..9ece2f56c 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/ModContext.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/ModContext.kt @@ -12,8 +12,9 @@ import com.google.gson.Gson import com.google.gson.GsonBuilder import kotlinx.coroutines.asCoroutineDispatcher import me.rhunk.snapenhance.bridge.BridgeClient -import me.rhunk.snapenhance.bridge.wrapper.ConfigWrapper import me.rhunk.snapenhance.bridge.wrapper.TranslationWrapper +import me.rhunk.snapenhance.core.config.ModConfig +import me.rhunk.snapenhance.core.config.impl.RootConfig import me.rhunk.snapenhance.data.MessageSender import me.rhunk.snapenhance.database.DatabaseAccess import me.rhunk.snapenhance.features.Feature @@ -39,8 +40,10 @@ class ModContext { val gson: Gson = GsonBuilder().create() + private val modConfig = ModConfig() + val config by modConfig + val translation = TranslationWrapper() - val config = ConfigWrapper() val features = FeatureManager(this) val mappings = MappingManager(this) val actionManager = ActionManager(this) @@ -87,7 +90,7 @@ class ModContext { fun softRestartApp(saveSettings: Boolean = false) { if (saveSettings) { - config.writeConfig() + modConfig.writeConfig() } val intent: Intent? = androidContext.packageManager.getLaunchIntentForPackage( Constants.SNAPCHAT_PACKAGE_NAME @@ -113,4 +116,8 @@ class ModContext { Process.killProcess(Process.myPid()) exitProcess(1) } + + fun reloadConfig() { + modConfig.loadFromBridge(bridgeClient) + } } \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/SnapEnhance.kt b/core/src/main/kotlin/me/rhunk/snapenhance/SnapEnhance.kt index 0c7b28c29..97036e65d 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/SnapEnhance.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/SnapEnhance.kt @@ -84,7 +84,7 @@ class SnapEnhance { } Logger.debug("Reloading config") - appContext.config.loadFromBridge(appContext.bridgeClient) + appContext.reloadConfig() } } @@ -97,7 +97,7 @@ class SnapEnhance { measureTime { with(appContext) { - config.loadFromBridge(bridgeClient) + reloadConfig() mappings.init() //if mappings aren't loaded, we can't initialize features if (!mappings.areMappingsLoaded) return diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/action/impl/OpenMap.kt b/core/src/main/kotlin/me/rhunk/snapenhance/action/impl/OpenMap.kt index 242c7db89..bb5854a03 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/action/impl/OpenMap.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/action/impl/OpenMap.kt @@ -13,8 +13,8 @@ class OpenMap: AbstractAction("action.open_map", dependsOnProperty = ConfigPrope val mapActivityIntent = Intent() mapActivityIntent.setClassName(BuildConfig.APPLICATION_ID, MapActivity::class.java.name) mapActivityIntent.putExtra("location", Bundle().apply { - putDouble("latitude", context.config.string(ConfigProperty.LATITUDE).toDouble()) - putDouble("longitude", context.config.string(ConfigProperty.LONGITUDE).toDouble()) + putDouble("latitude", context.config.spoof.location.latitude.get().toDouble()) + putDouble("longitude", context.config.spoof.location.longitude.get().toDouble()) }) context.mainActivity!!.startActivityForResult(mapActivityIntent, 0x1337) diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/config/ConfigContainer.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/ConfigContainer.kt new file mode 100644 index 000000000..983695f05 --- /dev/null +++ b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/ConfigContainer.kt @@ -0,0 +1,78 @@ +package me.rhunk.snapenhance.core.config + +import com.google.gson.JsonObject +import me.rhunk.snapenhance.Logger +import kotlin.reflect.KProperty + +typealias ConfigParamsBuilder = ConfigParams.() -> Unit + +open class ConfigContainer( + var globalState: Boolean? = null +) { + val properties = mutableMapOf, PropertyValue<*>>() + + private inline fun registerProperty( + key: String, + type: DataProcessors.PropertyDataProcessor<*>, + defaultValue: PropertyValue, + params: ConfigParams.() -> Unit = {} + ): PropertyValue { + val propertyKey = PropertyKey(key, type, ConfigParams().also { it.params() }) + properties[propertyKey] = defaultValue + return defaultValue + } + + protected fun boolean(key: String, defaultValue: Boolean = false, params: ConfigParamsBuilder = {}) = + registerProperty(key, DataProcessors.BOOLEAN, PropertyValue(defaultValue), params) + + protected fun integer(key: String, defaultValue: Int = 0, params: ConfigParamsBuilder = {}) = + registerProperty(key, DataProcessors.INTEGER, PropertyValue(defaultValue), params) + + protected fun float(key: String, defaultValue: Float = 0f, params: ConfigParamsBuilder = {}) = + registerProperty(key, DataProcessors.FLOAT, PropertyValue(defaultValue), params) + + protected fun string(key: String, defaultValue: String = "", params: ConfigParamsBuilder = {}) = + registerProperty(key, DataProcessors.STRING, PropertyValue(defaultValue), params) + + protected fun multiple( + key: String, + vararg values: String = emptyArray(), + params: ConfigParamsBuilder = {} + ) = registerProperty(key, + DataProcessors.STRING_MULTIPLE_SELECTION, PropertyValue(emptyList(), defaultValues = values.toList()), params) + + //null value is considered as Off/Disabled + protected fun unique( + key: String, + vararg values: String = emptyArray(), + params: ConfigParamsBuilder = {} + ) = registerProperty(key, + DataProcessors.STRING_UNIQUE_SELECTION, PropertyValue("null", defaultValues = values.toList()), params) + + protected fun container( + key: String, + container: T + ) = registerProperty(key, DataProcessors.container(container), PropertyValue(container)).get() + + fun toJson(): JsonObject { + val json = JsonObject() + properties.forEach { (propertyKey, propertyValue) -> + Logger.debug("${propertyKey.name} => $propertyValue") + val serializedValue = propertyValue.getNullable()?.let { propertyKey.dataProcessor.serializeAny(it) } + json.add(propertyKey.name, serializedValue) + } + return json + } + + fun fromJson(json: JsonObject) { + properties.forEach { (key, _) -> + val jsonElement = json.get(key.name) ?: return@forEach + key.dataProcessor.deserializeAny(jsonElement)?.let { + properties[key]?.setAny(it) + } + } + } + + operator fun getValue(t: Any?, property: KProperty<*>) = this.globalState + operator fun setValue(t: Any?, property: KProperty<*>, t1: Boolean?) { this.globalState = t1 } +} \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/config/ConfigObjects.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/ConfigObjects.kt new file mode 100644 index 000000000..b98cc2693 --- /dev/null +++ b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/ConfigObjects.kt @@ -0,0 +1,40 @@ +package me.rhunk.snapenhance.core.config + +import me.rhunk.snapenhance.Logger +import kotlin.reflect.KProperty + +class ConfigParams( + var shouldTranslate: Boolean = false, + var hidden: Boolean = false, + var isFolder: Boolean = false +) + +class PropertyValue( + private var value: T? = null, + val defaultValues: List<*>? = null +) { + inner class PropertyValueNullable { + fun get() = value + operator fun getValue(t: Any?, property: KProperty<*>): T? = getNullable() + operator fun setValue(t: Any?, property: KProperty<*>, t1: T?) = set(t1) + } + + fun nullable() = PropertyValueNullable() + + fun isSet() = value != null + fun getNullable() = value?.takeIf { it != "null" } + fun get() = getNullable() ?: throw IllegalStateException("Property is not set") + fun set(value: T?) { this.value = value } + @Suppress("UNCHECKED_CAST") + fun setAny(value: Any?) { this.value = value as T? } + + operator fun getValue(t: Any?, property: KProperty<*>): T = get() + operator fun setValue(t: Any?, property: KProperty<*>, t1: T?) = set(t1) +} + +class PropertyKey( + val name: String, + val dataProcessor: DataProcessors.PropertyDataProcessor, + val params: ConfigParams = ConfigParams(), +) + diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/config/DataProcessors.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/DataProcessors.kt new file mode 100644 index 000000000..755a5c8fe --- /dev/null +++ b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/DataProcessors.kt @@ -0,0 +1,94 @@ +package me.rhunk.snapenhance.core.config + +import com.google.gson.JsonArray +import com.google.gson.JsonElement +import com.google.gson.JsonNull +import com.google.gson.JsonObject +import com.google.gson.JsonPrimitive + +object DataProcessors { + enum class Type { + STRING, + BOOLEAN, + INTEGER, + FLOAT, + STRING_MULTIPLE_SELECTION, + STRING_UNIQUE_SELECTION, + CONTAINER, + } + + data class PropertyDataProcessor + internal constructor( + val type: Type, + private val serialize: (T) -> JsonElement, + private val deserialize: (JsonElement) -> T + ) { + @Suppress("UNCHECKED_CAST") + fun serializeAny(value: Any) = serialize(value as T) + fun deserializeAny(value: JsonElement) = deserialize(value) + } + + val STRING = PropertyDataProcessor( + type = Type.STRING, + serialize = { + if (it != null) JsonPrimitive(it) + else JsonNull.INSTANCE + }, + deserialize = { + if (it.isJsonNull) null + else it.asString + }, + ) + + val BOOLEAN = PropertyDataProcessor( + type = Type.BOOLEAN, + serialize = { + if (it) JsonPrimitive(true) + else JsonPrimitive(false) + }, + deserialize = { it.asBoolean }, + ) + + val INTEGER = PropertyDataProcessor( + type = Type.INTEGER, + serialize = { JsonPrimitive(it) }, + deserialize = { it.asInt }, + ) + + val FLOAT = PropertyDataProcessor( + type = Type.FLOAT, + serialize = { JsonPrimitive(it) }, + deserialize = { it.asFloat }, + ) + + val STRING_MULTIPLE_SELECTION = PropertyDataProcessor( + type = Type.STRING_MULTIPLE_SELECTION, + serialize = { JsonArray().apply { it.forEach { add(it) } } }, + deserialize = { obj -> + obj.asJsonArray.map { it.asString } + }, + ) + + val STRING_UNIQUE_SELECTION = PropertyDataProcessor( + type = Type.STRING_UNIQUE_SELECTION, + serialize = { JsonPrimitive(it) }, + deserialize = { obj -> obj.takeIf { !it.isJsonNull }?.asString } + ) + + fun container(container: T) = PropertyDataProcessor( + type = Type.CONTAINER, + serialize = { + JsonObject().apply { + addProperty("state", it.globalState) + add("properties", it.toJson()) + } + }, + deserialize = { obj -> + val jsonObject = obj.asJsonObject + container.apply { + globalState = jsonObject["state"].takeIf { !it.isJsonNull }?.asBoolean + fromJson(jsonObject["properties"].asJsonObject) + } + }, + ) +} \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/config/ModConfig.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/ModConfig.kt new file mode 100644 index 000000000..29fd998b8 --- /dev/null +++ b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/ModConfig.kt @@ -0,0 +1,57 @@ +package me.rhunk.snapenhance.core.config + +import android.content.Context +import com.google.gson.Gson +import com.google.gson.GsonBuilder +import com.google.gson.JsonObject +import me.rhunk.snapenhance.Logger +import me.rhunk.snapenhance.bridge.BridgeClient +import me.rhunk.snapenhance.bridge.FileLoaderWrapper +import me.rhunk.snapenhance.bridge.types.BridgeFileType +import me.rhunk.snapenhance.core.config.impl.RootConfig + +class ModConfig() { + val root = RootConfig() + + companion object { + val gson: Gson = GsonBuilder().setPrettyPrinting().create() + } + + private val file = FileLoaderWrapper(BridgeFileType.CONFIG, "{}".toByteArray(Charsets.UTF_8)) + + operator fun getValue(thisRef: Any?, property: Any?) = root + + private fun load() { + if (!file.isFileExists()) { + writeConfig() + return + } + + runCatching { + loadConfig() + }.onFailure { + Logger.error("Failed to load config", it) + writeConfig() + } + } + + private fun loadConfig() { + val configContent = file.read() + root.fromJson(gson.fromJson(configContent.toString(Charsets.UTF_8), JsonObject::class.java)) + } + + fun writeConfig() { + val configObject = root.toJson() + file.write(configObject.toString().toByteArray(Charsets.UTF_8)) + } + + fun loadFromContext(context: Context) { + file.loadFromContext(context) + load() + } + + fun loadFromBridge(bridgeClient: BridgeClient) { + file.loadFromBridge(bridgeClient) + load() + } +} \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/Camera.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/Camera.kt new file mode 100644 index 000000000..051518c6b --- /dev/null +++ b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/Camera.kt @@ -0,0 +1,15 @@ +package me.rhunk.snapenhance.core.config.impl + +import me.rhunk.snapenhance.core.config.ConfigContainer +import me.rhunk.snapenhance.features.impl.tweaks.CameraTweaks + +class Camera : ConfigContainer() { + val disable = boolean("disable_camera") + val immersiveCameraPreview = boolean("immersive_camera_preview") + val overridePreviewResolution = unique("override_preview_resolution", *CameraTweaks.resolutions.toTypedArray()) + { shouldTranslate = false } + val overridePictureResolution = unique("override_picture_resolution", *CameraTweaks.resolutions.toTypedArray()) + { shouldTranslate = false } + val forceHighestFrameRate = boolean("force_highest_frame_rate") + val forceCameraSourceEncoding = boolean("force_camera_source_encoding") +} diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/DownloaderConfig.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/DownloaderConfig.kt new file mode 100644 index 000000000..791bc774c --- /dev/null +++ b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/DownloaderConfig.kt @@ -0,0 +1,24 @@ +package me.rhunk.snapenhance.core.config.impl + +import me.rhunk.snapenhance.core.config.ConfigContainer + +class DownloaderConfig : ConfigContainer() { + val saveFolder = string("save_folder") { isFolder = true } + val autoDownloadOptions = multiple("auto_download_options", + "friend_snaps", + "friend_stories", + "public_stories", + "spotlight" + ) + val pathFormat = multiple("path_format", + "create_user_folder", + "append_hash", + "append_date_time", + "append_type", + "append_username" + ) + val allowDuplicate = boolean("allow_duplicate") + val mergeOverlays = boolean("merge_overlays") + val chatDownloadContextMenu = boolean("chat_download_context_menu") + val logging = multiple("logging", "started", "success", "progress", "failure") +} \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/Experimental.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/Experimental.kt new file mode 100644 index 000000000..e25b5934a --- /dev/null +++ b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/Experimental.kt @@ -0,0 +1,11 @@ +package me.rhunk.snapenhance.core.config.impl + +import me.rhunk.snapenhance.core.config.ConfigContainer + +class Experimental : ConfigContainer() { + val appPasscode = string("app_passcode") + val appLockOnResume = boolean("app_lock_on_resume") + val infiniteStoryBoost = boolean("infinite_story_boost") + val meoPasscodeBypass = boolean("meo_passcode_bypass") + val unlimitedMultiSnap = boolean("unlimited_multi_snap") +} \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/Global.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/Global.kt new file mode 100644 index 000000000..321746ef3 --- /dev/null +++ b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/Global.kt @@ -0,0 +1,16 @@ +package me.rhunk.snapenhance.core.config.impl + +import me.rhunk.snapenhance.core.config.ConfigContainer +import me.rhunk.snapenhance.data.NotificationType + +class Global : ConfigContainer() { + val snapchatPlus = boolean("snapchat_plus") + val autoUpdater = unique("auto_updater", "DAILY","EVERY_LAUNCH", "DAILY", "WEEKLY") + val disableMetrics = boolean("disable_metrics") + val disableVideoLengthRestrictions = boolean("disable_video_length_restrictions") + val disableGooglePlayDialogs = boolean("disable_google_play_dialogs") + val forceMediaSourceQuality = boolean("force_media_source_quality") + val betterNotifications = multiple("better_notifications", "snap", "chat", "reply_button", "download_button") + val notificationBlacklist = multiple("notification_blacklist", *NotificationType.getIncomingValues().map { it.key }.toTypedArray()) + val disableSnapSplitting = boolean("disable_snap_splitting") +} \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/MessagingTweaks.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/MessagingTweaks.kt new file mode 100644 index 000000000..5530525f3 --- /dev/null +++ b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/MessagingTweaks.kt @@ -0,0 +1,25 @@ +package me.rhunk.snapenhance.core.config.impl + +import me.rhunk.snapenhance.core.config.ConfigContainer +import me.rhunk.snapenhance.data.NotificationType + +class MessagingTweaks : ConfigContainer() { + val anonymousStoryViewing = boolean("annonymous_story_viewing") + val preventReadReceipts = boolean("prevent_read_receipts") + val hideBitmojiPresence = boolean("hide_bitmoji_presence") + val hideTypingNotifications = boolean("hide_typing_notifications") + val unlimitedSnapViewTime = boolean("unlimited_snap_view_time") + val preventMessageSending = multiple("prevent_message_sending", *NotificationType.getOutgoingValues().map { it.key }.toTypedArray()) + val messageLogger = boolean("message_logger") + val autoSaveMessagesInConversations = multiple("auto_save_messages_in_conversations", + "CHAT", + "SNAP", + "NOTE", + "EXTERNAL_MEDIA", + "STICKER" + ) + + val galleryMediaSendOverride = unique("gallery_media_send_override", "NOTE", "SNAP", "LIVE_SNAP") + val messagePreviewLength = integer("message_preview_length", defaultValue = 20) + +} \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/RootConfig.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/RootConfig.kt new file mode 100644 index 000000000..57cbb8759 --- /dev/null +++ b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/RootConfig.kt @@ -0,0 +1,15 @@ +package me.rhunk.snapenhance.core.config.impl + +import me.rhunk.snapenhance.core.config.ConfigContainer +import me.rhunk.snapenhance.data.NotificationType +import me.rhunk.snapenhance.features.impl.tweaks.CameraTweaks + +class RootConfig : ConfigContainer() { + val downloader = container("downloader", DownloaderConfig()) + val userInterface = container("user_interface", UserInterfaceTweaks()) + val messaging = container("messaging", MessagingTweaks()) + val global = container("global", Global()) + val camera = container("camera", Camera()) + val experimental = container("experimental", Experimental()) + val spoof = container("spoof", Spoof()) +} \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/Spoof.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/Spoof.kt new file mode 100644 index 000000000..eab2e5bff --- /dev/null +++ b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/Spoof.kt @@ -0,0 +1,17 @@ +package me.rhunk.snapenhance.core.config.impl + +import me.rhunk.snapenhance.core.config.ConfigContainer + +class Spoof : ConfigContainer() { + inner class Location : ConfigContainer(globalState = false) { + val latitude = float("location_latitude") + val longitude = float("location_longitude") + } + val location = container("location", Location()) + + inner class Device : ConfigContainer(globalState = false) { + val fingerprint = string("device_fingerprint") + val androidId = string("device_android_id") + } + val device = container("device", Device()) +} \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/UserInterfaceTweaks.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/UserInterfaceTweaks.kt new file mode 100644 index 000000000..b8f5fd895 --- /dev/null +++ b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/UserInterfaceTweaks.kt @@ -0,0 +1,34 @@ +package me.rhunk.snapenhance.core.config.impl + +import me.rhunk.snapenhance.core.config.ConfigContainer + +class UserInterfaceTweaks : ConfigContainer() { + val enableAppAppearance = boolean("enable_app_appearance") + val amoledDarkMode = boolean("amoled_dark_mode") + val blockAds = boolean("block_ads") + val mapFriendNameTags = boolean("map_friend_nametags") + val streakExpirationInfo = boolean("streak_expiration_info") + val hideStorySections = multiple("hide_story_sections", "hide_friend_suggestions", "hide_friends", "hide_following", "hide_for_you") + val hideUiComponents = multiple( + "hide_ui_components", + "hide_voice_record_button", + "hide_stickers_button", + "hide_cognac_button", + "hide_live_location_share_button", + "hide_call_buttons" + ) + val disableSpotlight = boolean("disable_spotlight") + val startupTab = unique("startup_tab", "ngs_map_icon_container", + "ngs_map_icon_container", + "ngs_chat_icon_container", + "ngs_camera_icon_container", + "ngs_community_icon_container", + "ngs_spotlight_icon_container", + "ngs_search_icon_container" + ) + val storyViewerOverride = unique("story_viewer_override", "DISCOVER_PLAYBACK_SEEKBAR", "VERTICAL_STORY_VIEWER") + + val friendFeedMenuButtons = multiple("friend_feed_menu_buttons", "auto_download_blacklist", "anti_auto_save", "stealth_mode", "conversation_info") + val enableFriendFeedMenuBar = boolean("enable_friend_feed_menu_bar") + val friendFeedMenuPosition = integer("friend_feed_menu_position", defaultValue = 1) +} diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/download/DownloadProcessor.kt b/core/src/main/kotlin/me/rhunk/snapenhance/download/DownloadProcessor.kt index 9dc23792c..d0c52823d 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/download/DownloadProcessor.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/download/DownloadProcessor.kt @@ -20,6 +20,7 @@ import me.rhunk.snapenhance.SharedContext import me.rhunk.snapenhance.bridge.DownloadCallback import me.rhunk.snapenhance.bridge.wrapper.ConfigWrapper import me.rhunk.snapenhance.config.ConfigProperty +import me.rhunk.snapenhance.core.config.ModConfig import me.rhunk.snapenhance.data.FileType import me.rhunk.snapenhance.download.data.DownloadMetadata import me.rhunk.snapenhance.download.data.DownloadRequest @@ -117,7 +118,7 @@ class DownloadProcessor ( private suspend fun saveMediaToGallery(inputFile: File, pendingDownload: PendingDownload) { if (coroutineContext.job.isCancelled) return - val config = ConfigWrapper().apply { loadFromContext(context) } + val config by ModConfig().apply { loadFromContext(context) } runCatching { val fileType = FileType.fromFile(inputFile) @@ -128,7 +129,7 @@ class DownloadProcessor ( val fileName = pendingDownload.metadata.outputPath.substringAfterLast("/") + "." + fileType.fileExtension - val outputFolder = DocumentFile.fromTreeUri(context, Uri.parse(config.string(ConfigProperty.SAVE_FOLDER))) + val outputFolder = DocumentFile.fromTreeUri(context, Uri.parse(config.downloader.saveFolder.get())) ?: throw Exception("Failed to open output folder") val outputFileFolder = pendingDownload.metadata.outputPath.let { diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/AutoUpdater.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/AutoUpdater.kt index c92ac5900..ac18f4dfe 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/AutoUpdater.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/AutoUpdater.kt @@ -1,7 +1,6 @@ package me.rhunk.snapenhance.features.impl import android.annotation.SuppressLint -import android.app.AlertDialog import android.app.DownloadManager import android.content.BroadcastReceiver import android.content.Context @@ -9,23 +8,22 @@ import android.content.Intent import android.content.IntentFilter import android.net.Uri import android.os.Environment -import me.rhunk.snapenhance.core.BuildConfig +import com.google.gson.JsonParser import me.rhunk.snapenhance.Logger -import me.rhunk.snapenhance.config.ConfigProperty +import me.rhunk.snapenhance.core.BuildConfig import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams import me.rhunk.snapenhance.ui.ViewAppearanceHelper import okhttp3.OkHttpClient import okhttp3.Request -import org.json.JSONArray class AutoUpdater : Feature("AutoUpdater", loadParams = FeatureLoadParams.ACTIVITY_CREATE_ASYNC) { override fun asyncOnActivityCreate() { - val checkForUpdateMode = context.config.state(ConfigProperty.AUTO_UPDATER) + val autoUpdaterTime = context.config.global.autoUpdater.getNullable() ?: return val currentTimeMillis = System.currentTimeMillis() val checkForUpdatesTimestamp = context.bridgeClient.getAutoUpdaterTime() - val delayTimestamp = when (checkForUpdateMode) { + val delayTimestamp = when (autoUpdaterTime) { "EVERY_LAUNCH" -> currentTimeMillis - checkForUpdatesTimestamp "DAILY" -> 86400000L "WEEKLY" -> 604800000L @@ -50,16 +48,16 @@ class AutoUpdater : Feature("AutoUpdater", loadParams = FeatureLoadParams.ACTIVI if (!response.isSuccessful) throw Throwable("Failed to fetch releases: ${response.code}") - val releases = JSONArray(response.body.string()).also { - if (it.length() == 0) throw Throwable("No releases found") + val releases = JsonParser.parseString(response.body.string()).asJsonArray.also { + if (it.size() == 0) throw Throwable("No releases found") } - val latestRelease = releases.getJSONObject(0) - val latestVersion = latestRelease.getString("tag_name") + val latestRelease = releases.get(0).asJsonObject + val latestVersion = latestRelease.getAsJsonPrimitive("tag_name").asString if (latestVersion.removePrefix("v") == BuildConfig.VERSION_NAME) return null - val releaseContentBody = latestRelease.getString("body") - val downloadEndpoint = latestRelease.getJSONArray("assets").getJSONObject(0).getString("browser_download_url") + val releaseContentBody = latestRelease.getAsJsonPrimitive("body").asString + val downloadEndpoint = latestRelease.getAsJsonArray("assets").get(0).asJsonObject.getAsJsonPrimitive("browser_download_url").asString context.runOnUiThread { ViewAppearanceHelper.newAlertDialogBuilder(context.mainActivity) diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/ConfigEnumKeys.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/ConfigEnumKeys.kt index eb04b21e8..f7069be01 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/ConfigEnumKeys.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/ConfigEnumKeys.kt @@ -55,7 +55,7 @@ class ConfigEnumKeys : Feature("Config enum keys", loadParams = FeatureLoadParam @SuppressLint("PrivateApi") override fun onActivityCreate() { - if (context.config.bool(ConfigProperty.NEW_MAP_UI)) { + if (context.config.userInterface.mapFriendNameTags.get()) { hookAllEnums(context.mappings.getMappedClass("enums", "PLUS")) { if (key == "REDUCE_MY_PROFILE_UI_COMPLEXITY") set(true) } @@ -63,17 +63,17 @@ class ConfigEnumKeys : Feature("Config enum keys", loadParams = FeatureLoadParam hookAllEnums(context.mappings.getMappedClass("enums", "ARROYO")) { if (key == "ENABLE_LONG_SNAP_SENDING") { - if (context.config.bool(ConfigProperty.DISABLE_SNAP_SPLITTING)) set(true) + if (context.config.global.disableSnapSplitting.get()) set(true) } } - if (context.config.bool(ConfigProperty.STREAK_EXPIRATION_INFO)) { + if (context.config.userInterface.streakExpirationInfo.get()) { hookAllEnums(context.mappings.getMappedClass("enums", "FRIENDS_FEED")) { if (key == "STREAK_EXPIRATION_INFO") set(true) } } - if (context.config.bool(ConfigProperty.BLOCK_ADS)) { + if (context.config.userInterface.blockAds.get()) { hookAllEnums(context.mappings.getMappedClass("enums", "SNAPADS")) { if (key == "BYPASS_AD_FEATURE_GATE") { set(true) @@ -84,14 +84,12 @@ class ConfigEnumKeys : Feature("Config enum keys", loadParams = FeatureLoadParam } } - context.config.state(ConfigProperty.STORY_VIEWER_OVERRIDE).let { state -> - if (state == "OFF") return@let - + context.config.userInterface.storyViewerOverride.getNullable()?.let { value -> hookAllEnums(context.mappings.getMappedClass("enums", "DISCOVER_FEED")) { - if (key == "DF_ENABLE_SHOWS_PAGE_CONTROLS" && state == "DISCOVER_PLAYBACK_SEEKBAR") { + if (key == "DF_ENABLE_SHOWS_PAGE_CONTROLS" && value == "DISCOVER_PLAYBACK_SEEKBAR") { set(true) } - if (key == "DF_VOPERA_FOR_STORIES" && state == "VERTICAL_STORY_VIEWER") { + if (key == "DF_VOPERA_FOR_STORIES" && value == "VERTICAL_STORY_VIEWER") { set(true) } } @@ -105,8 +103,8 @@ class ConfigEnumKeys : Feature("Config enum keys", loadParams = FeatureLoadParam sharedPreferencesImpl.methods.first { it.name == "getBoolean" }.hook(HookStage.BEFORE) { param -> when (param.arg(0)) { - "SIG_APP_APPEARANCE_SETTING" -> if (context.config.bool(ConfigProperty.ENABLE_APP_APPEARANCE)) param.setResult(true) - "SPOTLIGHT_5TH_TAB_ENABLED" -> if (context.config.bool(ConfigProperty.DISABLE_SPOTLIGHT)) param.setResult(false) + "SIG_APP_APPEARANCE_SETTING" -> if (context.config.userInterface.enableAppAppearance.get()) param.setResult(true) + "SPOTLIGHT_5TH_TAB_ENABLED" -> if (context.config.userInterface.disableSpotlight.get()) param.setResult(false) } } } diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/Messaging.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/Messaging.kt index 8b7940e86..a0e767e9b 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/Messaging.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/Messaging.kt @@ -61,9 +61,12 @@ class Messaging : Feature("Messaging", loadParams = FeatureLoadParams.ACTIVITY_C override fun asyncInit() { val stealthMode = context.feature(StealthMode::class) + val hideBitmojiPresence by context.config.messaging.hideBitmojiPresence + val hideTypingNotification by context.config.messaging.hideTypingNotifications + arrayOf("activate", "deactivate", "processTypingActivity").forEach { hook -> Hooker.hook(context.classCache.presenceSession, hook, HookStage.BEFORE, { - context.config.bool(ConfigProperty.HIDE_BITMOJI_PRESENCE) || stealthMode.isStealth(openedConversationUUID.toString()) + hideBitmojiPresence || stealthMode.isStealth(openedConversationUUID.toString()) }) { it.setResult(null) } @@ -81,7 +84,7 @@ class Messaging : Feature("Messaging", loadParams = FeatureLoadParams.ACTIVITY_C } Hooker.hook(context.classCache.conversationManager, "sendTypingNotification", HookStage.BEFORE, { - context.config.bool(ConfigProperty.HIDE_TYPING_NOTIFICATION) || stealthMode.isStealth(openedConversationUUID.toString()) + hideTypingNotification || stealthMode.isStealth(openedConversationUUID.toString()) }) { it.setResult(null) } diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/downloader/MediaDownloader.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/downloader/MediaDownloader.kt index df5308cc7..4be342aec 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/downloader/MediaDownloader.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/downloader/MediaDownloader.kt @@ -14,7 +14,6 @@ import me.rhunk.snapenhance.bridge.DownloadCallback import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.data.ContentType import me.rhunk.snapenhance.data.FileType -import me.rhunk.snapenhance.data.wrapper.impl.SnapUUID import me.rhunk.snapenhance.data.wrapper.impl.media.MediaInfo import me.rhunk.snapenhance.data.wrapper.impl.media.dash.LongformVideoPlaylistItem import me.rhunk.snapenhance.data.wrapper.impl.media.dash.SnapPlaylistItem @@ -73,8 +72,8 @@ class MediaDownloader : Feature("MediaDownloader", loadParams = FeatureLoadParam BitmojiSelfie.getBitmojiSelfie(it.bitmojiSelfieId!!, it.bitmojiAvatarId!!, BitmojiSelfie.BitmojiSelfieType.THREE_D) } - val downloadLogging = context.config.options(ConfigProperty.DOWNLOAD_LOGGING) - if (downloadLogging["started"] == true) { + val downloadLogging by context.config.downloader.logging + if (downloadLogging.contains("started")) { context.shortToast(context.translation["download_processor.download_started_toast"]) } @@ -83,7 +82,7 @@ class MediaDownloader : Feature("MediaDownloader", loadParams = FeatureLoadParam return DownloadManagerClient( context = context, metadata = DownloadMetadata( - mediaIdentifier = if (context.config.options(ConfigProperty.DOWNLOAD_OPTIONS)["allow_duplicate"] == false) { + mediaIdentifier = if (!context.config.downloader.allowDuplicate.get()) { generatedHash } else null, mediaDisplaySource = mediaDisplaySource, @@ -93,19 +92,19 @@ class MediaDownloader : Feature("MediaDownloader", loadParams = FeatureLoadParam ), callback = object: DownloadCallback.Stub() { override fun onSuccess(outputFile: String) { - if (downloadLogging["success"] != true) return + if (!downloadLogging.contains("success")) return Logger.debug("onSuccess: outputFile=$outputFile") context.shortToast(context.translation.format("download_processor.saved_toast", "path" to outputFile.split("/").takeLast(2).joinToString("/"))) } override fun onProgress(message: String) { - if (downloadLogging["progress"] != true) return + if (!downloadLogging.contains("progress")) return Logger.debug("onProgress: message=$message") context.shortToast(message) } override fun onFailure(message: String, throwable: String?) { - if (downloadLogging["failure"] != true) return + if (!downloadLogging.contains("failure")) return Logger.debug("onFailure: message=$message, throwable=$throwable") throwable?.let { context.longToast((message + it.takeIf { it.isNotEmpty() }.orEmpty())) @@ -118,13 +117,13 @@ class MediaDownloader : Feature("MediaDownloader", loadParams = FeatureLoadParam } private fun canMergeOverlay(): Boolean { - if (context.config.options(ConfigProperty.DOWNLOAD_OPTIONS)["merge_overlay"] == false) return false + if (!context.config.downloader.autoDownloadOptions.get().contains("merge_overlay")) return false return isFFmpegPresent } //TODO: implement subfolder argument private fun createNewFilePath(hexHash: String, mediaDisplayType: String?, pathPrefix: String): String { - val downloadOptions = context.config.options(ConfigProperty.DOWNLOAD_OPTIONS) + val pathFormat by context.config.downloader.pathFormat val sanitizedPathPrefix = pathPrefix .replace(" ", "_") .replace(Regex("[\\\\:*?\"<>|]"), "") @@ -142,21 +141,21 @@ class MediaDownloader : Feature("MediaDownloader", loadParams = FeatureLoadParam } } - if (downloadOptions["create_user_folder"] == true) { + if (pathFormat.contains("create_user_folder")) { finalPath.append(sanitizedPathPrefix).append("/") } - if (downloadOptions["append_hash"] == true) { + if (pathFormat.contains("append_hash")) { appendFileName(hexHash) } mediaDisplayType?.let { - if (downloadOptions["append_type"] == true) { + if (pathFormat.contains("append_type")) { appendFileName(it.lowercase().replace(" ", "-")) } } - if (downloadOptions["append_username"] == true) { + if (pathFormat.contains("append_username")) { appendFileName(sanitizedPathPrefix) } - if (downloadOptions["append_date_time"] == true) { + if (pathFormat.contains("append_date_time")) { appendFileName(currentDateTime) } @@ -377,8 +376,8 @@ class MediaDownloader : Feature("MediaDownloader", loadParams = FeatureLoadParam } private fun canAutoDownload(keyFilter: String? = null): Boolean { - val options = context.config.options(ConfigProperty.AUTO_DOWNLOAD_OPTIONS) - return options.filter { it.value }.any { keyFilter == null || it.key.contains(keyFilter, true) } + val options by context.config.downloader.autoDownloadOptions + return options.any { keyFilter == null || it.contains(keyFilter, true) } } override fun asyncOnActivityCreate() { diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/AmoledDarkMode.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/AmoledDarkMode.kt index 29fe89934..8d5f1b355 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/AmoledDarkMode.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/AmoledDarkMode.kt @@ -14,7 +14,7 @@ import me.rhunk.snapenhance.hook.hook class AmoledDarkMode : Feature("Amoled Dark Mode", loadParams = FeatureLoadParams.ACTIVITY_CREATE_SYNC) { @SuppressLint("DiscouragedApi") override fun onActivityCreate() { - if (!context.config.bool(ConfigProperty.AMOLED_DARK_MODE)) return + if (!context.config.userInterface.amoledDarkMode.get()) return val attributeCache = mutableMapOf() fun getAttribute(name: String): Int { diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/AppPasscode.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/AppPasscode.kt index 8cbc513a4..2d20708cf 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/AppPasscode.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/AppPasscode.kt @@ -27,7 +27,9 @@ class AppPasscode : Feature("App Passcode", loadParams = FeatureLoadParams.ACTIV fun lock() { if (isLocked) return isLocked = true - val passcode = context.config.string(ConfigProperty.APP_PASSCODE).also { if (it.isEmpty()) return } + val passcode by context.config.experimental.appPasscode.also { + if (it.getNullable()?.isEmpty() != false) return + } val isDigitPasscode = passcode.all { it.isDigit() } val mainActivity = context.mainActivity!! @@ -89,7 +91,7 @@ class AppPasscode : Feature("App Passcode", loadParams = FeatureLoadParams.ACTIV lock() } - if (!context.config.bool(ConfigProperty.APP_LOCK_ON_RESUME)) return + if (!context.config.experimental.appLockOnResume.get()) return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { context.mainActivity?.registerActivityLifecycleCallbacks(object: android.app.Application.ActivityLifecycleCallbacks { diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/DeviceSpooferHook.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/DeviceSpooferHook.kt index c8da19eeb..7ccfecdf9 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/DeviceSpooferHook.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/DeviceSpooferHook.kt @@ -9,39 +9,29 @@ import me.rhunk.snapenhance.hook.Hooker class DeviceSpooferHook: Feature("device_spoofer", loadParams = FeatureLoadParams.ACTIVITY_CREATE_ASYNC) { override fun asyncOnActivityCreate() { - //FINGERPRINT - if(getFingerprint().isNotEmpty()) { + val fingerprint by context.config.spoof.device.fingerprint + val androidId by context.config.spoof.device.androidId + + if (fingerprint.isNotEmpty()) { val fingerprintClass = android.os.Build::class.java Hooker.hook(fingerprintClass, "FINGERPRINT", HookStage.BEFORE) { hookAdapter -> - hookAdapter.setResult(getFingerprint()) + hookAdapter.setResult(fingerprint) + Logger.debug("Fingerprint spoofed to $fingerprint") } Hooker.hook(fingerprintClass, "deriveFingerprint", HookStage.BEFORE) { hookAdapter -> - hookAdapter.setResult(getFingerprint()) + hookAdapter.setResult(fingerprint) + Logger.debug("Fingerprint spoofed to $fingerprint") } } - else { - Logger.xposedLog("Fingerprint is null, not spoofing") - } - - //ANDROID ID - if(getAndroidId().isNotEmpty()) { + + if (androidId.isNotEmpty()) { val settingsSecureClass = android.provider.Settings.Secure::class.java Hooker.hook(settingsSecureClass, "getString", HookStage.BEFORE) { hookAdapter -> if(hookAdapter.args()[1] == "android_id") { - hookAdapter.setResult(getAndroidId()) + hookAdapter.setResult(androidId) + Logger.debug("Android ID spoofed to $androidId") } } } - else { - Logger.xposedLog("Android ID is null, not spoofing") - } - } - private fun getFingerprint():String { - return context.config.string(ConfigProperty.FINGERPRINT) - } - - private fun getAndroidId():String { - return context.config.string(ConfigProperty.ANDROID_ID) } - } \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/InfiniteStoryBoost.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/InfiniteStoryBoost.kt index 1e5be29ff..c24fcd1ab 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/InfiniteStoryBoost.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/InfiniteStoryBoost.kt @@ -11,7 +11,7 @@ class InfiniteStoryBoost : Feature("InfiniteStoryBoost", loadParams = FeatureLoa val storyBoostStateClass = context.mappings.getMappedClass("StoryBoostStateClass") storyBoostStateClass.hookConstructor(HookStage.BEFORE, { - context.config.bool(ConfigProperty.INFINITE_STORY_BOOST) + context.config.experimental.infiniteStoryBoost.get() }) { param -> val startTimeMillis = param.arg(1) //reset timestamp if it's more than 24 hours diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/MeoPasscodeBypass.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/MeoPasscodeBypass.kt index 27074562e..2e0031b2f 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/MeoPasscodeBypass.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/MeoPasscodeBypass.kt @@ -14,7 +14,7 @@ class MeoPasscodeBypass : Feature("Meo Passcode Bypass", loadParams = FeatureLoa context.androidContext.classLoader.loadClass(bcrypt["class"].toString()), bcrypt["hashMethod"].toString(), HookStage.BEFORE, - { context.config.bool(ConfigProperty.MEO_PASSCODE_BYPASS) }, + { context.config.experimental.meoPasscodeBypass.get() }, ) { param -> //set the hash to the result of the method param.setResult(param.arg(1)) diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/UnlimitedMultiSnap.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/UnlimitedMultiSnap.kt index 783a6bad0..a5d749bc0 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/UnlimitedMultiSnap.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/UnlimitedMultiSnap.kt @@ -10,7 +10,7 @@ import me.rhunk.snapenhance.util.setObjectField class UnlimitedMultiSnap : Feature("UnlimitedMultiSnap", loadParams = FeatureLoadParams.ACTIVITY_CREATE_ASYNC) { override fun asyncOnActivityCreate() { android.util.Pair::class.java.hookConstructor(HookStage.AFTER, { - context.config.bool(ConfigProperty.UNLIMITED_MULTI_SNAP) + context.config.experimental.unlimitedMultiSnap.get() }) { param -> val first = param.arg(0) val second = param.arg(1) diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/privacy/DisableMetrics.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/privacy/DisableMetrics.kt index f9b9a7a88..e4bef80de 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/privacy/DisableMetrics.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/privacy/DisableMetrics.kt @@ -10,11 +10,10 @@ import me.rhunk.snapenhance.hook.Hooker class DisableMetrics : Feature("DisableMetrics", loadParams = FeatureLoadParams.INIT_SYNC) { override fun init() { - val disableMetricsFilter: (HookAdapter) -> Boolean = { - context.config.bool(ConfigProperty.DISABLE_METRICS) - } + val disableMetrics by context.config.global.disableMetrics - Hooker.hook(context.classCache.unifiedGrpcService, "unaryCall", HookStage.BEFORE, disableMetricsFilter) { param -> + Hooker.hook(context.classCache.unifiedGrpcService, "unaryCall", HookStage.BEFORE, + { disableMetrics }) { param -> val url: String = param.arg(0) if (url.endsWith("snapchat.valis.Valis/SendClientUpdate") || url.endsWith("targetingQuery") @@ -23,7 +22,8 @@ class DisableMetrics : Feature("DisableMetrics", loadParams = FeatureLoadParams. } } - Hooker.hook(context.classCache.networkApi, "submit", HookStage.BEFORE, disableMetricsFilter) { param -> + Hooker.hook(context.classCache.networkApi, "submit", HookStage.BEFORE, + { disableMetrics }) { param -> val httpRequest: Any = param.arg(0) val url = XposedHelpers.getObjectField(httpRequest, "mUrl").toString() /*if (url.contains("resolve?co=")) { diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/privacy/PreventMessageSending.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/privacy/PreventMessageSending.kt index 9c88c98f2..22008590b 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/privacy/PreventMessageSending.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/privacy/PreventMessageSending.kt @@ -11,16 +11,15 @@ import me.rhunk.snapenhance.hook.hook class PreventMessageSending : Feature("Prevent message sending", loadParams = FeatureLoadParams.ACTIVITY_CREATE_ASYNC) { override fun asyncOnActivityCreate() { + val preventMessageSending by context.config.messaging.preventMessageSending + context.classCache.conversationManager.hook("updateMessage", HookStage.BEFORE) { param -> val messageUpdate = param.arg(2).toString(); - val options by lazy { - context.config.options(ConfigProperty.PREVENT_SENDING_MESSAGES) - } - if (messageUpdate == "SCREENSHOT" && options["chat_screenshot"] == true) { + if (messageUpdate == "SCREENSHOT" && preventMessageSending.contains("chat_screenshot")) { param.setResult(null) } - if (messageUpdate == "SCREEN_RECORD" && options["chat_screen_record"] == true) { + if (messageUpdate == "SCREEN_RECORD" && preventMessageSending.contains("chat_screen_record")) { param.setResult(null) } } @@ -29,9 +28,8 @@ class PreventMessageSending : Feature("Prevent message sending", loadParams = Fe val message = MessageContent(param.arg(1)) val contentType = message.contentType val associatedType = NotificationType.fromContentType(contentType) ?: return@hook - val options = context.config.options(ConfigProperty.PREVENT_SENDING_MESSAGES) - if (options[associatedType.key] == true) { + if (preventMessageSending.contains(associatedType.key)) { Logger.debug("Preventing message sending for $associatedType") param.setResult(null) } diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/AnonymousStoryViewing.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/AnonymousStoryViewing.kt index b50daeb5c..f9e9adc5c 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/AnonymousStoryViewing.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/AnonymousStoryViewing.kt @@ -9,7 +9,8 @@ import me.rhunk.snapenhance.util.getObjectField class AnonymousStoryViewing : Feature("Anonymous Story Viewing", loadParams = FeatureLoadParams.ACTIVITY_CREATE_ASYNC) { override fun asyncOnActivityCreate() { - Hooker.hook(context.classCache.networkApi,"submit", HookStage.BEFORE, { context.config.bool(ConfigProperty.ANONYMOUS_STORY_VIEW) }) { + val anonymousStoryViewProperty by context.config.messaging.anonymousStoryViewing + Hooker.hook(context.classCache.networkApi,"submit", HookStage.BEFORE, { anonymousStoryViewProperty }) { val httpRequest: Any = it.arg(0) val url = httpRequest.getObjectField("mUrl") as String if (url.endsWith("readreceipt-indexer/batchuploadreadreceipts") || url.endsWith("v2/batch_cta")) { diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/MessageLogger.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/MessageLogger.kt index 1e19d3c09..8f50a24c6 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/MessageLogger.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/MessageLogger.kt @@ -52,11 +52,6 @@ class MessageLogger : Feature("MessageLogger", @OptIn(ExperimentalTime::class) override fun asyncOnActivityCreate() { - ConfigProperty.MESSAGE_LOGGER.valueContainer.addPropertyChangeListener { - context.config.writeConfig() - context.softRestartApp() - } - if (!context.database.hasArroyo()) { return } @@ -130,9 +125,8 @@ class MessageLogger : Feature("MessageLogger", } override fun init() { - Hooker.hookConstructor(context.classCache.message, HookStage.AFTER, { - context.config.bool(ConfigProperty.MESSAGE_LOGGER) - }) { param -> + val messageLogger by context.config.messaging.messageLogger + Hooker.hookConstructor(context.classCache.message, HookStage.AFTER, { messageLogger }) { param -> processSnapMessage(param.thisObject()) } } diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/PreventReadReceipts.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/PreventReadReceipts.kt index b5c6ed341..212c53e3f 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/PreventReadReceipts.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/PreventReadReceipts.kt @@ -9,8 +9,9 @@ import me.rhunk.snapenhance.hook.Hooker class PreventReadReceipts : Feature("PreventReadReceipts", loadParams = FeatureLoadParams.ACTIVITY_CREATE_SYNC) { override fun onActivityCreate() { + val preventReadReceipts by context.config.messaging.preventReadReceipts val isConversationInStealthMode: (SnapUUID) -> Boolean = hook@{ - if (context.config.bool(ConfigProperty.PREVENT_READ_RECEIPTS)) return@hook true + if (preventReadReceipts) return@hook true context.feature(StealthMode::class).isStealth(it.toString()) } diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/AutoSave.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/AutoSave.kt index 61002faaa..047f97790 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/AutoSave.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/AutoSave.kt @@ -32,6 +32,10 @@ class AutoSave : Feature("Auto Save", loadParams = FeatureLoadParams.ACTIVITY_CR context.classCache.conversationManager.methods.first { it.name == "fetchConversationWithMessagesPaginated" } } + private val autoSaveFilter by lazy { + context.config.messaging.autoSaveMessagesInConversations.get() + } + private fun saveMessage(conversationId: SnapUUID, message: Message) { val messageId = message.messageDescriptor.messageId if (messageLogger.isMessageRemoved(messageId)) return @@ -62,11 +66,11 @@ class AutoSave : Feature("Auto Save", loadParams = FeatureLoadParams.ACTIVITY_CR if (message.messageMetadata.savedBy.any { uuid -> uuid.toString() == myUserId }) return false val contentType = message.messageContent.contentType.toString() - return context.config.options(ConfigProperty.AUTO_SAVE_MESSAGES).filter { it.value }.any { it.key == contentType } + return autoSaveFilter.any { it == contentType } } private fun canSave(): Boolean { - if (context.config.options(ConfigProperty.AUTO_SAVE_MESSAGES).none { it.value }) return false + if (autoSaveFilter.isEmpty()) return false with(context.feature(Messaging::class)) { if (openedConversationUUID == null) return@canSave false diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/CameraTweaks.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/CameraTweaks.kt index 3f1a1bf60..2efb85f76 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/CameraTweaks.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/CameraTweaks.kt @@ -16,16 +16,16 @@ import me.rhunk.snapenhance.hook.hookConstructor class CameraTweaks : Feature("Camera Tweaks", loadParams = FeatureLoadParams.ACTIVITY_CREATE_SYNC) { companion object { - val resolutions = listOf("OFF", "3264x2448", "3264x1840", "3264x1504", "2688x1512", "2560x1920", "2448x2448", "2340x1080", "2160x1080", "1920x1440", "1920x1080", "1600x1200", "1600x960", "1600x900", "1600x736", "1600x720", "1560x720", "1520x720", "1440x1080", "1440x720", "1280x720", "1080x1080", "1080x720", "960x720", "720x720", "720x480", "640x480", "352x288", "320x240", "176x144") + val resolutions = listOf("3264x2448", "3264x1840", "3264x1504", "2688x1512", "2560x1920", "2448x2448", "2340x1080", "2160x1080", "1920x1440", "1920x1080", "1600x1200", "1600x960", "1600x900", "1600x736", "1600x720", "1560x720", "1520x720", "1440x1080", "1440x720", "1280x720", "1080x1080", "1080x720", "960x720", "720x720", "720x480", "640x480", "352x288", "320x240", "176x144") } - private fun parseResolution(resolution: String): IntArray? { - return resolution.takeIf { resolution != "OFF" }?.split("x")?.map { it.toInt() }?.toIntArray() + private fun parseResolution(resolution: String): IntArray { + return resolution.split("x").map { it.toInt() }.toIntArray() } @SuppressLint("MissingPermission", "DiscouragedApi") override fun onActivityCreate() { - if (context.config.bool(ConfigProperty.CAMERA_DISABLE)) { + if (context.config.camera.disable.get()) { ContextWrapper::class.java.hook("checkPermission", HookStage.BEFORE) { param -> val permission = param.arg(0) if (permission == Manifest.permission.CAMERA) { @@ -39,16 +39,16 @@ class CameraTweaks : Feature("Camera Tweaks", loadParams = FeatureLoadParams.ACT } ConfigEnumKeys.hookAllEnums(context.mappings.getMappedClass("enums", "CAMERA")) { - if (key == "FORCE_CAMERA_HIGHEST_FPS" && context.config.bool(ConfigProperty.FORCE_HIGHEST_FRAME_RATE)) { + if (key == "FORCE_CAMERA_HIGHEST_FPS" && context.config.camera.forceHighestFrameRate.get()) { set(true) } - if (key == "MEDIA_RECORDER_MAX_QUALITY_LEVEL" && context.config.bool(ConfigProperty.FORCE_CAMERA_SOURCE_ENCODING)) { + if (key == "MEDIA_RECORDER_MAX_QUALITY_LEVEL" && context.config.camera.forceCameraSourceEncoding.get()) { value!!.javaClass.enumConstants?.let { enumData -> set(enumData.filter { it.toString() == "LEVEL_MAX" }) } } } - val previewResolutionConfig = parseResolution(context.config.state(ConfigProperty.OVERRIDE_PREVIEW_RESOLUTION)) - val captureResolutionConfig = parseResolution(context.config.state(ConfigProperty.OVERRIDE_PICTURE_RESOLUTION)) + val previewResolutionConfig = context.config.camera.overridePreviewResolution.getNullable()?.let { parseResolution(it) } + val captureResolutionConfig = context.config.camera.overridePictureResolution.getNullable()?.let { parseResolution(it) } context.mappings.getMappedClass("ScCameraSettings").hookConstructor(HookStage.BEFORE) { param -> val previewResolution = ScSize(param.argNullable(2)) diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/DisableVideoLengthRestriction.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/DisableVideoLengthRestriction.kt index 9c63cef59..568fc32fe 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/DisableVideoLengthRestriction.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/DisableVideoLengthRestriction.kt @@ -9,10 +9,9 @@ import me.rhunk.snapenhance.hook.Hooker class DisableVideoLengthRestriction : Feature("DisableVideoLengthRestriction", loadParams = FeatureLoadParams.ACTIVITY_CREATE_ASYNC) { override fun asyncOnActivityCreate() { val defaultMediaItem = context.mappings.getMappedClass("DefaultMediaItem") + val isState by context.config.global.disableVideoLengthRestrictions - Hooker.hookConstructor(defaultMediaItem, HookStage.BEFORE, { - context.config.bool(ConfigProperty.DISABLE_VIDEO_LENGTH_RESTRICTION) - }) { param -> + Hooker.hookConstructor(defaultMediaItem, HookStage.BEFORE, { isState }) { param -> //set the video length argument param.setArg(5, -1L) } diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/GalleryMediaSendOverride.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/GalleryMediaSendOverride.kt index 094c384e2..50bf2d26c 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/GalleryMediaSendOverride.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/GalleryMediaSendOverride.kt @@ -15,7 +15,7 @@ import me.rhunk.snapenhance.util.protobuf.ProtoReader class GalleryMediaSendOverride : Feature("Gallery Media Send Override", loadParams = FeatureLoadParams.INIT_SYNC) { override fun init() { Hooker.hook(context.classCache.conversationManager, "sendMessageWithContent", HookStage.BEFORE) { param -> - val overrideType = context.config.state(ConfigProperty.GALLERY_MEDIA_SEND_OVERRIDE).also { if (it == "OFF") return@hook } + val overrideType = context.config.messaging.galleryMediaSendOverride.getNullable() ?: return@hook val localMessageContent = MessageContent(param.arg(1)) if (localMessageContent.contentType != ContentType.EXTERNAL_MEDIA) return@hook diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/GooglePlayServicesDialogs.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/GooglePlayServicesDialogs.kt index d0e9b525b..d564e022e 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/GooglePlayServicesDialogs.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/GooglePlayServicesDialogs.kt @@ -11,7 +11,7 @@ import java.lang.reflect.Modifier class GooglePlayServicesDialogs : Feature("Disable GMS Dialogs", loadParams = FeatureLoadParams.ACTIVITY_CREATE_ASYNC) { override fun asyncOnActivityCreate() { - if (!context.config.bool(ConfigProperty.DISABLE_GOOGLE_PLAY_DIALOGS)) return + if (!context.config.global.disableGooglePlayDialogs.get()) return findClass("com.google.android.gms.common.GoogleApiAvailability").methods .first { Modifier.isStatic(it.modifiers) && it.returnType == AlertDialog::class.java }.let { method -> diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/LocationSpoofer.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/LocationSpoofer.kt index 550934c3b..7e082f6aa 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/LocationSpoofer.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/LocationSpoofer.kt @@ -6,6 +6,7 @@ import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams import me.rhunk.snapenhance.hook.HookStage import me.rhunk.snapenhance.hook.Hooker +import me.rhunk.snapenhance.hook.hook class LocationSpoofer: Feature("LocationSpoof", loadParams = FeatureLoadParams.ACTIVITY_CREATE_ASYNC) { override fun asyncOnActivityCreate() { @@ -13,52 +14,29 @@ class LocationSpoofer: Feature("LocationSpoof", loadParams = FeatureLoadParams.A val intent = param.argNullable(2) ?: return@hook val bundle = intent.getBundleExtra("location") ?: return@hook param.setResult(null) - val latitude = bundle.getFloat("latitude") - val longitude = bundle.getFloat("longitude") - with(context.config) { - get(ConfigProperty.LATITUDE).writeFrom(latitude.toString()) - get(ConfigProperty.LONGITUDE).writeFrom(longitude.toString()) - writeConfig() + with(context.config.spoof.location) { + latitude.set(bundle.getFloat("latitude")) + longitude.set(bundle.getFloat("longitude")) + + context.longToast("Location set to $latitude, $longitude") } - context.longToast("Location set to $latitude, $longitude") } - if (!context.config.bool(ConfigProperty.LOCATION_SPOOF)) return - val locationClass = android.location.Location::class.java - val locationManagerClass = android.location.LocationManager::class.java + if (context.config.spoof.location.globalState != true) return - Hooker.hook(locationClass, "getLatitude", HookStage.BEFORE) { hookAdapter -> - hookAdapter.setResult(getLatitude()) - } + val latitude by context.config.spoof.location.latitude + val longitude by context.config.spoof.location.longitude - Hooker.hook(locationClass, "getLongitude", HookStage.BEFORE) { hookAdapter -> - hookAdapter.setResult(getLongitude()) - } + val locationClass = android.location.Location::class.java + val locationManagerClass = android.location.LocationManager::class.java - Hooker.hook(locationClass, "getAccuracy", HookStage.BEFORE) { hookAdapter -> - hookAdapter.setResult(getAccuracy()) - } + locationClass.hook("getLatitude", HookStage.BEFORE) { it.setResult(latitude) } + locationClass.hook("getLongitude", HookStage.BEFORE) { it.setResult(longitude) } + locationClass.hook("getAccuracy", HookStage.BEFORE) { it.setResult(0.0F) } //Might be redundant because it calls isProviderEnabledForUser which we also hook, meaning if isProviderEnabledForUser returns true this will also return true - Hooker.hook(locationManagerClass, "isProviderEnabled", HookStage.BEFORE) { hookAdapter -> - hookAdapter.setResult(true) - } - - Hooker.hook(locationManagerClass, "isProviderEnabledForUser", HookStage.BEFORE) {hookAdapter -> - hookAdapter.setResult(true) - } - } - - private fun getLatitude():Double { - return context.config.string(ConfigProperty.LATITUDE).toDouble() - } - - private fun getLongitude():Double { - return context.config.string(ConfigProperty.LONGITUDE).toDouble() - } - - private fun getAccuracy():Float { - return 0.0f + locationManagerClass.hook("isProviderEnabled", HookStage.BEFORE) { it.setResult(true) } + locationManagerClass.hook("isProviderEnabledForUser", HookStage.BEFORE) { it.setResult(true) } } } \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/MediaQualityLevelOverride.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/MediaQualityLevelOverride.kt index 2bd58a6d5..2907594a1 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/MediaQualityLevelOverride.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/MediaQualityLevelOverride.kt @@ -11,10 +11,12 @@ class MediaQualityLevelOverride : Feature("MediaQualityLevelOverride", loadParam val enumQualityLevel = context.mappings.getMappedClass("EnumQualityLevel") val mediaQualityLevelProvider = context.mappings.getMappedMap("MediaQualityLevelProvider") + val forceMediaSourceQuality by context.config.global.forceMediaSourceQuality + context.androidContext.classLoader.loadClass(mediaQualityLevelProvider["class"].toString()).hook( mediaQualityLevelProvider["method"].toString(), HookStage.BEFORE, - { context.config.bool(ConfigProperty.FORCE_MEDIA_SOURCE_QUALITY) } + { forceMediaSourceQuality } ) { param -> param.setResult(enumQualityLevel.enumConstants.firstOrNull { it.toString() == "LEVEL_MAX" } ) } diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/Notifications.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/Notifications.kt index b5c20ebc5..c91664d26 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/Notifications.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/Notifications.kt @@ -65,6 +65,10 @@ class Notifications : Feature("Notifications", loadParams = FeatureLoadParams.IN context.androidContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager } + private val betterNotificationFilter by lazy { + context.config.global.betterNotifications.get() + } + private fun setNotificationText(notification: Notification, conversationId: String) { val messageText = StringBuilder().apply { cachedMessages.computeIfAbsent(conversationId) { mutableListOf() }.forEach { @@ -87,7 +91,6 @@ class Notifications : Feature("Notifications", loadParams = FeatureLoadParams.IN } private fun setupNotificationActionButtons(contentType: ContentType, conversationId: String, messageId: Long, notificationData: NotificationData) { - val betterNotifications = context.config.options(ConfigProperty.BETTER_NOTIFICATIONS) val notificationBuilder = XposedHelpers.newInstance( Notification.Builder::class.java, @@ -115,7 +118,7 @@ class Notifications : Feature("Notifications", loadParams = FeatureLoadParams.IN } newAction("Reply", ACTION_REPLY, { - betterNotifications["reply_button"] == true && contentType == ContentType.CHAT + betterNotificationFilter.contains("reply_button") && contentType == ContentType.CHAT }) { val chatReplyInput = RemoteInput.Builder("chat_reply_input") .setLabel("Reply") @@ -124,7 +127,7 @@ class Notifications : Feature("Notifications", loadParams = FeatureLoadParams.IN } newAction("Download", ACTION_DOWNLOAD, { - betterNotifications["download_button"] == true && (contentType == ContentType.EXTERNAL_MEDIA || contentType == ContentType.SNAP) + betterNotificationFilter.contains("download_button") && (contentType == ContentType.EXTERNAL_MEDIA || contentType == ContentType.SNAP) }) {} notificationBuilder.setActions(*actions.toTypedArray()) @@ -282,8 +285,7 @@ class Notifications : Feature("Notifications", loadParams = FeatureLoadParams.IN val notificationType = extras.getString("notification_type") ?: return@hook val conversationId = extras.getString("conversation_id") ?: return@hook - if (context.config.options(ConfigProperty.BETTER_NOTIFICATIONS) - .filter { it.value }.map { it.key.uppercase() }.none { + if (betterNotificationFilter.map { it.uppercase() }.none { notificationType.contains(it) }) return@hook @@ -319,15 +321,15 @@ class Notifications : Feature("Notifications", loadParams = FeatureLoadParams.IN } findClass("com.google.firebase.messaging.FirebaseMessagingService").run { + val states by context.config.global.notificationBlacklist methods.first { it.declaringClass == this && it.returnType == Void::class.javaPrimitiveType && it.parameterCount == 1 && it.parameterTypes[0] == Intent::class.java } .hook(HookStage.BEFORE) { param -> val intent = param.argNullable(0) ?: return@hook val messageType = intent.getStringExtra("type") ?: return@hook - val states = context.config.options(ConfigProperty.NOTIFICATION_BLACKLIST) Logger.xposedLog("received message type: $messageType") - if (states[messageType.replaceFirst("mischief_", "")] == true) { + if (states.contains(messageType.replaceFirst("mischief_", ""))) { param.setResult(null) } } diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/SnapchatPlus.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/SnapchatPlus.kt index 944798af6..09cba1483 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/SnapchatPlus.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/SnapchatPlus.kt @@ -11,7 +11,7 @@ class SnapchatPlus: Feature("SnapchatPlus", loadParams = FeatureLoadParams.ACTIV private val expirationTimeMillis = (System.currentTimeMillis() + 15552000000L) override fun asyncOnActivityCreate() { - if (!context.config.bool(ConfigProperty.SNAPCHAT_PLUS)) return + if (!context.config.global.snapchatPlus.get()) return val subscriptionInfoClass = context.mappings.getMappedClass("SubscriptionInfoClass") diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/UnlimitedSnapViewTime.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/UnlimitedSnapViewTime.kt index 50c686258..99dba59e8 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/UnlimitedSnapViewTime.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/UnlimitedSnapViewTime.kt @@ -14,9 +14,8 @@ import me.rhunk.snapenhance.util.protobuf.ProtoReader class UnlimitedSnapViewTime : Feature("UnlimitedSnapViewTime", loadParams = FeatureLoadParams.ACTIVITY_CREATE_SYNC) { override fun onActivityCreate() { - Hooker.hookConstructor(context.classCache.message, HookStage.AFTER, { - context.config.bool(ConfigProperty.UNLIMITED_SNAP_VIEW_TIME) - }) { param -> + val state by context.config.messaging.unlimitedSnapViewTime + Hooker.hookConstructor(context.classCache.message, HookStage.AFTER, { state }) { param -> val message = Message(param.thisObject()) if (message.messageState != MessageState.COMMITTED) return@hookConstructor if (message.messageContent.contentType != ContentType.SNAP) return@hookConstructor diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/StartupPageOverride.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/StartupPageOverride.kt index 5bf968a8b..358c75a2e 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/StartupPageOverride.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/StartupPageOverride.kt @@ -25,9 +25,7 @@ class StartupPageOverride : Feature("StartupPageOverride", loadParams = FeatureL */ override fun onActivityCreate() { - val ngsIconName = context.config.state(ConfigProperty.STARTUP_PAGE_OVERRIDE).also { - if (it == "OFF") return - } + val ngsIconName = context.config.userInterface.startupTab.getNullable() ?: return context.androidContext.classLoader.loadClass("com.snap.mushroom.MainActivity").apply { hook("onPostCreate", HookStage.AFTER) { diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/UITweaks.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/UITweaks.kt index 3e64d9e95..2c0d09c43 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/UITweaks.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/UITweaks.kt @@ -36,10 +36,10 @@ class UITweaks : Feature("UITweaks", loadParams = FeatureLoadParams.ACTIVITY_CRE @SuppressLint("DiscouragedApi", "InternalInsetResource") override fun onActivityCreate() { - val blockAds = context.config.bool(ConfigProperty.BLOCK_ADS) - val hiddenElements = context.config.options(ConfigProperty.HIDE_UI_ELEMENTS) - val hideStorySection = context.config.options(ConfigProperty.HIDE_STORY_SECTION) - val isImmersiveCamera = context.config.bool(ConfigProperty.IMMERSIVE_CAMERA_PREVIEW) + val blockAds by context.config.userInterface.blockAds + val hiddenElements by context.config.userInterface.hideUiComponents + val hideStorySections by context.config.userInterface.hideStorySections + val isImmersiveCamera by context.config.camera.immersiveCameraPreview val displayMetrics = context.resources.displayMetrics @@ -61,11 +61,11 @@ class UITweaks : Feature("UITweaks", loadParams = FeatureLoadParams.ACTIVITY_CRE Hooker.hook(View::class.java, "setVisibility", HookStage.BEFORE) { methodParam -> val viewId = (methodParam.thisObject() as View).id - if (viewId == chatNoteRecordButton && hiddenElements["remove_voice_record_button"] == true) { + if (viewId == chatNoteRecordButton && hiddenElements.contains("hide_voice_record_button")) { methodParam.setArg(0, View.GONE) } if (viewId == callButton1 || viewId == callButton2) { - if (hiddenElements["remove_call_buttons"] == false) return@hook + if (!hiddenElements.contains("hide_call_buttons")) return@hook methodParam.setArg(0, View.GONE) } } @@ -79,7 +79,7 @@ class UITweaks : Feature("UITweaks", loadParams = FeatureLoadParams.ACTIVITY_CRE val view: View = param.arg(0) val viewId = view.id - if (hideStorySection["hide_for_you"] == true) { + if (hideStorySections.contains("hide_for_you")) { if (viewId == getIdentifier("df_large_story", "id") || viewId == getIdentifier("df_promoted_story", "id")) { hideStorySection(param) @@ -90,12 +90,12 @@ class UITweaks : Feature("UITweaks", loadParams = FeatureLoadParams.ACTIVITY_CRE } } - if (hideStorySection["hide_friends"] == true && viewId == getIdentifier("friend_card_frame", "id")) { + if (hideStorySections.contains("hide_friends") && viewId == getIdentifier("friend_card_frame", "id")) { hideStorySection(param) } //mappings? - if (hideStorySection["hide_friend_suggestions"] == true && view.javaClass.superclass?.name?.endsWith("StackDrawLayout") == true) { + if (hideStorySections.contains("hide_friend_suggestions") && view.javaClass.superclass?.name?.endsWith("StackDrawLayout") == true) { val layoutParams = view.layoutParams as? FrameLayout.LayoutParams ?: return@hook if (layoutParams.width == -1 && layoutParams.height == -2 && @@ -108,7 +108,7 @@ class UITweaks : Feature("UITweaks", loadParams = FeatureLoadParams.ACTIVITY_CRE } } - if (hideStorySection["hide_following"] == true && (viewId == getIdentifier("df_small_story", "id")) + if (hideStorySections.contains("hide_following") && (viewId == getIdentifier("df_small_story", "id")) ) { hideStorySection(param) } @@ -134,26 +134,26 @@ class UITweaks : Feature("UITweaks", loadParams = FeatureLoadParams.ACTIVITY_CRE } } - if (viewId == chatNoteRecordButton && hiddenElements["remove_voice_record_button"] == true) { + if (viewId == chatNoteRecordButton && hiddenElements.contains("hide_voice_record_button")) { view.isEnabled = false view.setWillNotDraw(true) } - if (getIdentifier("chat_input_bar_cognac", "id") == viewId && hiddenElements["remove_cognac_button"] == true) { + if (getIdentifier("chat_input_bar_cognac", "id") == viewId && hiddenElements.contains("hide_cognac_button")) { view.visibility = View.GONE } - if (getIdentifier("chat_input_bar_sticker", "id") == viewId && hiddenElements["remove_stickers_button"] == true) { + if (getIdentifier("chat_input_bar_sticker", "id") == viewId && hiddenElements.contains("hide_stickers_button")) { view.visibility = View.GONE } - if (getIdentifier("chat_input_bar_sharing_drawer_button", "id") == viewId && hiddenElements["remove_live_location_share_button"] == true) { + if (getIdentifier("chat_input_bar_sharing_drawer_button", "id") == viewId && hiddenElements.contains("hide_live_location_share_button")) { param.setResult(null) } if (viewId == callButton1 || viewId == callButton2) { - if (hiddenElements["remove_call_buttons"] == false) return@hook + if (!hiddenElements.contains("hide_call_buttons")) return@hook if (view.visibility == View.GONE) return@hook } if (viewId == callButtonsStub) { - if (hiddenElements["remove_call_buttons"] == false) return@hook + if (!hiddenElements.contains("hide_call_buttons")) return@hook param.setResult(null) } } diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/hook/Hooker.kt b/core/src/main/kotlin/me/rhunk/snapenhance/hook/Hooker.kt index b3ee2a7a7..8fbe29d99 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/hook/Hooker.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/hook/Hooker.kt @@ -4,11 +4,14 @@ import de.robv.android.xposed.XC_MethodHook import de.robv.android.xposed.XposedBridge import java.lang.reflect.Member +typealias HookFilter = (HookAdapter) -> Boolean +typealias HookConsumer = (HookAdapter) -> Unit + object Hooker { inline fun newMethodHook( stage: HookStage, - crossinline consumer: (HookAdapter) -> Unit, - crossinline filter: ((HookAdapter) -> Boolean) = { true } + crossinline consumer: HookConsumer, + crossinline filter: HookFilter = { true } ): XC_MethodHook { return if (stage == HookStage.BEFORE) object : XC_MethodHook() { override fun beforeHookedMethod(param: MethodHookParam<*>) { @@ -25,21 +28,21 @@ object Hooker { clazz: Class<*>, methodName: String, stage: HookStage, - crossinline consumer: (HookAdapter) -> Unit + crossinline consumer: HookConsumer ): Set = hook(clazz, methodName, stage, { true }, consumer) inline fun hook( clazz: Class<*>, methodName: String, stage: HookStage, - crossinline filter: (HookAdapter) -> Boolean, - crossinline consumer: (HookAdapter) -> Unit + crossinline filter: HookFilter, + crossinline consumer: HookConsumer ): Set = XposedBridge.hookAllMethods(clazz, methodName, newMethodHook(stage, consumer, filter)) inline fun hook( member: Member, stage: HookStage, - crossinline consumer: (HookAdapter) -> Unit + crossinline consumer: HookConsumer ): XC_MethodHook.Unhook { return hook(member, stage, { true }, consumer) } @@ -47,8 +50,8 @@ object Hooker { inline fun hook( member: Member, stage: HookStage, - crossinline filter: ((HookAdapter) -> Boolean), - crossinline consumer: (HookAdapter) -> Unit + crossinline filter: HookFilter, + crossinline consumer: HookConsumer ): XC_MethodHook.Unhook { return XposedBridge.hookMethod(member, newMethodHook(stage, consumer, filter)) } @@ -57,7 +60,7 @@ object Hooker { inline fun hookConstructor( clazz: Class<*>, stage: HookStage, - crossinline consumer: (HookAdapter) -> Unit + crossinline consumer: HookConsumer ) { XposedBridge.hookAllConstructors(clazz, newMethodHook(stage, consumer)) } @@ -65,8 +68,8 @@ object Hooker { inline fun hookConstructor( clazz: Class<*>, stage: HookStage, - crossinline filter: ((HookAdapter) -> Boolean), - crossinline consumer: (HookAdapter) -> Unit + crossinline filter: HookFilter, + crossinline consumer: HookConsumer ) { XposedBridge.hookAllConstructors(clazz, newMethodHook(stage, consumer, filter)) } @@ -76,7 +79,7 @@ object Hooker { instance: Any, methodName: String, stage: HookStage, - crossinline hookConsumer: (HookAdapter) -> Unit + crossinline hookConsumer: HookConsumer ) { val unhooks: MutableSet = HashSet() hook(clazz, methodName, stage) { param-> @@ -92,7 +95,7 @@ object Hooker { clazz: Class<*>, methodName: String, stage: HookStage, - crossinline hookConsumer: (HookAdapter) -> Unit + crossinline hookConsumer: HookConsumer ) { val unhooks: MutableSet = HashSet() hook(clazz, methodName, stage) { param-> @@ -106,7 +109,7 @@ object Hooker { instance: Any, methodName: String, stage: HookStage, - crossinline hookConsumer: (HookAdapter) -> Unit + crossinline hookConsumer: HookConsumer ) { val unhooks: MutableSet = HashSet() hook(clazz, methodName, stage) { param-> @@ -119,35 +122,35 @@ object Hooker { inline fun Class<*>.hookConstructor( stage: HookStage, - crossinline consumer: (HookAdapter) -> Unit + crossinline consumer: HookConsumer ) = Hooker.hookConstructor(this, stage, consumer) inline fun Class<*>.hookConstructor( stage: HookStage, - crossinline filter: ((HookAdapter) -> Boolean), - crossinline consumer: (HookAdapter) -> Unit + crossinline filter: HookFilter, + crossinline consumer: HookConsumer ) = Hooker.hookConstructor(this, stage, filter, consumer) inline fun Class<*>.hook( methodName: String, stage: HookStage, - crossinline consumer: (HookAdapter) -> Unit + crossinline consumer: HookConsumer ): Set = Hooker.hook(this, methodName, stage, consumer) inline fun Class<*>.hook( methodName: String, stage: HookStage, - crossinline filter: (HookAdapter) -> Boolean, - crossinline consumer: (HookAdapter) -> Unit + crossinline filter: HookFilter, + crossinline consumer: HookConsumer ): Set = Hooker.hook(this, methodName, stage, filter, consumer) inline fun Member.hook( stage: HookStage, - crossinline consumer: (HookAdapter) -> Unit + crossinline consumer: HookConsumer ): XC_MethodHook.Unhook = Hooker.hook(this, stage, consumer) inline fun Member.hook( stage: HookStage, - crossinline filter: ((HookAdapter) -> Boolean), - crossinline consumer: (HookAdapter) -> Unit + crossinline filter: HookFilter, + crossinline consumer: HookConsumer ): XC_MethodHook.Unhook = Hooker.hook(this, stage, filter, consumer) \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ui/ItemHelper.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/ItemHelper.kt index c47437ff5..2d658aa6c 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/ui/ItemHelper.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/ui/ItemHelper.kt @@ -82,4 +82,6 @@ class ItemHelper( callback(value) } } + + } diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/ChatActionMenu.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/ChatActionMenu.kt index dcc16bbb2..bde676f48 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/ChatActionMenu.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/ChatActionMenu.kt @@ -104,7 +104,7 @@ class ChatActionMenu : AbstractMenu() { } } - if (context.config.bool(ConfigProperty.CHAT_DOWNLOAD_CONTEXT_MENU)) { + if (context.config.downloader.chatDownloadContextMenu.get()) { injectButton(Button(viewGroup.context).apply { text = this@ChatActionMenu.context.translation["chat_action_menu.preview_button"] setOnClickListener { @@ -127,7 +127,7 @@ class ChatActionMenu : AbstractMenu() { } //delete logged message button - if (context.config.bool(ConfigProperty.MESSAGE_LOGGER)) { + if (context.config.messaging.messageLogger.get()) { injectButton(Button(viewGroup.context).apply { text = this@ChatActionMenu.context.translation["chat_action_menu.delete_logged_message_button"] setOnClickListener { diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/FriendFeedInfoMenu.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/FriendFeedInfoMenu.kt index 55a1a4663..98d55864c 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/FriendFeedInfoMenu.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/FriendFeedInfoMenu.kt @@ -104,7 +104,7 @@ class FriendFeedInfoMenu : AbstractMenu() { //query message val messages: List? = context.database.getMessagesFromConversationId( conversationId, - context.config.int(ConfigProperty.MESSAGE_PREVIEW_LENGTH) + context.config.messaging.messagePreviewLength.get() )?.reversed() if (messages.isNullOrEmpty()) { @@ -240,12 +240,12 @@ class FriendFeedInfoMenu : AbstractMenu() { fun inject(viewModel: View, viewConsumer: ((View) -> Unit)) { val modContext = context - val friendFeedMenuOptions = context.config.options(ConfigProperty.FRIEND_FEED_MENU_BUTTONS) - if (friendFeedMenuOptions.none { it.value }) return + val friendFeedMenuOptions by context.config.userInterface.friendFeedMenuButtons + if (friendFeedMenuOptions.isEmpty()) return val (conversationId, targetUser) = getCurrentConversationInfo() - if (!context.config.bool(ConfigProperty.ENABLE_FRIEND_FEED_MENU_BAR)) { + if (!context.config.userInterface.enableFriendFeedMenuBar.get()) { //preview button val previewButton = Button(viewModel.context).apply { text = modContext.translation["friend_menu_option.preview"] @@ -272,7 +272,7 @@ class FriendFeedInfoMenu : AbstractMenu() { } } - if (friendFeedMenuOptions["anti_auto_save"] == true) { + if (friendFeedMenuOptions.contains("anti_auto_save")) { createToggleFeature(viewConsumer, "friend_menu_option.anti_auto_save", { context.feature(AntiAutoSave::class).isConversationIgnored(conversationId) }, @@ -282,7 +282,7 @@ class FriendFeedInfoMenu : AbstractMenu() { run { val userId = context.database.getFriendFeedInfoByConversationId(conversationId)?.friendUserId ?: return@run - if (friendFeedMenuOptions["auto_download_blacklist"] == true) { + if (friendFeedMenuOptions.contains("auto_download_blacklist")) { createToggleFeature(viewConsumer, "friend_menu_option.auto_download_blacklist", { context.feature(AntiAutoDownload::class).isUserIgnored(userId) }, @@ -291,10 +291,10 @@ class FriendFeedInfoMenu : AbstractMenu() { } } - if (friendFeedMenuOptions["stealth_mode"] == true) { + if (friendFeedMenuOptions.contains("stealth_mode")) { viewConsumer(stealthSwitch) } - if (friendFeedMenuOptions["conversation_info"] == true) { + if (friendFeedMenuOptions.contains("conversation_info")) { viewConsumer(previewButton) } return @@ -338,7 +338,7 @@ class FriendFeedInfoMenu : AbstractMenu() { }) } - if (friendFeedMenuOptions["auto_download_blacklist"] == true) { + if (friendFeedMenuOptions.contains("auto_download_blacklist")) { run { val userId = context.database.getFriendFeedInfoByConversationId(conversationId)?.friendUserId @@ -352,7 +352,7 @@ class FriendFeedInfoMenu : AbstractMenu() { } } - if (friendFeedMenuOptions["anti_auto_save"] == true) { + if (friendFeedMenuOptions.contains("anti_auto_save")) { //diskette createActionButton("\uD83D\uDCAC", isDisabled = !context.feature(AntiAutoSave::class) @@ -363,7 +363,7 @@ class FriendFeedInfoMenu : AbstractMenu() { } - if (friendFeedMenuOptions["stealth_mode"] == true) { + if (friendFeedMenuOptions.contains("stealth_mode")) { //eyes createActionButton( "\uD83D\uDC7B", @@ -376,7 +376,7 @@ class FriendFeedInfoMenu : AbstractMenu() { } } - if (friendFeedMenuOptions["conversation_info"] == true) { + if (friendFeedMenuOptions.contains("conversation_info")) { //user createActionButton("\uD83D\uDC64") { showPreview( diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/MenuViewInjector.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/MenuViewInjector.kt index d787e682c..a867fb5dd 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/MenuViewInjector.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/MenuViewInjector.kt @@ -124,7 +124,7 @@ class MenuViewInjector : Feature("MenuViewInjector", loadParams = FeatureLoadPar viewGroup.addOnAttachStateChangeListener(object: View.OnAttachStateChangeListener { override fun onViewAttachedToWindow(v: View) {} override fun onViewDetachedFromWindow(v: View) { - context.config.writeConfig() + //context.config.writeConfig() } }) return@hook @@ -132,10 +132,9 @@ class MenuViewInjector : Feature("MenuViewInjector", loadParams = FeatureLoadPar if (messaging.lastFetchConversationUUID == null || messaging.lastFetchConversationUserUUID == null) return@hook //filter by the slot index - if (viewGroup.getChildCount() != context.config.int(ConfigProperty.FRIEND_FEED_MENU_POSITION)) return@hook + if (viewGroup.getChildCount() != context.config.userInterface.friendFeedMenuPosition.get()) return@hook friendFeedInfoMenu.inject(viewGroup, originalAddView) } } } - } \ No newline at end of file From e2249417eb58aec283ec2439ea92f702df1421d9 Mon Sep 17 00:00:00 2001 From: rhunk <101876869+rhunk@users.noreply.github.com> Date: Tue, 1 Aug 2023 01:23:57 +0200 Subject: [PATCH 10/81] fix: device spoof global state --- .../kotlin/me/rhunk/snapenhance/core/config/ConfigContainer.kt | 1 - .../snapenhance/features/impl/experiments/DeviceSpooferHook.kt | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/config/ConfigContainer.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/ConfigContainer.kt index 983695f05..c806c423f 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/core/config/ConfigContainer.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/ConfigContainer.kt @@ -57,7 +57,6 @@ open class ConfigContainer( fun toJson(): JsonObject { val json = JsonObject() properties.forEach { (propertyKey, propertyValue) -> - Logger.debug("${propertyKey.name} => $propertyValue") val serializedValue = propertyValue.getNullable()?.let { propertyKey.dataProcessor.serializeAny(it) } json.add(propertyKey.name, serializedValue) } diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/DeviceSpooferHook.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/DeviceSpooferHook.kt index 7ccfecdf9..5f9a4839a 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/DeviceSpooferHook.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/DeviceSpooferHook.kt @@ -9,6 +9,8 @@ import me.rhunk.snapenhance.hook.Hooker class DeviceSpooferHook: Feature("device_spoofer", loadParams = FeatureLoadParams.ACTIVITY_CREATE_ASYNC) { override fun asyncOnActivityCreate() { + if (context.config.spoof.globalState != true) return + val fingerprint by context.config.spoof.device.fingerprint val androidId by context.config.spoof.device.androidId From 79d3bb5ba922e54f728391458932c528067cef58 Mon Sep 17 00:00:00 2001 From: rhunk <101876869+rhunk@users.noreply.github.com> Date: Tue, 1 Aug 2023 18:37:35 +0200 Subject: [PATCH 11/81] config refactor + ui --- app/src/main/AndroidManifest.xml | 10 - .../rhunk/snapenhance/manager/MainActivity.kt | 3 - .../rhunk/snapenhance/manager/Navigation.kt | 10 +- .../me/rhunk/snapenhance/manager/Section.kt | 11 +- .../manager/data/ManagerContext.kt | 1 - .../manager/sections/FeaturesSection.kt | 235 ---------- .../sections/features/CallbackAlias.kt | 4 + .../{ => sections/features}/Dialogs.kt | 76 ++-- .../sections/features/FeaturesSection.kt | 293 ++++++++++++ .../manager/util/SaveFolderChecker.kt | 1 - .../kotlin/me/rhunk/snapenhance/ModContext.kt | 1 - .../me/rhunk/snapenhance/SharedContext.kt | 6 - .../snapenhance/action/AbstractAction.kt | 4 +- .../action/impl/CheckForUpdates.kt | 3 +- .../rhunk/snapenhance/action/impl/OpenMap.kt | 3 +- .../rhunk/snapenhance/bridge/BridgeClient.kt | 2 +- .../bridge/wrapper/ConfigWrapper.kt | 73 --- .../bridge/wrapper/TranslationWrapper.kt | 13 - .../snapenhance/config/ConfigAccessor.kt | 62 --- .../snapenhance/config/ConfigCategory.kt | 14 - .../snapenhance/config/ConfigProperty.kt | 417 ------------------ .../rhunk/snapenhance/config/ConfigValue.kt | 22 - .../config/impl/ConfigIntegerValue.kt | 17 - .../config/impl/ConfigStateListValue.kt | 32 -- .../config/impl/ConfigStateSelection.kt | 22 - .../config/impl/ConfigStateValue.kt | 17 - .../config/impl/ConfigStringValue.kt | 21 - .../core/config/ConfigContainer.kt | 5 +- .../snapenhance/core/config/ConfigObjects.kt | 14 +- .../snapenhance/core/config/DataProcessors.kt | 2 +- .../snapenhance/core/config/impl/Global.kt | 2 +- .../core/config/impl/RootConfig.kt | 2 - .../snapenhance/download/DownloadProcessor.kt | 2 - .../features/impl/ConfigEnumKeys.kt | 5 - .../snapenhance/features/impl/Messaging.kt | 3 +- .../impl/downloader/MediaDownloader.kt | 1 - .../impl/experiments/AmoledDarkMode.kt | 1 - .../features/impl/experiments/AppPasscode.kt | 2 - .../impl/experiments/DeviceSpooferHook.kt | 1 - .../impl/experiments/InfiniteStoryBoost.kt | 1 - .../impl/experiments/MeoPasscodeBypass.kt | 1 - .../impl/experiments/UnlimitedMultiSnap.kt | 1 - .../features/impl/privacy/DisableMetrics.kt | 2 - .../impl/privacy/PreventMessageSending.kt | 1 - .../impl/spying/AnonymousStoryViewing.kt | 1 - .../features/impl/spying/MessageLogger.kt | 1 - .../impl/spying/PreventReadReceipts.kt | 1 - .../features/impl/tweaks/AutoSave.kt | 1 - .../features/impl/tweaks/CameraTweaks.kt | 1 - .../tweaks/DisableVideoLengthRestriction.kt | 1 - .../impl/tweaks/GalleryMediaSendOverride.kt | 2 - .../impl/tweaks/GooglePlayServicesDialogs.kt | 1 - .../features/impl/tweaks/LocationSpoofer.kt | 1 - .../impl/tweaks/MediaQualityLevelOverride.kt | 1 - .../features/impl/tweaks/Notifications.kt | 1 - .../features/impl/tweaks/SnapchatPlus.kt | 1 - .../impl/tweaks/UnlimitedSnapViewTime.kt | 1 - .../features/impl/ui/StartupPageOverride.kt | 1 - .../snapenhance/features/impl/ui/UITweaks.kt | 1 - .../snapenhance/manager/impl/ActionManager.kt | 2 +- .../snapenhance/manager/impl/ConfigManager.kt | 58 --- .../manager/impl/MappingManager.kt | 1 - .../me/rhunk/snapenhance/ui/ItemHelper.kt | 87 ---- .../snapenhance/ui/config/ConfigActivity.kt | 279 ------------ .../download/DebugSettingsLayoutInflater.kt | 11 +- .../ui/download/DownloadListAdapter.kt | 2 +- .../ui/download/DownloadManagerActivity.kt | 4 +- .../ui/menu/impl/ChatActionMenu.kt | 1 - .../ui/menu/impl/FriendFeedInfoMenu.kt | 1 - .../ui/menu/impl/MenuViewInjector.kt | 2 - .../ui/menu/impl/OperaContextActionMenu.kt | 2 +- .../ui/menu/impl/SettingsGearInjector.kt | 3 +- .../snapenhance/ui/menu/impl/SettingsMenu.kt | 23 +- .../ui/spoof/DeviceSpooferActivity.kt | 111 ----- .../util/export/MessageExporter.kt | 2 +- gradle/libs.versions.toml | 4 +- 76 files changed, 389 insertions(+), 1639 deletions(-) delete mode 100644 app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/FeaturesSection.kt create mode 100644 app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/features/CallbackAlias.kt rename app/src/main/kotlin/me/rhunk/snapenhance/manager/{ => sections/features}/Dialogs.kt (71%) create mode 100644 app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/features/FeaturesSection.kt delete mode 100644 core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/ConfigWrapper.kt delete mode 100644 core/src/main/kotlin/me/rhunk/snapenhance/config/ConfigAccessor.kt delete mode 100644 core/src/main/kotlin/me/rhunk/snapenhance/config/ConfigCategory.kt delete mode 100644 core/src/main/kotlin/me/rhunk/snapenhance/config/ConfigProperty.kt delete mode 100644 core/src/main/kotlin/me/rhunk/snapenhance/config/ConfigValue.kt delete mode 100644 core/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigIntegerValue.kt delete mode 100644 core/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigStateListValue.kt delete mode 100644 core/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigStateSelection.kt delete mode 100644 core/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigStateValue.kt delete mode 100644 core/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigStringValue.kt delete mode 100644 core/src/main/kotlin/me/rhunk/snapenhance/manager/impl/ConfigManager.kt delete mode 100644 core/src/main/kotlin/me/rhunk/snapenhance/ui/ItemHelper.kt delete mode 100644 core/src/main/kotlin/me/rhunk/snapenhance/ui/config/ConfigActivity.kt delete mode 100644 core/src/main/kotlin/me/rhunk/snapenhance/ui/spoof/DeviceSpooferActivity.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 48d16183a..63e45933c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -50,16 +50,6 @@ android:name=".ui.map.MapActivity" android:exported="true" android:excludeFromRecents="true" /> - - + }.onEach { (section, instance) -> + instance.enumSection = section instance.manager = context instance.navController = navController } } NavHost(navController, startDestination = startDestination.route, Modifier.padding(innerPadding)) { - sections.forEach { (section, instance) -> - composable(section.route) { - instance.Content() - } + sections.forEach { (_, instance) -> + instance.build(this) } } } diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/Section.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/Section.kt index 8cf1d2268..ab9d338cb 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/manager/Section.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/manager/Section.kt @@ -9,10 +9,12 @@ import androidx.compose.material.icons.filled.Stars import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.vector.ImageVector import androidx.navigation.NavController +import androidx.navigation.NavGraphBuilder +import androidx.navigation.compose.composable import me.rhunk.snapenhance.manager.data.ManagerContext -import me.rhunk.snapenhance.manager.sections.FeaturesSection import me.rhunk.snapenhance.manager.sections.HomeSection import me.rhunk.snapenhance.manager.sections.NotImplemented +import me.rhunk.snapenhance.manager.sections.features.FeaturesSection import kotlin.reflect.KClass enum class EnumSection( @@ -59,9 +61,16 @@ enum class EnumSection( open class Section { + lateinit var enumSection: EnumSection lateinit var manager: ManagerContext lateinit var navController: NavController @Composable open fun Content() { NotImplemented() } + + open fun build(navGraphBuilder: NavGraphBuilder) { + navGraphBuilder.composable(enumSection.route) { + Content() + } + } } \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/data/ManagerContext.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/data/ManagerContext.kt index ab6863e21..c8643a7c3 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/manager/data/ManagerContext.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/manager/data/ManagerContext.kt @@ -1,7 +1,6 @@ package me.rhunk.snapenhance.manager.data import android.content.Context -import me.rhunk.snapenhance.bridge.wrapper.ConfigWrapper import me.rhunk.snapenhance.bridge.wrapper.MappingsWrapper import me.rhunk.snapenhance.bridge.wrapper.TranslationWrapper import me.rhunk.snapenhance.core.config.ModConfig diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/FeaturesSection.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/FeaturesSection.kt deleted file mode 100644 index b0c60b7ba..000000000 --- a/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/FeaturesSection.kt +++ /dev/null @@ -1,235 +0,0 @@ -package me.rhunk.snapenhance.manager.sections - -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxHeight -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.widthIn -import androidx.compose.foundation.layout.wrapContentWidth -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.MaterialTheme -import androidx.compose.material.SnackbarHost -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.OpenInNew -import androidx.compose.material.icons.rounded.Save -import androidx.compose.material.rememberScaffoldState -import androidx.compose.material3.Card -import androidx.compose.material3.FilledIconButton -import androidx.compose.material3.FloatingActionButton -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton -import androidx.compose.material3.Scaffold -import androidx.compose.material3.Switch -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import androidx.compose.ui.window.Dialog -import androidx.compose.ui.window.DialogProperties -import kotlinx.coroutines.launch -import me.rhunk.snapenhance.Logger -import me.rhunk.snapenhance.config.ConfigProperty -import me.rhunk.snapenhance.config.impl.ConfigIntegerValue -import me.rhunk.snapenhance.config.impl.ConfigStateListValue -import me.rhunk.snapenhance.config.impl.ConfigStateSelection -import me.rhunk.snapenhance.config.impl.ConfigStateValue -import me.rhunk.snapenhance.config.impl.ConfigStringValue -import me.rhunk.snapenhance.manager.Dialogs -import me.rhunk.snapenhance.manager.Section - -typealias ClickCallback = (Boolean) -> Unit -typealias RegisterClickCallback = (ClickCallback) -> ClickCallback - -class FeaturesSection : Section() { - private val dialogs by lazy { Dialogs(manager) } - - @Composable - private fun PropertyAction(item: ConfigProperty, registerClickCallback: RegisterClickCallback) { - val showDialog = remember { mutableStateOf(false) } - val dialogComposable = remember { mutableStateOf<@Composable () -> Unit>({})} - - fun registerDialogOnClickCallback() = registerClickCallback { - showDialog.value = true - } - - if (showDialog.value) { - Dialog(onDismissRequest = { showDialog.value = false }, properties = DialogProperties()) { - dialogComposable.value() - } - } - - when (val container = remember { item.valueContainer }) { - is ConfigStateValue -> { - val state = remember { mutableStateOf(container.value()) } - Switch( - checked = state.value, - onCheckedChange = registerClickCallback { - state.value = state.value.not() - container.writeFrom(state.value.toString()) - } - ) - } - - is ConfigStateSelection -> { - registerDialogOnClickCallback() - dialogComposable.value = { - dialogs.StateSelectionDialog(item) - } - Text( - overflow = TextOverflow.Ellipsis, - maxLines = 1, - modifier = Modifier.widthIn(0.dp, 120.dp), - text = if (item.disableValueLocalization) container.value() else { - manager.translation.propertyOption(item, container.value()) - }, - ) - } - - is ConfigStateListValue, is ConfigStringValue, is ConfigIntegerValue -> { - dialogComposable.value = { - when (container) { - is ConfigStateListValue -> { - dialogs.StateListDialog(item) - } - is ConfigStringValue, is ConfigIntegerValue -> { - dialogs.KeyboardInputDialog(item) { showDialog.value = false } - } - } - } - - registerDialogOnClickCallback().let { { it.invoke(true) } }.also { - if (container is ConfigIntegerValue) { - FilledIconButton(onClick = it) { - Text(text = container.value().toString(), modifier = Modifier.wrapContentWidth(), overflow = TextOverflow.Ellipsis) - } - } else { - IconButton(onClick = it) { - Icon(Icons.Filled.OpenInNew, contentDescription = null) - } - } - } - } - } - } - - @Composable - private fun PropertyCard(item: ConfigProperty) { - val clickCallback = remember { mutableStateOf(null) } - Card( - modifier = Modifier - .fillMaxWidth() - .clickable { - clickCallback.value?.invoke(true) - } - .padding(start = 10.dp, end = 10.dp, top = 5.dp, bottom = 5.dp) - ) { - Row( - modifier = Modifier - .fillMaxSize() - .padding(all = 4.dp), - horizontalArrangement = Arrangement.SpaceBetween - ) { - Column( - modifier = Modifier - .align(Alignment.CenterVertically) - .weight(1f, fill = true) - .padding(all = 10.dp) - ) { - Text(text = manager.translation.propertyName(item), fontSize = 16.sp, fontWeight = FontWeight.Bold) - Text( - text = manager.translation.propertyDescription(item), - fontSize = 12.sp, - lineHeight = 15.sp - ) - } - - Column( - modifier = Modifier - .align(Alignment.CenterVertically) - .padding(all = 10.dp) - ) { - PropertyAction(item, registerClickCallback = { callback -> - clickCallback.value = callback - callback - }) - } - } - } - } - - @Composable - private fun PropertyContainer() { - val properties = remember { - val items by manager.config - items.properties.map { it.key to it.value } - } - - LazyColumn( - modifier = Modifier - .fillMaxHeight(), - verticalArrangement = Arrangement.Center - ) { - items(properties) { (key, value) -> - // Logger.debug("key: $key, value: $value") - } - } - } - - - @Composable - @Preview - override fun Content() { - val scope = rememberCoroutineScope() - val scaffoldState = rememberScaffoldState() - Scaffold( - snackbarHost = { SnackbarHost(scaffoldState.snackbarHostState) }, - floatingActionButton = { - FloatingActionButton( - onClick = { - //manager.config.writeConfig() - scope.launch { - scaffoldState.snackbarHostState.showSnackbar("Saved") - } - }, - containerColor = MaterialTheme.colors.primary, - contentColor = MaterialTheme.colors.onPrimary, - shape = RoundedCornerShape(16.dp), - ) { - Icon( - imageVector = Icons.Rounded.Save, - contentDescription = null - ) - } - }, - modifier = Modifier.fillMaxSize(), - content = { innerPadding -> - Column( - modifier = Modifier - .fillMaxSize() - .padding(innerPadding) - ) { - Text( - text = "Features", - modifier = Modifier.padding(all = 10.dp), - fontSize = 20.sp - ) - PropertyContainer() - } - } - ) - } -} \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/features/CallbackAlias.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/features/CallbackAlias.kt new file mode 100644 index 000000000..9e93b545f --- /dev/null +++ b/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/features/CallbackAlias.kt @@ -0,0 +1,4 @@ +package me.rhunk.snapenhance.manager.sections.features + +typealias ClickCallback = (Boolean) -> Unit +typealias RegisterClickCallback = (ClickCallback) -> ClickCallback \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/Dialogs.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/features/Dialogs.kt similarity index 71% rename from app/src/main/kotlin/me/rhunk/snapenhance/manager/Dialogs.kt rename to app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/features/Dialogs.kt index 586c9b667..5e07232e9 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/manager/Dialogs.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/features/Dialogs.kt @@ -1,4 +1,4 @@ -package me.rhunk.snapenhance.manager +package me.rhunk.snapenhance.manager.sections.features import androidx.compose.foundation.ScrollState import androidx.compose.foundation.clickable @@ -29,16 +29,11 @@ import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.unit.dp -import me.rhunk.snapenhance.config.ConfigProperty -import me.rhunk.snapenhance.config.impl.ConfigIntegerValue -import me.rhunk.snapenhance.config.impl.ConfigStateListValue -import me.rhunk.snapenhance.config.impl.ConfigStateSelection -import me.rhunk.snapenhance.manager.data.ManagerContext +import me.rhunk.snapenhance.core.config.DataProcessors +import me.rhunk.snapenhance.core.config.PropertyPair -class Dialogs( - private val context: ManagerContext -) { +class Dialogs { @Composable fun DefaultDialogCard(content: @Composable ColumnScope.() -> Unit) { Card( @@ -65,17 +60,25 @@ class Dialogs( } @Composable - fun StateSelectionDialog(config: ConfigProperty) { - assert(config.valueContainer is ConfigStateSelection) - val keys = (config.valueContainer as ConfigStateSelection).keys() + @Suppress("UNCHECKED_CAST") + fun UniqueSelectionDialog(property: PropertyPair<*>) { + val keys = (property.value.defaultValues as List).toMutableList().apply { + add(0, "disabled") + } + val selectedValue = remember { - mutableStateOf(config.valueContainer.value()) + mutableStateOf(property.value.getNullable()?.toString() ?: "disabled") } + DefaultDialogCard { - keys.forEach { item -> + keys.forEachIndexed { index, item -> fun select() { selectedValue.value = item - config.valueContainer.writeFrom(item) + property.value.setAny(if (index == 0) { + null + } else { + item + }) } Row( @@ -83,9 +86,7 @@ class Dialogs( verticalAlignment = Alignment.CenterVertically ) { DefaultEntryText( - text = if (config.disableValueLocalization) - item - else context.translation.propertyOption(config, item), + text = item, modifier = Modifier.weight(1f) ) RadioButton( @@ -98,12 +99,12 @@ class Dialogs( } @Composable - fun KeyboardInputDialog(config: ConfigProperty, dismiss: () -> Unit = {}) { + fun KeyboardInputDialog(property: PropertyPair<*>, dismiss: () -> Unit = {}) { val focusRequester = remember { FocusRequester() } DefaultDialogCard { val fieldValue = remember { - mutableStateOf(config.valueContainer.read().let { + mutableStateOf(property.value.get().toString().let { TextFieldValue( text = it, selection = TextRange(it.length) @@ -123,8 +124,8 @@ class Dialogs( onValueChange = { fieldValue.value = it }, - keyboardOptions = when (config.valueContainer) { - is ConfigIntegerValue -> { + keyboardOptions = when (property.key.dataType.type) { + DataProcessors.Type.INTEGER -> { KeyboardOptions(keyboardType = KeyboardType.Number) } else -> { @@ -142,7 +143,15 @@ class Dialogs( Text(text = "Cancel") } Button(onClick = { - config.valueContainer.writeFrom(fieldValue.value.text) + if (property.key.dataType.type == DataProcessors.Type.INTEGER) { + runCatching { + property.value.setAny(fieldValue.value.text.toInt()) + }.onFailure { + property.value.setAny(0) + } + } else { + property.value.setAny(fieldValue.value.text) + } dismiss() }) { Text(text = "Ok") @@ -152,18 +161,23 @@ class Dialogs( } @Composable - fun StateListDialog(config: ConfigProperty) { - assert(config.valueContainer is ConfigStateListValue) - val stateList = (config.valueContainer as ConfigStateListValue).value() + @Suppress("UNCHECKED_CAST") + fun MultipleSelectionDialog(property: PropertyPair<*>) { + val defaultItems = property.value.defaultValues as List + val toggledStates = property.value.get() as MutableList DefaultDialogCard { - stateList.keys.forEach { key -> + defaultItems.forEach { key -> val state = remember { - mutableStateOf(stateList[key] ?: false) + mutableStateOf(toggledStates.contains(key)) } fun toggle(value: Boolean? = null) { state.value = value ?: !state.value - stateList[key] = state.value + if (state.value) { + toggledStates.add(key) + } else { + toggledStates.remove(key) + } } Row( @@ -171,9 +185,7 @@ class Dialogs( verticalAlignment = Alignment.CenterVertically ) { DefaultEntryText( - text = if (config.disableValueLocalization) - key - else context.translation.propertyOption(config, key), + text = key, modifier = Modifier .weight(1f) ) diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/features/FeaturesSection.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/features/FeaturesSection.kt new file mode 100644 index 000000000..756292418 --- /dev/null +++ b/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/features/FeaturesSection.kt @@ -0,0 +1,293 @@ +package me.rhunk.snapenhance.manager.sections.features + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.widthIn +import androidx.compose.foundation.layout.wrapContentWidth +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.MaterialTheme +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material.icons.filled.OpenInNew +import androidx.compose.material.icons.rounded.Save +import androidx.compose.material3.Card +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.FilledIconButton +import androidx.compose.material3.FloatingActionButton +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Scaffold +import androidx.compose.material3.SnackbarHost +import androidx.compose.material3.Switch +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.material3.rememberBottomSheetScaffoldState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.compose.ui.window.Dialog +import androidx.compose.ui.window.DialogProperties +import androidx.navigation.NavGraphBuilder +import androidx.navigation.compose.composable +import androidx.navigation.navigation +import kotlinx.coroutines.launch +import me.rhunk.snapenhance.core.config.ConfigContainer +import me.rhunk.snapenhance.core.config.DataProcessors +import me.rhunk.snapenhance.core.config.PropertyPair +import me.rhunk.snapenhance.manager.Section + +class FeaturesSection : Section() { + private val dialogs by lazy { Dialogs() } + + companion object { + private const val MAIN_ROUTE = "root" + } + + @Composable + private fun PropertyAction(property: PropertyPair<*>, registerClickCallback: RegisterClickCallback) { + val showDialog = remember { mutableStateOf(false) } + val dialogComposable = remember { mutableStateOf<@Composable () -> Unit>({}) } + + fun registerDialogOnClickCallback() = registerClickCallback { + showDialog.value = true + } + + if (showDialog.value) { + Dialog( + onDismissRequest = { showDialog.value = false }, + properties = DialogProperties() + ) { + dialogComposable.value() + } + } + + val propertyValue = property.value + + when (val dataType = remember { property.key.dataType.type }) { + DataProcessors.Type.BOOLEAN -> { + val state = remember { mutableStateOf(propertyValue.get() as Boolean) } + Switch( + checked = state.value, + onCheckedChange = registerClickCallback { + state.value = state.value.not() + propertyValue.setAny(state.value) + } + ) + } + + DataProcessors.Type.STRING_UNIQUE_SELECTION -> { + registerDialogOnClickCallback() + + dialogComposable.value = { + dialogs.UniqueSelectionDialog(property) + } + + Text( + overflow = TextOverflow.Ellipsis, + maxLines = 1, + modifier = Modifier.widthIn(0.dp, 120.dp), + text = (propertyValue.getNullable() as? String) ?: "Disabled", + ) + } + + DataProcessors.Type.STRING_MULTIPLE_SELECTION, DataProcessors.Type.STRING, DataProcessors.Type.INTEGER -> { + dialogComposable.value = { + when (dataType) { + DataProcessors.Type.STRING_MULTIPLE_SELECTION -> { + dialogs.MultipleSelectionDialog(property) + } + DataProcessors.Type.STRING, DataProcessors.Type.INTEGER -> { + dialogs.KeyboardInputDialog(property) { showDialog.value = false } + } + else -> {} + } + } + + registerDialogOnClickCallback().let { { it.invoke(true) } }.also { + if (dataType == DataProcessors.Type.INTEGER) { + FilledIconButton(onClick = it) { + Text( + text = propertyValue.get().toString(), + modifier = Modifier.wrapContentWidth(), + overflow = TextOverflow.Ellipsis + ) + } + } else { + IconButton(onClick = it) { + Icon(Icons.Filled.OpenInNew, contentDescription = null) + } + } + } + } + else -> {} + } + + } + + @Composable + private fun PropertyCard(property: PropertyPair<*>) { + val clickCallback = remember { mutableStateOf(null) } + Card( + modifier = Modifier + .fillMaxWidth() + .padding(start = 10.dp, end = 10.dp, top = 5.dp, bottom = 5.dp) + ) { + Row( + modifier = Modifier + .fillMaxSize() + .clickable { + clickCallback.value?.invoke(true) + } + .padding(all = 4.dp), + horizontalArrangement = Arrangement.SpaceBetween + ) { + Column( + modifier = Modifier + .align(Alignment.CenterVertically) + .weight(1f, fill = true) + .padding(all = 10.dp) + ) { + Text( + text = property.name, + fontSize = 16.sp, + fontWeight = FontWeight.Bold + ) + Text( + text = property.name, + fontSize = 12.sp, + lineHeight = 15.sp + ) + } + + when (property.key.dataType.type) { + DataProcessors.Type.CONTAINER -> { + clickCallback.value = { + navController.navigate("container/${property.name}") + } + } + else -> { + Column( + modifier = Modifier + .align(Alignment.CenterVertically) + .padding(all = 10.dp) + ) { + PropertyAction(property, registerClickCallback = { callback -> + clickCallback.value = callback + callback + }) + } + } + } + } + } + } + + + @OptIn(ExperimentalMaterial3Api::class) + @Composable + private fun Container( + containerName: String, + configContainer: ConfigContainer + ) { + val properties = remember { + configContainer.properties.map { PropertyPair(it.key, it.value) } + } + + val scope = rememberCoroutineScope() + val scaffoldState = rememberBottomSheetScaffoldState() + Scaffold( + snackbarHost = { SnackbarHost(scaffoldState.snackbarHostState) }, + modifier = Modifier.fillMaxSize(), + topBar = { + TopAppBar( + title = { + Text(text = containerName, textAlign = TextAlign.Center) + }, + navigationIcon = { + if (navController.currentBackStackEntry?.destination?.route != MAIN_ROUTE) { + IconButton(onClick = { navController.popBackStack() }) { + Icon(Icons.Filled.ArrowBack, contentDescription = null) + } + } + } + ) + }, + floatingActionButton = { + FloatingActionButton( + onClick = { + manager.config.writeConfig() + scope.launch { + scaffoldState.snackbarHostState.showSnackbar("Saved") + } + }, + modifier = Modifier.padding(25.dp), + containerColor = MaterialTheme.colors.primary, + contentColor = MaterialTheme.colors.onPrimary, + shape = RoundedCornerShape(16.dp), + ) { + Icon( + imageVector = Icons.Rounded.Save, + contentDescription = null + ) + } + }, + content = { innerPadding -> + LazyColumn( + modifier = Modifier.fillMaxHeight().padding(innerPadding), + verticalArrangement = Arrangement.Center + ) { + items(properties) { + PropertyCard(it) + } + } + } + ) + + } + + override fun build(navGraphBuilder: NavGraphBuilder) { + val allContainers by lazy { + val containers = mutableMapOf() + fun queryContainerRecursive(container: ConfigContainer) { + container.properties.forEach { + if (it.key.dataType.type == DataProcessors.Type.CONTAINER) { + containers[it.key.name] = it.value.get() as ConfigContainer + queryContainerRecursive(it.value.get() as ConfigContainer) + } + } + } + queryContainerRecursive(manager.config.root) + containers + } + + navGraphBuilder.navigation(route = "features", startDestination = MAIN_ROUTE) { + composable(MAIN_ROUTE) { + Container(MAIN_ROUTE, manager.config.root) + } + + composable("container/{name}") { backStackEntry -> + backStackEntry.arguments?.getString("name")?.let { containerName -> + allContainers[containerName]?.let { + Container(containerName, it) + } + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/util/SaveFolderChecker.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/util/SaveFolderChecker.kt index 757b903b2..476012954 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/manager/util/SaveFolderChecker.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/manager/util/SaveFolderChecker.kt @@ -6,7 +6,6 @@ import android.content.Intent import android.widget.Toast import androidx.activity.ComponentActivity import androidx.activity.result.contract.ActivityResultContracts -import me.rhunk.snapenhance.Logger import me.rhunk.snapenhance.core.config.PropertyValue import kotlin.system.exitProcess diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ModContext.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ModContext.kt index 9ece2f56c..7fa0f779c 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/ModContext.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/ModContext.kt @@ -14,7 +14,6 @@ import kotlinx.coroutines.asCoroutineDispatcher import me.rhunk.snapenhance.bridge.BridgeClient import me.rhunk.snapenhance.bridge.wrapper.TranslationWrapper import me.rhunk.snapenhance.core.config.ModConfig -import me.rhunk.snapenhance.core.config.impl.RootConfig import me.rhunk.snapenhance.data.MessageSender import me.rhunk.snapenhance.database.DatabaseAccess import me.rhunk.snapenhance.features.Feature diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/SharedContext.kt b/core/src/main/kotlin/me/rhunk/snapenhance/SharedContext.kt index 2b1904ac5..77d3a289d 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/SharedContext.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/SharedContext.kt @@ -7,7 +7,6 @@ import android.content.Intent import android.os.Build import android.os.Environment import android.provider.Settings -import me.rhunk.snapenhance.bridge.wrapper.ConfigWrapper import me.rhunk.snapenhance.bridge.wrapper.TranslationWrapper import me.rhunk.snapenhance.download.DownloadTaskManager import kotlin.system.exitProcess @@ -18,7 +17,6 @@ import kotlin.system.exitProcess object SharedContext { lateinit var downloadTaskManager: DownloadTaskManager lateinit var translation: TranslationWrapper - lateinit var config: ConfigWrapper private fun askForStoragePermission(context: Context) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { @@ -76,10 +74,6 @@ object SharedContext { loadFromContext(context) } } - if (!this::config.isInitialized) { - config = ConfigWrapper().apply { loadFromContext(context) } - } - //askForPermissions(context) } } \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/action/AbstractAction.kt b/core/src/main/kotlin/me/rhunk/snapenhance/action/AbstractAction.kt index 4bc21cc09..6026691c0 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/action/AbstractAction.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/action/AbstractAction.kt @@ -1,12 +1,10 @@ package me.rhunk.snapenhance.action import me.rhunk.snapenhance.ModContext -import me.rhunk.snapenhance.config.ConfigProperty import java.io.File abstract class AbstractAction( - val nameKey: String, - val dependsOnProperty: ConfigProperty? = null, + val nameKey: String ) { lateinit var context: ModContext diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/action/impl/CheckForUpdates.kt b/core/src/main/kotlin/me/rhunk/snapenhance/action/impl/CheckForUpdates.kt index c142d1fa2..3a8e20941 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/action/impl/CheckForUpdates.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/action/impl/CheckForUpdates.kt @@ -1,10 +1,9 @@ package me.rhunk.snapenhance.action.impl import me.rhunk.snapenhance.action.AbstractAction -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.features.impl.AutoUpdater -class CheckForUpdates : AbstractAction("action.check_for_updates", dependsOnProperty = ConfigProperty.AUTO_UPDATER) { +class CheckForUpdates : AbstractAction("action.check_for_updates") { override fun run() { context.executeAsync { runCatching { diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/action/impl/OpenMap.kt b/core/src/main/kotlin/me/rhunk/snapenhance/action/impl/OpenMap.kt index bb5854a03..641802031 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/action/impl/OpenMap.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/action/impl/OpenMap.kt @@ -3,11 +3,10 @@ package me.rhunk.snapenhance.action.impl import android.content.Intent import android.os.Bundle import me.rhunk.snapenhance.action.AbstractAction -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.core.BuildConfig import me.rhunk.snapenhance.ui.map.MapActivity -class OpenMap: AbstractAction("action.open_map", dependsOnProperty = ConfigProperty.LOCATION_SPOOF) { +class OpenMap: AbstractAction("action.open_map") { override fun run() { context.runOnUiThread { val mapActivityIntent = Intent() diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/bridge/BridgeClient.kt b/core/src/main/kotlin/me/rhunk/snapenhance/bridge/BridgeClient.kt index 6c9dd3f09..b3819ce35 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/bridge/BridgeClient.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/bridge/BridgeClient.kt @@ -10,10 +10,10 @@ import android.os.Handler import android.os.HandlerThread import android.os.IBinder import de.robv.android.xposed.XposedHelpers -import me.rhunk.snapenhance.core.BuildConfig import me.rhunk.snapenhance.Logger.xposedLog import me.rhunk.snapenhance.ModContext import me.rhunk.snapenhance.bridge.types.BridgeFileType +import me.rhunk.snapenhance.core.BuildConfig import me.rhunk.snapenhance.data.LocalePair import java.util.concurrent.CompletableFuture import java.util.concurrent.Executors diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/ConfigWrapper.kt b/core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/ConfigWrapper.kt deleted file mode 100644 index 47c1bc212..000000000 --- a/core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/ConfigWrapper.kt +++ /dev/null @@ -1,73 +0,0 @@ -package me.rhunk.snapenhance.bridge.wrapper - -import android.content.Context -import com.google.gson.GsonBuilder -import com.google.gson.JsonObject -import me.rhunk.snapenhance.Logger -import me.rhunk.snapenhance.bridge.BridgeClient -import me.rhunk.snapenhance.bridge.FileLoaderWrapper -import me.rhunk.snapenhance.bridge.types.BridgeFileType -import me.rhunk.snapenhance.config.ConfigAccessor -import me.rhunk.snapenhance.config.ConfigProperty - -class ConfigWrapper: ConfigAccessor() { - companion object { - private val gson = GsonBuilder().setPrettyPrinting().create() - } - - private val file = FileLoaderWrapper(BridgeFileType.CONFIG, "{}".toByteArray(Charsets.UTF_8)) - - fun load() { - ConfigProperty.sortedByCategory().forEach { key -> - set(key, key.valueContainer) - } - - if (!file.isFileExists()) { - writeConfig() - return - } - - runCatching { - loadConfig() - }.onFailure { - Logger.error("Failed to load config", it) - writeConfig() - } - } - - fun save() { - writeConfig() - } - - private fun loadConfig() { - val configContent = file.read() - - val configObject: JsonObject = gson.fromJson( - configContent.toString(Charsets.UTF_8), - JsonObject::class.java - ) - - entries().forEach { (key, value) -> - value.writeFrom(configObject.get(key.name)?.asString ?: value.read()) - } - } - - fun writeConfig() { - val configObject = JsonObject() - entries().forEach { (key, value) -> - configObject.addProperty(key.name, value.read()) - } - - file.write(gson.toJson(configObject).toByteArray(Charsets.UTF_8)) - } - - fun loadFromContext(context: Context) { - file.loadFromContext(context) - load() - } - - fun loadFromBridge(bridgeClient: BridgeClient) { - file.loadFromBridge(bridgeClient) - load() - } -} \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/TranslationWrapper.kt b/core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/TranslationWrapper.kt index 035d47f89..9bd2cc671 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/TranslationWrapper.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/TranslationWrapper.kt @@ -5,7 +5,6 @@ import com.google.gson.JsonObject import com.google.gson.JsonParser import me.rhunk.snapenhance.Logger import me.rhunk.snapenhance.bridge.BridgeClient -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.data.LocalePair import java.util.Locale @@ -80,18 +79,6 @@ class TranslationWrapper { return translationMap[key] ?: key.also { Logger.debug("Missing translation for $key") } } - fun propertyName(property: ConfigProperty): String { - return get("property.${property.translationKey}.name") - } - - fun propertyDescription(property: ConfigProperty): String { - return get("property.${property.translationKey}.description") - } - - fun propertyOption(property: ConfigProperty, item: String): String { - return get(property.getOptionTranslationKey(item)) - } - fun format(key: String, vararg args: Pair): String { return args.fold(get(key)) { acc, pair -> acc.replace("{${pair.first}}", pair.second) diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/config/ConfigAccessor.kt b/core/src/main/kotlin/me/rhunk/snapenhance/config/ConfigAccessor.kt deleted file mode 100644 index 2361bab59..000000000 --- a/core/src/main/kotlin/me/rhunk/snapenhance/config/ConfigAccessor.kt +++ /dev/null @@ -1,62 +0,0 @@ -package me.rhunk.snapenhance.config - -open class ConfigAccessor( - private val configMap: MutableMap> = mutableMapOf() -) { - fun bool(key: ConfigProperty): Boolean { - return get(key).value() as Boolean - } - - fun int(key: ConfigProperty): Int { - return get(key).value() as Int - } - - fun string(key: ConfigProperty): String { - return get(key).value() as String - } - - fun double(key: ConfigProperty): Double { - return get(key).value() as Double - } - - fun float(key: ConfigProperty): Float { - return get(key).value() as Float - } - - fun long(key: ConfigProperty): Long { - return get(key).value() as Long - } - - fun short(key: ConfigProperty): Short { - return get(key).value() as Short - } - - fun byte(key: ConfigProperty): Byte { - return get(key).value() as Byte - } - - fun char(key: ConfigProperty): Char { - return get(key).value() as Char - } - - @Suppress("UNCHECKED_CAST") - fun options(key: ConfigProperty): Map { - return get(key).value() as Map - } - - fun state(key: ConfigProperty): String { - return get(key).value() as String - } - - fun get(key: ConfigProperty): ConfigValue<*> { - return configMap[key]!! - } - - fun set(key: ConfigProperty, value: ConfigValue<*>) { - configMap[key] = value - } - - fun entries(): Set>> { - return configMap.entries - } -} \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/config/ConfigCategory.kt b/core/src/main/kotlin/me/rhunk/snapenhance/config/ConfigCategory.kt deleted file mode 100644 index 47f3356c2..000000000 --- a/core/src/main/kotlin/me/rhunk/snapenhance/config/ConfigCategory.kt +++ /dev/null @@ -1,14 +0,0 @@ -package me.rhunk.snapenhance.config - -enum class ConfigCategory( - val key: String, - val hidden: Boolean = false -) { - SPYING_PRIVACY("spying_privacy"), - MEDIA_MANAGEMENT("media_manager"), - UI_TWEAKS("ui_tweaks"), - UPDATES("updates"), - CAMERA("camera"), - EXPERIMENTAL_DEBUGGING("experimental_debugging"), - DEVICE_SPOOFER("device_spoofer", hidden = true) -} diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/config/ConfigProperty.kt b/core/src/main/kotlin/me/rhunk/snapenhance/config/ConfigProperty.kt deleted file mode 100644 index 39fa7390e..000000000 --- a/core/src/main/kotlin/me/rhunk/snapenhance/config/ConfigProperty.kt +++ /dev/null @@ -1,417 +0,0 @@ -package me.rhunk.snapenhance.config - -import me.rhunk.snapenhance.config.impl.ConfigIntegerValue -import me.rhunk.snapenhance.config.impl.ConfigStateListValue -import me.rhunk.snapenhance.config.impl.ConfigStateSelection -import me.rhunk.snapenhance.config.impl.ConfigStateValue -import me.rhunk.snapenhance.config.impl.ConfigStringValue -import me.rhunk.snapenhance.data.NotificationType -import me.rhunk.snapenhance.features.impl.tweaks.CameraTweaks - -enum class ConfigProperty( - val translationKey: String, - val category: ConfigCategory, - val valueContainer: ConfigValue<*>, - val valueContainerTranslationKey: String? = null, - val shouldAppearInSettings: Boolean = true, - val disableValueLocalization: Boolean = false -) { - - //SPYING AND PRIVACY - MESSAGE_LOGGER("message_logger", - ConfigCategory.SPYING_PRIVACY, - ConfigStateValue(false) - ), - PREVENT_READ_RECEIPTS( - "prevent_read_receipts", - ConfigCategory.SPYING_PRIVACY, - ConfigStateValue(false) - ), - HIDE_BITMOJI_PRESENCE( - "hide_bitmoji_presence", - ConfigCategory.SPYING_PRIVACY, - ConfigStateValue(false) - ), - BETTER_NOTIFICATIONS( - "better_notifications", - ConfigCategory.SPYING_PRIVACY, - ConfigStateListValue( - listOf("snap", "chat", "reply_button", "download_button"), - mutableMapOf( - "snap" to false, - "chat" to false, - "reply_button" to false, - "download_button" to false - ) - ) - ), - NOTIFICATION_BLACKLIST( - "notification_blacklist", - ConfigCategory.SPYING_PRIVACY, - ConfigStateListValue( - NotificationType.getIncomingValues().map { it.key }, - NotificationType.getIncomingValues().associate { it.key to false }.toMutableMap() - ), - valueContainerTranslationKey = "notifications", - ), - DISABLE_METRICS("disable_metrics", - ConfigCategory.SPYING_PRIVACY, - ConfigStateValue(false) - ), - BLOCK_ADS("block_ads", - ConfigCategory.SPYING_PRIVACY, - ConfigStateValue(false) - ), - UNLIMITED_SNAP_VIEW_TIME("unlimited_snap_view_time", - ConfigCategory.SPYING_PRIVACY, - ConfigStateValue(false) - ), - PREVENT_SENDING_MESSAGES( - "prevent_sending_messages", - ConfigCategory.SPYING_PRIVACY, - ConfigStateListValue( - NotificationType.getOutgoingValues().map { it.key }, - NotificationType.getOutgoingValues().associate { it.key to false }.toMutableMap() - ), - valueContainerTranslationKey = "notifications", - ), - ANONYMOUS_STORY_VIEW( - "anonymous_story_view", - ConfigCategory.SPYING_PRIVACY, - ConfigStateValue(false) - ), - HIDE_TYPING_NOTIFICATION( - "hide_typing_notification", - ConfigCategory.SPYING_PRIVACY, - ConfigStateValue(false) - ), - - //MEDIA MANAGEMENT - SAVE_FOLDER( - "save_folder", - ConfigCategory.MEDIA_MANAGEMENT, - ConfigStringValue("", isFolderPath =true), - ), - AUTO_DOWNLOAD_OPTIONS( - "auto_download_options", - ConfigCategory.MEDIA_MANAGEMENT, - ConfigStateListValue( - listOf("friend_snaps", "friend_stories", "public_stories", "spotlight"), - mutableMapOf( - "friend_snaps" to false, - "friend_stories" to false, - "public_stories" to false, - "spotlight" to false - ) - ) - ), - DOWNLOAD_OPTIONS( - "download_options", - ConfigCategory.MEDIA_MANAGEMENT, - ConfigStateListValue( - listOf( - "allow_duplicate", - "create_user_folder", - "append_hash", - "append_date_time", - "append_type", - "append_username", - "merge_overlay" - ), - mutableMapOf( - "allow_duplicate" to false, - "create_user_folder" to true, - "append_hash" to true, - "append_date_time" to true, - "append_type" to false, - "append_username" to false, - "merge_overlay" to false, - ) - ) - ), - CHAT_DOWNLOAD_CONTEXT_MENU( - "chat_download_context_menu", - ConfigCategory.MEDIA_MANAGEMENT, - ConfigStateValue(false) - ), - GALLERY_MEDIA_SEND_OVERRIDE( - "gallery_media_send_override", - ConfigCategory.MEDIA_MANAGEMENT, - ConfigStateSelection( - listOf("OFF", "NOTE", "SNAP", "LIVE_SNAP"), - "OFF" - ) - ), - AUTO_SAVE_MESSAGES("auto_save_messages", - ConfigCategory.MEDIA_MANAGEMENT, - ConfigStateListValue( - listOf("CHAT", "SNAP", "NOTE", "EXTERNAL_MEDIA", "STICKER") - ) - ), - - FORCE_MEDIA_SOURCE_QUALITY( - "force_media_source_quality", - ConfigCategory.MEDIA_MANAGEMENT, - ConfigStateValue(false) - ), - DOWNLOAD_LOGGING( - "download_logging", - ConfigCategory.MEDIA_MANAGEMENT, - ConfigStateListValue( - listOf("started", "success", "progress", "failure"), - mutableMapOf( - "started" to false, - "success" to true, - "progress" to false, - "failure" to true - ) - ) - ), - - //UI AND TWEAKS - ENABLE_FRIEND_FEED_MENU_BAR( - "enable_friend_feed_menu_bar", - ConfigCategory.UI_TWEAKS, - ConfigStateValue(false) - ), - FRIEND_FEED_MENU_BUTTONS( - "friend_feed_menu_buttons", - ConfigCategory.UI_TWEAKS, - ConfigStateListValue( - listOf("auto_download_blacklist", "anti_auto_save", "stealth_mode", "conversation_info"), - mutableMapOf( - "auto_download_blacklist" to false, - "anti_auto_save" to false, - "stealth_mode" to true, - "conversation_info" to true - ) - ) - ), - FRIEND_FEED_MENU_POSITION("friend_feed_menu_buttons_position", - ConfigCategory.UI_TWEAKS, - ConfigIntegerValue(1) - ), - HIDE_UI_ELEMENTS( - "hide_ui_elements", - ConfigCategory.UI_TWEAKS, - ConfigStateListValue( - listOf("remove_voice_record_button", "remove_stickers_button", "remove_cognac_button", "remove_live_location_share_button", "remove_call_buttons", "remove_camera_borders"), - mutableMapOf( - "remove_voice_record_button" to false, - "remove_stickers_button" to false, - "remove_cognac_button" to false, - "remove_live_location_share_button" to false, - "remove_call_buttons" to false, - "remove_camera_borders" to false - ) - ) - ), - HIDE_STORY_SECTION( - "hide_story_section", - ConfigCategory.UI_TWEAKS, - ConfigStateListValue( - listOf("hide_friend_suggestions", "hide_friends", "hide_following", "hide_for_you"), - mutableMapOf( - "hide_friend_suggestions" to false, - "hide_friends" to false, - "hide_following" to false, - "hide_for_you" to false - ) - ) - ), - STORY_VIEWER_OVERRIDE("story_viewer_override", - ConfigCategory.UI_TWEAKS, - ConfigStateSelection( - listOf("OFF", "DISCOVER_PLAYBACK_SEEKBAR", "VERTICAL_STORY_VIEWER"), - "OFF" - ) - ), - STREAK_EXPIRATION_INFO( - "streak_expiration_info", - ConfigCategory.UI_TWEAKS, - ConfigStateValue(false) - ), - DISABLE_SNAP_SPLITTING( - "disable_snap_splitting", - ConfigCategory.UI_TWEAKS, - ConfigStateValue(false) - ), - DISABLE_VIDEO_LENGTH_RESTRICTION( - "disable_video_length_restriction", - ConfigCategory.UI_TWEAKS, - ConfigStateValue(false) - ), - SNAPCHAT_PLUS("snapchat_plus", - ConfigCategory.UI_TWEAKS, - ConfigStateValue(false) - ), - NEW_MAP_UI("new_map_ui", - ConfigCategory.UI_TWEAKS, - ConfigStateValue(false) - ), - LOCATION_SPOOF( - "location_spoof", - ConfigCategory.UI_TWEAKS, - ConfigStateValue(false) - ), - LATITUDE( - "latitude_value", - ConfigCategory.UI_TWEAKS, - ConfigStringValue("0.0000"), - shouldAppearInSettings = false - ), - LONGITUDE( - "longitude_value", - ConfigCategory.UI_TWEAKS, - ConfigStringValue("0.0000"), - shouldAppearInSettings = false - ), - MESSAGE_PREVIEW_LENGTH( - "message_preview_length", - ConfigCategory.UI_TWEAKS, - ConfigIntegerValue(20) - ), - UNLIMITED_CONVERSATION_PINNING( - "unlimited_conversation_pinning", - ConfigCategory.UI_TWEAKS, - ConfigStateValue(false) - ), - DISABLE_SPOTLIGHT( - "disable_spotlight", - ConfigCategory.UI_TWEAKS, - ConfigStateValue(false) - ), - ENABLE_APP_APPEARANCE( - "enable_app_appearance", - ConfigCategory.UI_TWEAKS, - ConfigStateValue(false) - ), - STARTUP_PAGE_OVERRIDE( - "startup_page_override", - ConfigCategory.UI_TWEAKS, - ConfigStateSelection( - listOf( - "OFF", - "ngs_map_icon_container", - "ngs_chat_icon_container", - "ngs_camera_icon_container", - "ngs_community_icon_container", - "ngs_spotlight_icon_container", - "ngs_search_icon_container" - ), - "OFF" - ) - ), - DISABLE_GOOGLE_PLAY_DIALOGS( - "disable_google_play_dialogs", - ConfigCategory.UI_TWEAKS, - ConfigStateValue(false) - ), - - //CAMERA - CAMERA_DISABLE( - "disable_camera", - ConfigCategory.CAMERA, - ConfigStateValue(false) - ), - IMMERSIVE_CAMERA_PREVIEW( - "immersive_camera_preview", - ConfigCategory.CAMERA, - ConfigStateValue(false) - ), - OVERRIDE_PREVIEW_RESOLUTION( - "preview_resolution", - ConfigCategory.CAMERA, - ConfigStateSelection( - CameraTweaks.resolutions, - "OFF" - ), - disableValueLocalization = true - ), - OVERRIDE_PICTURE_RESOLUTION( - "picture_resolution", - ConfigCategory.CAMERA, - ConfigStateSelection( - CameraTweaks.resolutions, - "OFF" - ), - disableValueLocalization = true - ), - FORCE_HIGHEST_FRAME_RATE( - "force_highest_frame_rate", - ConfigCategory.CAMERA, - ConfigStateValue(false) - ), - FORCE_CAMERA_SOURCE_ENCODING( - "force_camera_source_encoding", - ConfigCategory.CAMERA, - ConfigStateValue(false) - ), - - // UPDATES - AUTO_UPDATER( - "auto_updater", - ConfigCategory.UPDATES, - ConfigStateSelection( - listOf("DISABLED", "EVERY_LAUNCH", "DAILY", "WEEKLY"), - "DAILY" - ) - ), - - // EXPERIMENTAL DEBUGGING - APP_PASSCODE( - "app_passcode", - ConfigCategory.EXPERIMENTAL_DEBUGGING, - ConfigStringValue("", isHidden = true) - ), - APP_LOCK_ON_RESUME( - "app_lock_on_resume", - ConfigCategory.EXPERIMENTAL_DEBUGGING, - ConfigStateValue(false) - ), - INFINITE_STORY_BOOST( - "infinite_story_boost", - ConfigCategory.EXPERIMENTAL_DEBUGGING, - ConfigStateValue(false) - ), - MEO_PASSCODE_BYPASS( - "meo_passcode_bypass", - ConfigCategory.EXPERIMENTAL_DEBUGGING, - ConfigStateValue(false) - ), - AMOLED_DARK_MODE( - "amoled_dark_mode", - ConfigCategory.EXPERIMENTAL_DEBUGGING, - ConfigStateValue(false) - ), - UNLIMITED_MULTI_SNAP( - "unlimited_multi_snap", - ConfigCategory.EXPERIMENTAL_DEBUGGING, - ConfigStateValue(false) - ), - - //DEVICE SPOOFER - DEVICE_SPOOF( - "device_spoof", - ConfigCategory.DEVICE_SPOOFER, - ConfigStateValue(false) - ), - FINGERPRINT( - "device_fingerprint", - ConfigCategory.DEVICE_SPOOFER, - ConfigStringValue("") - ), - ANDROID_ID( - "android_id", - ConfigCategory.DEVICE_SPOOFER, - ConfigStringValue("") - ); - - fun getOptionTranslationKey(key: String) = "option.property.${valueContainerTranslationKey ?: translationKey}.$key" - - companion object { - fun sortedByCategory(): List { - return values().sortedBy { it.category.ordinal } - } - } -} \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/config/ConfigValue.kt b/core/src/main/kotlin/me/rhunk/snapenhance/config/ConfigValue.kt deleted file mode 100644 index e12c8f6ca..000000000 --- a/core/src/main/kotlin/me/rhunk/snapenhance/config/ConfigValue.kt +++ /dev/null @@ -1,22 +0,0 @@ -package me.rhunk.snapenhance.config - -abstract class ConfigValue { - private val propertyChangeListeners = mutableListOf<(T) -> Unit>() - - fun addPropertyChangeListener(listener: (T) -> Unit) = propertyChangeListeners.add(listener) - fun removePropertyChangeListener(listener: (T) -> Unit) = propertyChangeListeners.remove(listener) - - abstract fun value(): T - abstract fun read(): String - protected abstract fun write(value: String) - - protected fun onValueChanged() { - propertyChangeListeners.forEach { it(value()) } - } - - fun writeFrom(value: String) { - val oldValue = read() - write(value) - if (oldValue != value) onValueChanged() - } -} \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigIntegerValue.kt b/core/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigIntegerValue.kt deleted file mode 100644 index e02cac045..000000000 --- a/core/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigIntegerValue.kt +++ /dev/null @@ -1,17 +0,0 @@ -package me.rhunk.snapenhance.config.impl - -import me.rhunk.snapenhance.config.ConfigValue - -class ConfigIntegerValue( - private var value: Int -) : ConfigValue() { - override fun value() = value - - override fun read(): String { - return value.toString() - } - - override fun write(value: String) { - this.value = value.toInt() - } -} \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigStateListValue.kt b/core/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigStateListValue.kt deleted file mode 100644 index 005d9aa63..000000000 --- a/core/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigStateListValue.kt +++ /dev/null @@ -1,32 +0,0 @@ -package me.rhunk.snapenhance.config.impl - -import me.rhunk.snapenhance.config.ConfigValue - -class ConfigStateListValue( - private val keys: List, - private var states: MutableMap = mutableMapOf() -) : ConfigValue>() { - override fun value() = states - - fun setKey(key: String, state: Boolean) { - states[key] = state - onValueChanged() - } - - operator fun get(key: String) = states[key] ?: false - - override fun read(): String { - return keys.joinToString("|") { "$it:${states[it]}" } - } - - override fun write(value: String) { - value.split("|").forEach { - val (key, state) = it.split(":") - states[key] = state.toBoolean() - } - } - - override fun toString(): String { - return states.filter { it.value }.keys.joinToString(", ") { it } - } -} \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigStateSelection.kt b/core/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigStateSelection.kt deleted file mode 100644 index 4f9d3f86d..000000000 --- a/core/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigStateSelection.kt +++ /dev/null @@ -1,22 +0,0 @@ -package me.rhunk.snapenhance.config.impl - -import me.rhunk.snapenhance.config.ConfigValue - -class ConfigStateSelection( - private val keys: List, - private var state: String = "" -) : ConfigValue() { - fun keys(): List { - return keys - } - - override fun value() = state - - override fun read(): String { - return state - } - - override fun write(value: String) { - state = value - } -} \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigStateValue.kt b/core/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigStateValue.kt deleted file mode 100644 index ef8dfaea2..000000000 --- a/core/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigStateValue.kt +++ /dev/null @@ -1,17 +0,0 @@ -package me.rhunk.snapenhance.config.impl - -import me.rhunk.snapenhance.config.ConfigValue - -class ConfigStateValue( - private var value: Boolean -) : ConfigValue() { - override fun value() = value - - override fun read(): String { - return value.toString() - } - - override fun write(value: String) { - this.value = value.toBoolean() - } -} \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigStringValue.kt b/core/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigStringValue.kt deleted file mode 100644 index 5f991adc2..000000000 --- a/core/src/main/kotlin/me/rhunk/snapenhance/config/impl/ConfigStringValue.kt +++ /dev/null @@ -1,21 +0,0 @@ -package me.rhunk.snapenhance.config.impl - -import me.rhunk.snapenhance.config.ConfigValue - -class ConfigStringValue( - private var value: String = "", - val isFolderPath: Boolean = false, - val isHidden: Boolean = false -) : ConfigValue() { - override fun value() = value - - fun hiddenValue() = if (isHidden) value.map { '*' }.joinToString("") else value - - override fun read(): String { - return value - } - - override fun write(value: String) { - this.value = value - } -} \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/config/ConfigContainer.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/ConfigContainer.kt index c806c423f..bba5bbfae 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/core/config/ConfigContainer.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/ConfigContainer.kt @@ -1,7 +1,6 @@ package me.rhunk.snapenhance.core.config import com.google.gson.JsonObject -import me.rhunk.snapenhance.Logger import kotlin.reflect.KProperty typealias ConfigParamsBuilder = ConfigParams.() -> Unit @@ -57,7 +56,7 @@ open class ConfigContainer( fun toJson(): JsonObject { val json = JsonObject() properties.forEach { (propertyKey, propertyValue) -> - val serializedValue = propertyValue.getNullable()?.let { propertyKey.dataProcessor.serializeAny(it) } + val serializedValue = propertyValue.getNullable()?.let { propertyKey.dataType.serializeAny(it) } json.add(propertyKey.name, serializedValue) } return json @@ -66,7 +65,7 @@ open class ConfigContainer( fun fromJson(json: JsonObject) { properties.forEach { (key, _) -> val jsonElement = json.get(key.name) ?: return@forEach - key.dataProcessor.deserializeAny(jsonElement)?.let { + key.dataType.deserializeAny(jsonElement)?.let { properties[key]?.setAny(it) } } diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/config/ConfigObjects.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/ConfigObjects.kt index b98cc2693..dbd4ea6a8 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/core/config/ConfigObjects.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/ConfigObjects.kt @@ -1,12 +1,20 @@ package me.rhunk.snapenhance.core.config -import me.rhunk.snapenhance.Logger import kotlin.reflect.KProperty + +data class PropertyPair( + val key: PropertyKey, + val value: PropertyValue<*> +) { + val name get() = key.name +} + class ConfigParams( var shouldTranslate: Boolean = false, var hidden: Boolean = false, - var isFolder: Boolean = false + var isFolder: Boolean = false, + val disabledKey: String? = null ) class PropertyValue( @@ -34,7 +42,7 @@ class PropertyValue( class PropertyKey( val name: String, - val dataProcessor: DataProcessors.PropertyDataProcessor, + val dataType: DataProcessors.PropertyDataProcessor, val params: ConfigParams = ConfigParams(), ) diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/config/DataProcessors.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/DataProcessors.kt index 755a5c8fe..8c852cb6b 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/core/config/DataProcessors.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/DataProcessors.kt @@ -65,7 +65,7 @@ object DataProcessors { type = Type.STRING_MULTIPLE_SELECTION, serialize = { JsonArray().apply { it.forEach { add(it) } } }, deserialize = { obj -> - obj.asJsonArray.map { it.asString } + obj.asJsonArray.map { it.asString }.toMutableList() }, ) diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/Global.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/Global.kt index 321746ef3..b8688a85b 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/Global.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/Global.kt @@ -5,7 +5,7 @@ import me.rhunk.snapenhance.data.NotificationType class Global : ConfigContainer() { val snapchatPlus = boolean("snapchat_plus") - val autoUpdater = unique("auto_updater", "DAILY","EVERY_LAUNCH", "DAILY", "WEEKLY") + val autoUpdater = unique("auto_updater", "EVERY_LAUNCH", "DAILY", "WEEKLY") val disableMetrics = boolean("disable_metrics") val disableVideoLengthRestrictions = boolean("disable_video_length_restrictions") val disableGooglePlayDialogs = boolean("disable_google_play_dialogs") diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/RootConfig.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/RootConfig.kt index 57cbb8759..86f277e2b 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/RootConfig.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/RootConfig.kt @@ -1,8 +1,6 @@ package me.rhunk.snapenhance.core.config.impl import me.rhunk.snapenhance.core.config.ConfigContainer -import me.rhunk.snapenhance.data.NotificationType -import me.rhunk.snapenhance.features.impl.tweaks.CameraTweaks class RootConfig : ConfigContainer() { val downloader = container("downloader", DownloaderConfig()) diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/download/DownloadProcessor.kt b/core/src/main/kotlin/me/rhunk/snapenhance/download/DownloadProcessor.kt index d0c52823d..9fc7038f0 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/download/DownloadProcessor.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/download/DownloadProcessor.kt @@ -18,8 +18,6 @@ import me.rhunk.snapenhance.Constants import me.rhunk.snapenhance.Logger import me.rhunk.snapenhance.SharedContext import me.rhunk.snapenhance.bridge.DownloadCallback -import me.rhunk.snapenhance.bridge.wrapper.ConfigWrapper -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.core.config.ModConfig import me.rhunk.snapenhance.data.FileType import me.rhunk.snapenhance.download.data.DownloadMetadata diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/ConfigEnumKeys.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/ConfigEnumKeys.kt index f7069be01..2cc81ee75 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/ConfigEnumKeys.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/ConfigEnumKeys.kt @@ -1,7 +1,6 @@ package me.rhunk.snapenhance.features.impl import android.annotation.SuppressLint -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams import me.rhunk.snapenhance.hook.HookStage @@ -95,10 +94,6 @@ class ConfigEnumKeys : Feature("Config enum keys", loadParams = FeatureLoadParam } } - ConfigProperty.ENABLE_APP_APPEARANCE.valueContainer.addPropertyChangeListener { - context.softRestartApp(true) - } - val sharedPreferencesImpl = context.androidContext.classLoader.loadClass("android.app.SharedPreferencesImpl") sharedPreferencesImpl.methods.first { it.name == "getBoolean" }.hook(HookStage.BEFORE) { param -> diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/Messaging.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/Messaging.kt index a0e767e9b..99096ebef 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/Messaging.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/Messaging.kt @@ -1,14 +1,13 @@ package me.rhunk.snapenhance.features.impl -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.data.wrapper.impl.SnapUUID import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams +import me.rhunk.snapenhance.features.impl.spying.StealthMode import me.rhunk.snapenhance.hook.HookStage import me.rhunk.snapenhance.hook.Hooker import me.rhunk.snapenhance.hook.hook import me.rhunk.snapenhance.util.getObjectField -import me.rhunk.snapenhance.features.impl.spying.StealthMode; class Messaging : Feature("Messaging", loadParams = FeatureLoadParams.ACTIVITY_CREATE_SYNC or FeatureLoadParams.INIT_ASYNC or FeatureLoadParams.INIT_SYNC) { lateinit var conversationManager: Any diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/downloader/MediaDownloader.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/downloader/MediaDownloader.kt index 4be342aec..9249fe396 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/downloader/MediaDownloader.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/downloader/MediaDownloader.kt @@ -11,7 +11,6 @@ import me.rhunk.snapenhance.Constants.ARROYO_URL_KEY_PROTO_PATH import me.rhunk.snapenhance.Logger import me.rhunk.snapenhance.Logger.xposedLog import me.rhunk.snapenhance.bridge.DownloadCallback -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.data.ContentType import me.rhunk.snapenhance.data.FileType import me.rhunk.snapenhance.data.wrapper.impl.media.MediaInfo diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/AmoledDarkMode.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/AmoledDarkMode.kt index 8d5f1b355..e86c57969 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/AmoledDarkMode.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/AmoledDarkMode.kt @@ -4,7 +4,6 @@ import android.annotation.SuppressLint import android.content.res.TypedArray import android.graphics.drawable.ColorDrawable import me.rhunk.snapenhance.Constants -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams import me.rhunk.snapenhance.hook.HookStage diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/AppPasscode.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/AppPasscode.kt index 2d20708cf..4c752a9cc 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/AppPasscode.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/AppPasscode.kt @@ -1,7 +1,6 @@ package me.rhunk.snapenhance.features.impl.experiments import android.annotation.SuppressLint -import android.app.AlertDialog import android.content.Context import android.os.Build import android.text.Editable @@ -9,7 +8,6 @@ import android.text.InputType import android.text.TextWatcher import android.view.inputmethod.InputMethodManager import android.widget.EditText -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams import me.rhunk.snapenhance.ui.ViewAppearanceHelper diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/DeviceSpooferHook.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/DeviceSpooferHook.kt index 5f9a4839a..1b2cbc387 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/DeviceSpooferHook.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/DeviceSpooferHook.kt @@ -1,7 +1,6 @@ package me.rhunk.snapenhance.features.impl.experiments import me.rhunk.snapenhance.Logger -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams import me.rhunk.snapenhance.hook.HookStage diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/InfiniteStoryBoost.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/InfiniteStoryBoost.kt index c24fcd1ab..4d0de897a 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/InfiniteStoryBoost.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/InfiniteStoryBoost.kt @@ -1,6 +1,5 @@ package me.rhunk.snapenhance.features.impl.experiments -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams import me.rhunk.snapenhance.hook.HookStage diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/MeoPasscodeBypass.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/MeoPasscodeBypass.kt index 2e0031b2f..1a02e1d30 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/MeoPasscodeBypass.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/MeoPasscodeBypass.kt @@ -1,6 +1,5 @@ package me.rhunk.snapenhance.features.impl.experiments -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams import me.rhunk.snapenhance.hook.HookStage diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/UnlimitedMultiSnap.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/UnlimitedMultiSnap.kt index a5d749bc0..c2514824d 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/UnlimitedMultiSnap.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/experiments/UnlimitedMultiSnap.kt @@ -1,6 +1,5 @@ package me.rhunk.snapenhance.features.impl.experiments -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams import me.rhunk.snapenhance.hook.HookStage diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/privacy/DisableMetrics.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/privacy/DisableMetrics.kt index e4bef80de..257e69d55 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/privacy/DisableMetrics.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/privacy/DisableMetrics.kt @@ -1,10 +1,8 @@ package me.rhunk.snapenhance.features.impl.privacy import de.robv.android.xposed.XposedHelpers -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams -import me.rhunk.snapenhance.hook.HookAdapter import me.rhunk.snapenhance.hook.HookStage import me.rhunk.snapenhance.hook.Hooker diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/privacy/PreventMessageSending.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/privacy/PreventMessageSending.kt index 22008590b..ada46e858 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/privacy/PreventMessageSending.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/privacy/PreventMessageSending.kt @@ -1,7 +1,6 @@ package me.rhunk.snapenhance.features.impl.privacy import me.rhunk.snapenhance.Logger -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.data.NotificationType import me.rhunk.snapenhance.data.wrapper.impl.MessageContent import me.rhunk.snapenhance.features.Feature diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/AnonymousStoryViewing.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/AnonymousStoryViewing.kt index f9e9adc5c..4d8b1441e 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/AnonymousStoryViewing.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/AnonymousStoryViewing.kt @@ -1,6 +1,5 @@ package me.rhunk.snapenhance.features.impl.spying -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams import me.rhunk.snapenhance.hook.HookStage diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/MessageLogger.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/MessageLogger.kt index 8f50a24c6..c4228c0d1 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/MessageLogger.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/MessageLogger.kt @@ -4,7 +4,6 @@ import android.os.DeadObjectException import com.google.gson.JsonObject import com.google.gson.JsonParser import me.rhunk.snapenhance.Logger -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.data.ContentType import me.rhunk.snapenhance.data.MessageState import me.rhunk.snapenhance.data.wrapper.impl.Message diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/PreventReadReceipts.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/PreventReadReceipts.kt index 212c53e3f..aa5305297 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/PreventReadReceipts.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/PreventReadReceipts.kt @@ -1,6 +1,5 @@ package me.rhunk.snapenhance.features.impl.spying -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.data.wrapper.impl.SnapUUID import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/AutoSave.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/AutoSave.kt index 047f97790..22e646c0b 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/AutoSave.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/AutoSave.kt @@ -1,7 +1,6 @@ package me.rhunk.snapenhance.features.impl.tweaks import me.rhunk.snapenhance.Logger -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.data.MessageState import me.rhunk.snapenhance.data.wrapper.impl.Message import me.rhunk.snapenhance.data.wrapper.impl.SnapUUID diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/CameraTweaks.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/CameraTweaks.kt index 2efb85f76..09287129d 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/CameraTweaks.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/CameraTweaks.kt @@ -5,7 +5,6 @@ import android.annotation.SuppressLint import android.content.ContextWrapper import android.content.pm.PackageManager import android.hardware.camera2.CameraManager -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.data.wrapper.impl.ScSize import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/DisableVideoLengthRestriction.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/DisableVideoLengthRestriction.kt index 568fc32fe..f47e641f3 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/DisableVideoLengthRestriction.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/DisableVideoLengthRestriction.kt @@ -1,6 +1,5 @@ package me.rhunk.snapenhance.features.impl.tweaks -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams import me.rhunk.snapenhance.hook.HookStage diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/GalleryMediaSendOverride.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/GalleryMediaSendOverride.kt index 50bf2d26c..bdfc2f76d 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/GalleryMediaSendOverride.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/GalleryMediaSendOverride.kt @@ -1,7 +1,5 @@ package me.rhunk.snapenhance.features.impl.tweaks -import android.app.AlertDialog -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.data.ContentType import me.rhunk.snapenhance.data.MessageSender import me.rhunk.snapenhance.data.wrapper.impl.MessageContent diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/GooglePlayServicesDialogs.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/GooglePlayServicesDialogs.kt index d564e022e..4a5ec5619 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/GooglePlayServicesDialogs.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/GooglePlayServicesDialogs.kt @@ -2,7 +2,6 @@ package me.rhunk.snapenhance.features.impl.tweaks import android.app.AlertDialog import me.rhunk.snapenhance.Logger -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams import me.rhunk.snapenhance.hook.HookStage diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/LocationSpoofer.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/LocationSpoofer.kt index 7e082f6aa..8ed9d07eb 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/LocationSpoofer.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/LocationSpoofer.kt @@ -1,7 +1,6 @@ package me.rhunk.snapenhance.features.impl.tweaks import android.content.Intent -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams import me.rhunk.snapenhance.hook.HookStage diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/MediaQualityLevelOverride.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/MediaQualityLevelOverride.kt index 2907594a1..d76c7f43e 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/MediaQualityLevelOverride.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/MediaQualityLevelOverride.kt @@ -1,6 +1,5 @@ package me.rhunk.snapenhance.features.impl.tweaks -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams import me.rhunk.snapenhance.hook.HookStage diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/Notifications.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/Notifications.kt index c91664d26..1f117b310 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/Notifications.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/Notifications.kt @@ -14,7 +14,6 @@ import de.robv.android.xposed.XposedBridge import de.robv.android.xposed.XposedHelpers import me.rhunk.snapenhance.Constants import me.rhunk.snapenhance.Logger -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.data.ContentType import me.rhunk.snapenhance.data.MediaReferenceType import me.rhunk.snapenhance.data.wrapper.impl.Message diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/SnapchatPlus.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/SnapchatPlus.kt index 09cba1483..1efc14932 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/SnapchatPlus.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/SnapchatPlus.kt @@ -1,6 +1,5 @@ package me.rhunk.snapenhance.features.impl.tweaks -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams import me.rhunk.snapenhance.hook.HookStage diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/UnlimitedSnapViewTime.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/UnlimitedSnapViewTime.kt index 99dba59e8..19a8f5a82 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/UnlimitedSnapViewTime.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/UnlimitedSnapViewTime.kt @@ -1,6 +1,5 @@ package me.rhunk.snapenhance.features.impl.tweaks -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.data.ContentType import me.rhunk.snapenhance.data.MessageState import me.rhunk.snapenhance.data.wrapper.impl.Message diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/StartupPageOverride.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/StartupPageOverride.kt index 358c75a2e..707d9643b 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/StartupPageOverride.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/StartupPageOverride.kt @@ -5,7 +5,6 @@ import android.view.View import android.view.ViewGroup import android.widget.LinearLayout import me.rhunk.snapenhance.Constants -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams import me.rhunk.snapenhance.hook.HookStage diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/UITweaks.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/UITweaks.kt index 2c0d09c43..b88ed6b5b 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/UITweaks.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/UITweaks.kt @@ -8,7 +8,6 @@ import android.view.View import android.view.ViewGroup import android.widget.FrameLayout import me.rhunk.snapenhance.Constants -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams import me.rhunk.snapenhance.hook.HookAdapter diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/manager/impl/ActionManager.kt b/core/src/main/kotlin/me/rhunk/snapenhance/manager/impl/ActionManager.kt index 4dfc34158..92d91f8df 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/manager/impl/ActionManager.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/manager/impl/ActionManager.kt @@ -1,6 +1,5 @@ package me.rhunk.snapenhance.manager.impl -import me.rhunk.snapenhance.core.BuildConfig import me.rhunk.snapenhance.ModContext import me.rhunk.snapenhance.action.AbstractAction import me.rhunk.snapenhance.action.impl.CheckForUpdates @@ -9,6 +8,7 @@ import me.rhunk.snapenhance.action.impl.ClearMessageLogger import me.rhunk.snapenhance.action.impl.ExportChatMessages import me.rhunk.snapenhance.action.impl.OpenMap import me.rhunk.snapenhance.action.impl.RefreshMappings +import me.rhunk.snapenhance.core.BuildConfig import me.rhunk.snapenhance.manager.Manager import kotlin.reflect.KClass diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/manager/impl/ConfigManager.kt b/core/src/main/kotlin/me/rhunk/snapenhance/manager/impl/ConfigManager.kt deleted file mode 100644 index 7e14570a0..000000000 --- a/core/src/main/kotlin/me/rhunk/snapenhance/manager/impl/ConfigManager.kt +++ /dev/null @@ -1,58 +0,0 @@ -package me.rhunk.snapenhance.manager.impl - -import com.google.gson.JsonObject -import me.rhunk.snapenhance.Logger -import me.rhunk.snapenhance.ModContext -import me.rhunk.snapenhance.bridge.types.BridgeFileType -import me.rhunk.snapenhance.config.ConfigAccessor -import me.rhunk.snapenhance.config.ConfigProperty -import me.rhunk.snapenhance.manager.Manager -import java.nio.charset.StandardCharsets - -class ConfigManager( - private val context: ModContext -) : ConfigAccessor(), Manager { - - override fun init() { - ConfigProperty.sortedByCategory().forEach { key -> - set(key, key.valueContainer) - } - - if (!context.bridgeClient.isFileExists(BridgeFileType.CONFIG)) { - writeConfig() - return - } - - runCatching { - loadConfig() - }.onFailure { - Logger.xposedLog("Failed to load config", it) - writeConfig() - } - } - - private fun loadConfig() { - val configContent = context.bridgeClient.createAndReadFile( - BridgeFileType.CONFIG, - "{}".toByteArray(Charsets.UTF_8) - ) - val configObject: JsonObject = context.gson.fromJson( - String(configContent, StandardCharsets.UTF_8), - JsonObject::class.java - ) - entries().forEach { (key, value) -> - value.writeFrom(configObject.get(key.name)?.asString ?: value.read()) - } - } - - fun writeConfig() { - val configObject = JsonObject() - entries().forEach { (key, value) -> - configObject.addProperty(key.name, value.read()) - } - context.bridgeClient.writeFile( - BridgeFileType.CONFIG, - context.gson.toJson(configObject).toByteArray(Charsets.UTF_8) - ) - } -} \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/manager/impl/MappingManager.kt b/core/src/main/kotlin/me/rhunk/snapenhance/manager/impl/MappingManager.kt index 86d8ad65c..87a6ca8f2 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/manager/impl/MappingManager.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/manager/impl/MappingManager.kt @@ -1,6 +1,5 @@ package me.rhunk.snapenhance.manager.impl -import android.app.AlertDialog import com.google.gson.JsonElement import com.google.gson.JsonParser import me.rhunk.snapenhance.Constants diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ui/ItemHelper.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/ItemHelper.kt deleted file mode 100644 index 2d658aa6c..000000000 --- a/core/src/main/kotlin/me/rhunk/snapenhance/ui/ItemHelper.kt +++ /dev/null @@ -1,87 +0,0 @@ -package me.rhunk.snapenhance.ui - -import android.app.Activity -import android.app.AlertDialog -import android.content.Context -import android.content.Intent -import android.widget.EditText -import android.widget.TextView -import android.widget.Toast -import me.rhunk.snapenhance.core.R -import me.rhunk.snapenhance.SharedContext -import me.rhunk.snapenhance.config.ConfigProperty -import me.rhunk.snapenhance.util.ActivityResultCallback -import kotlin.math.abs -import kotlin.random.Random - -class ItemHelper( - private val context : Context -) { - val positiveButtonText by lazy { - SharedContext.translation["button.ok"] - } - - val cancelButtonText by lazy { - SharedContext.translation["button.cancel"] - } - - fun longToast(message: String, context: Context) { - Toast.makeText(context, message, Toast.LENGTH_LONG).show() - } - - fun createTranslatedTextView(property: ConfigProperty, shouldTranslatePropertyValue: Boolean = true): TextView { - return object: TextView(context) { - override fun setText(text: CharSequence?, type: BufferType?) { - val newText = text?.takeIf { it.isNotEmpty() }?.let { - if (!shouldTranslatePropertyValue || property.disableValueLocalization) it - else SharedContext.translation[property.getOptionTranslationKey(it.toString())] - }?.let { - if (it.length > 20) { - "...${it.substring(it.length - 20)}" - } else { - it - } - } ?: "" - super.setTextColor(context.getColor(R.color.tertiaryText)) - super.setText(newText, type) - } - } - } - - fun askForValue(property: ConfigProperty, requestedInputType: Int, callback: (String) -> Unit) { - val editText = EditText(context).apply { - inputType = requestedInputType - setText(property.valueContainer.value().toString()) - } - AlertDialog.Builder(context) - .setTitle(SharedContext.translation["property.${property.translationKey}.name"]) - .setView(editText) - .setPositiveButton(positiveButtonText) { _, _ -> - callback(editText.text.toString()) - } - .setNegativeButton(cancelButtonText) { dialog, _ -> - dialog.cancel() - } - .show() - } - - fun askForFolder(activity: Activity, property: ConfigProperty, callback: (String) -> Unit): Pair { - val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) - .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - .addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION) - - val requestCode = abs(Random.nextInt()) - activity.startActivityForResult(intent, requestCode) - - return requestCode to let@{_, resultCode, data -> - if (resultCode != Activity.RESULT_OK) return@let - val uri = data?.data ?: return@let - val value = uri.toString() - activity.contentResolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION) - property.valueContainer.writeFrom(value) - callback(value) - } - } - - -} diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ui/config/ConfigActivity.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/config/ConfigActivity.kt deleted file mode 100644 index 7a47c87a3..000000000 --- a/core/src/main/kotlin/me/rhunk/snapenhance/ui/config/ConfigActivity.kt +++ /dev/null @@ -1,279 +0,0 @@ -package me.rhunk.snapenhance.ui.config - -import android.app.Activity -import android.app.AlertDialog -import android.content.Intent -import android.content.res.ColorStateList -import android.os.Bundle -import android.text.Html -import android.text.InputType -import android.view.View -import android.view.ViewGroup -import android.widget.ImageButton -import android.widget.Switch -import android.widget.TextView -import android.widget.Toast -import me.rhunk.snapenhance.core.BuildConfig -import me.rhunk.snapenhance.core.R -import me.rhunk.snapenhance.SharedContext -import me.rhunk.snapenhance.config.ConfigCategory -import me.rhunk.snapenhance.config.ConfigProperty -import me.rhunk.snapenhance.config.impl.ConfigIntegerValue -import me.rhunk.snapenhance.config.impl.ConfigStateListValue -import me.rhunk.snapenhance.config.impl.ConfigStateSelection -import me.rhunk.snapenhance.config.impl.ConfigStateValue -import me.rhunk.snapenhance.config.impl.ConfigStringValue -import me.rhunk.snapenhance.ui.ItemHelper -import me.rhunk.snapenhance.util.ActivityResultCallback -import kotlin.system.exitProcess - - -class ConfigActivity : Activity() { - private val itemHelper = ItemHelper(this) - private val activityResultCallbacks = mutableMapOf() - - @Deprecated("Deprecated in Java") - @Suppress("DEPRECATION") - override fun onBackPressed() { - super.onBackPressed() - finish() - } - - override fun onDestroy() { - super.onDestroy() - SharedContext.config.writeConfig() - } - - override fun onPause() { - super.onPause() - SharedContext.config.writeConfig() - } - - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - activityResultCallbacks[requestCode]?.invoke(requestCode, resultCode, data) - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - SharedContext.ensureInitialized(this) - setContentView(R.layout.config_activity) - - findViewById(R.id.title_bar).let { titleBar -> - titleBar.findViewById(R.id.title).text = SharedContext.translation["config_activity.title"] - titleBar.findViewById(R.id.back_button).visibility = View.GONE - } - - val propertyListLayout = findViewById(R.id.property_list) - - if (intent.getBooleanExtra("lspatched", false) || - applicationInfo.packageName != "me.rhunk.snapenhance" || - BuildConfig.DEBUG) { - propertyListLayout.addView( - layoutInflater.inflate( - R.layout.config_activity_debug_item, - propertyListLayout, - false - ).apply { - findViewById(R.id.debug_item_content).apply { - text = Html.fromHtml( - "You are using a debug/unofficial build!\n" + - "Please consider downloading stable builds from GitHub.", - Html.FROM_HTML_MODE_COMPACT - ) - movementMethod = android.text.method.LinkMovementMethod.getInstance() - } - }) - } - - //check if save folder is set - //TODO: first run activity - run { - val saveFolder = SharedContext.config.string(ConfigProperty.SAVE_FOLDER) - val itemHelper = ItemHelper(this) - - if (saveFolder.isEmpty() || !saveFolder.startsWith("content://")) { - AlertDialog.Builder(this) - .setTitle("Save folder") - .setMessage("Please select a folder where you want to save downloaded files.") - .setPositiveButton("Select") { _, _ -> - val (requestCode, callback) = itemHelper.askForFolder( - this, - ConfigProperty.SAVE_FOLDER - ) {} - activityResultCallbacks[requestCode] = { a1, a2, a3 -> - callback(a1, a2, a3) - Toast.makeText(this, "Save Folder set!", Toast.LENGTH_SHORT).show() - finish() - } - } - .setNegativeButton("Cancel") { _, _ -> - exitProcess(0) - } - .show() - } - } - - var currentCategory: ConfigCategory? = null - - SharedContext.config.entries().filter { !it.key.category.hidden }.forEach { (property, value) -> - val configItem = layoutInflater.inflate(R.layout.config_activity_item, propertyListLayout, false) - - fun addSeparator() { - //add separator - propertyListLayout.addView(View(this).apply { - layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 1) - setBackgroundColor(getColor(R.color.tertiaryBackground)) - }) - } - - if (property.category != currentCategory) { - if(!property.shouldAppearInSettings) return@forEach - currentCategory = property.category - with(layoutInflater.inflate(R.layout.config_activity_item, propertyListLayout, false)) { - findViewById(R.id.name).apply { - text = SharedContext.translation["category.${property.category.key}"] - textSize = 20f - typeface = typeface?.let { android.graphics.Typeface.create(it, android.graphics.Typeface.BOLD) } - } - propertyListLayout.addView(this) - } - addSeparator() - } - - if (!property.shouldAppearInSettings) return@forEach - - val propertyName = SharedContext.translation["property.${property.translationKey}.name"] - - configItem.findViewById(R.id.name).text = propertyName - configItem.findViewById(R.id.description).also { - it.text = SharedContext.translation["property.${property.translationKey}.description"] - it.visibility = if (it.text.isEmpty()) View.GONE else View.VISIBLE - } - - fun addValueView(view: View) { - configItem.findViewById(R.id.value).addView(view) - } - - when (value) { - is ConfigStateValue -> { - val switch = Switch(this) - switch.isChecked = value.value() - switch.trackTintList = ColorStateList( - arrayOf( - intArrayOf(android.R.attr.state_checked), - intArrayOf(-android.R.attr.state_checked) - ), - intArrayOf( - switch.highlightColor, - getColor(R.color.tertiaryBackground) - ) - ) - switch.setOnCheckedChangeListener { _, isChecked -> - value.writeFrom(isChecked.toString()) - } - configItem.setOnClickListener { switch.toggle() } - addValueView(switch) - } - is ConfigStringValue, is ConfigIntegerValue -> { - val textView = itemHelper.createTranslatedTextView(property, shouldTranslatePropertyValue = false).also { - it.text = value.value().toString() - } - configItem.setOnClickListener { - if (value is ConfigStringValue && value.isFolderPath) { - val (requestCode, callback) = itemHelper.askForFolder(this, property) { - value.writeFrom(it) - textView.text = value.value() - } - - activityResultCallbacks[requestCode] = callback - return@setOnClickListener - } - - if (value is ConfigIntegerValue) { - itemHelper.askForValue(property, InputType.TYPE_CLASS_NUMBER) { - try { - value.writeFrom(it) - textView.text = value.value().toString() - } catch (e: NumberFormatException) { - itemHelper.longToast(SharedContext.translation["config_activity.invalid_number_toast"], this) - } - } - return@setOnClickListener - } - itemHelper.askForValue(property, InputType.TYPE_CLASS_TEXT) { - value.writeFrom(it) - textView.text = value.value().toString() - } - } - addValueView(textView) - } - is ConfigStateListValue -> { - val textView = itemHelper.createTranslatedTextView(property, shouldTranslatePropertyValue = false) - val values = value.value() - - fun updateText() { - textView.text = SharedContext.translation.format("config_activity.selected_text", "count" to values.filter { it.value }.size.toString()) - } - - updateText() - - configItem.setOnClickListener { - AlertDialog.Builder(this) - .setTitle(propertyName) - .setPositiveButton(itemHelper.positiveButtonText) { _, _ -> - updateText() - } - .setMultiChoiceItems( - values.keys.map { - if (property.disableValueLocalization) it - else SharedContext.translation[property.getOptionTranslationKey(it)] - }.toTypedArray(), - values.map { it.value }.toBooleanArray() - ) { _, which, isChecked -> - value.setKey(values.keys.elementAt(which), isChecked) - } - .show() - } - - addValueView(textView) - } - is ConfigStateSelection -> { - val textView = itemHelper.createTranslatedTextView(property, shouldTranslatePropertyValue = true) - textView.text = value.value() - - configItem.setOnClickListener { - val builder = AlertDialog.Builder(this) - builder.setTitle(propertyName) - - builder.setSingleChoiceItems( - value.keys().toTypedArray().map { - if (property.disableValueLocalization) it - else SharedContext.translation[property.getOptionTranslationKey(it)] - }.toTypedArray(), - value.keys().indexOf(value.value()) - ) { _, which -> - value.writeFrom(value.keys()[which]) - } - - builder.setPositiveButton(itemHelper.positiveButtonText) { _, _ -> - textView.text = value.value() - } - - builder.show() - } - addValueView(textView) - } - } - - propertyListLayout.addView(configItem) - addSeparator() - } - - propertyListLayout.addView(layoutInflater.inflate(R.layout.config_activity_debug_item, propertyListLayout, false).apply { - findViewById(R.id.debug_item_content).apply { - text = Html.fromHtml("Made by rhunk on GitHub", Html.FROM_HTML_MODE_COMPACT) - movementMethod = android.text.method.LinkMovementMethod.getInstance() - } - }) - } -} \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DebugSettingsLayoutInflater.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DebugSettingsLayoutInflater.kt index 888eb96d9..ce720583e 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DebugSettingsLayoutInflater.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DebugSettingsLayoutInflater.kt @@ -1,7 +1,6 @@ package me.rhunk.snapenhance.ui.download import android.app.AlertDialog -import android.content.Intent import android.view.View import android.view.ViewGroup import android.widget.ArrayAdapter @@ -9,11 +8,9 @@ import android.widget.ImageButton import android.widget.ListView import android.widget.TextView import android.widget.Toast -import me.rhunk.snapenhance.core.R import me.rhunk.snapenhance.SharedContext import me.rhunk.snapenhance.bridge.types.BridgeFileType -import me.rhunk.snapenhance.ui.config.ConfigActivity -import me.rhunk.snapenhance.ui.spoof.DeviceSpooferActivity +import me.rhunk.snapenhance.core.R import java.io.File class ActionListAdapter( @@ -68,12 +65,6 @@ class DebugSettingsLayoutInflater( debugSettingsLayout.findViewById(R.id.setting_page_list).apply { adapter = ActionListAdapter(activity, R.layout.debug_setting_item, mutableListOf Unit>>().apply { - add(SharedContext.translation["config_activity.title"] to { - activity.startActivity(Intent(activity, ConfigActivity::class.java)) - }) - add(SharedContext.translation["spoof_activity.title"] to { - activity.startActivity(Intent(activity, DeviceSpooferActivity::class.java)) - }) add(debugSettingsTranslation["clear_cache_title"] to { context.cacheDir.listFiles()?.forEach { it.deleteRecursively() diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadListAdapter.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadListAdapter.kt index d7351d3e7..60231301e 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadListAdapter.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadListAdapter.kt @@ -23,8 +23,8 @@ import kotlinx.coroutines.job import kotlinx.coroutines.launch import kotlinx.coroutines.withTimeout import me.rhunk.snapenhance.Logger -import me.rhunk.snapenhance.core.R import me.rhunk.snapenhance.SharedContext +import me.rhunk.snapenhance.core.R import me.rhunk.snapenhance.data.FileType import me.rhunk.snapenhance.download.data.PendingDownload import me.rhunk.snapenhance.download.enums.DownloadStage diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadManagerActivity.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadManagerActivity.kt index 1472afd08..be7e1898c 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadManagerActivity.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadManagerActivity.kt @@ -15,10 +15,10 @@ import android.widget.ImageButton import android.widget.TextView import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.RecyclerView -import me.rhunk.snapenhance.core.BuildConfig -import me.rhunk.snapenhance.core.R import me.rhunk.snapenhance.SharedContext import me.rhunk.snapenhance.bridge.wrapper.TranslationWrapper +import me.rhunk.snapenhance.core.BuildConfig +import me.rhunk.snapenhance.core.R import me.rhunk.snapenhance.download.data.PendingDownload class DownloadManagerActivity : Activity() { diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/ChatActionMenu.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/ChatActionMenu.kt index bde676f48..e2ec912d5 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/ChatActionMenu.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/ChatActionMenu.kt @@ -10,7 +10,6 @@ import android.widget.Button import android.widget.LinearLayout import me.rhunk.snapenhance.Constants import me.rhunk.snapenhance.Constants.VIEW_INJECTED_CODE -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.features.impl.Messaging import me.rhunk.snapenhance.features.impl.downloader.MediaDownloader import me.rhunk.snapenhance.features.impl.spying.MessageLogger diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/FriendFeedInfoMenu.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/FriendFeedInfoMenu.kt index 98d55864c..3d2c3f011 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/FriendFeedInfoMenu.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/FriendFeedInfoMenu.kt @@ -21,7 +21,6 @@ import android.widget.LinearLayout import android.widget.Switch import android.widget.Toast import me.rhunk.snapenhance.Logger -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.data.ContentType import me.rhunk.snapenhance.data.wrapper.impl.FriendActionButton import me.rhunk.snapenhance.database.objects.ConversationMessage diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/MenuViewInjector.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/MenuViewInjector.kt index a867fb5dd..d5191de02 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/MenuViewInjector.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/MenuViewInjector.kt @@ -6,9 +6,7 @@ import android.view.View import android.view.ViewGroup import android.widget.FrameLayout import android.widget.LinearLayout -import de.robv.android.xposed.XC_MethodHook.Unhook import me.rhunk.snapenhance.Constants -import me.rhunk.snapenhance.config.ConfigProperty import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams import me.rhunk.snapenhance.features.impl.Messaging diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/OperaContextActionMenu.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/OperaContextActionMenu.kt index ecf261794..064383824 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/OperaContextActionMenu.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/OperaContextActionMenu.kt @@ -10,8 +10,8 @@ import android.widget.ScrollView import me.rhunk.snapenhance.Constants import me.rhunk.snapenhance.Logger import me.rhunk.snapenhance.features.impl.downloader.MediaDownloader -import me.rhunk.snapenhance.ui.menu.AbstractMenu import me.rhunk.snapenhance.ui.ViewAppearanceHelper.applyTheme +import me.rhunk.snapenhance.ui.menu.AbstractMenu @SuppressLint("DiscouragedApi") class OperaContextActionMenu : AbstractMenu() { diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/SettingsGearInjector.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/SettingsGearInjector.kt index 36e0e6d14..a1887cafb 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/SettingsGearInjector.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/SettingsGearInjector.kt @@ -6,9 +6,8 @@ import android.view.View import android.view.ViewGroup import android.widget.FrameLayout import android.widget.ImageView -import me.rhunk.snapenhance.core.BuildConfig import me.rhunk.snapenhance.Constants -import me.rhunk.snapenhance.ui.config.ConfigActivity +import me.rhunk.snapenhance.core.BuildConfig import me.rhunk.snapenhance.ui.menu.AbstractMenu import java.io.File diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/SettingsMenu.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/SettingsMenu.kt index 365f2844a..aee3e657a 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/SettingsMenu.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/SettingsMenu.kt @@ -1,26 +1,13 @@ package me.rhunk.snapenhance.ui.menu.impl import android.annotation.SuppressLint -import android.app.AlertDialog -import android.graphics.Color -import android.graphics.Typeface -import android.text.InputType import android.view.View import android.widget.Button -import android.widget.EditText -import android.widget.LinearLayout -import android.widget.Switch -import android.widget.TextView -import me.rhunk.snapenhance.config.ConfigProperty -import me.rhunk.snapenhance.config.impl.ConfigIntegerValue -import me.rhunk.snapenhance.config.impl.ConfigStateListValue -import me.rhunk.snapenhance.config.impl.ConfigStateSelection -import me.rhunk.snapenhance.config.impl.ConfigStateValue -import me.rhunk.snapenhance.config.impl.ConfigStringValue -import me.rhunk.snapenhance.ui.menu.AbstractMenu import me.rhunk.snapenhance.ui.ViewAppearanceHelper +import me.rhunk.snapenhance.ui.menu.AbstractMenu class SettingsMenu : AbstractMenu() { + /* @SuppressLint("ClickableViewAccessibility") private fun createCategoryTitle(key: String): TextView { val categoryText = TextView(context.androidContext) @@ -186,7 +173,7 @@ class SettingsMenu : AbstractMenu() { setPadding(0, 0, 0, thickness) setBackgroundColor(color) } - } + }*/ //TODO: quick settings @SuppressLint("SetTextI18n") @@ -194,9 +181,7 @@ class SettingsMenu : AbstractMenu() { val actions = context.actionManager.getActions().map { Pair(it) { val button = Button(viewModel.context) - button.text = (it.dependsOnProperty?.let { property -> - "["+context.translation["property.${property.translationKey}.name"] + "] " - }?: "") + context.translation[it.nameKey] + button.text = context.translation[it.nameKey] button.setOnClickListener { _ -> it.run() diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ui/spoof/DeviceSpooferActivity.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/spoof/DeviceSpooferActivity.kt deleted file mode 100644 index c6b61abfa..000000000 --- a/core/src/main/kotlin/me/rhunk/snapenhance/ui/spoof/DeviceSpooferActivity.kt +++ /dev/null @@ -1,111 +0,0 @@ -package me.rhunk.snapenhance.ui.spoof - -import android.app.Activity -import android.content.res.ColorStateList -import android.os.Bundle -import android.text.InputType -import android.view.View -import android.view.ViewGroup -import android.widget.ImageButton -import android.widget.Switch -import android.widget.TextView -import me.rhunk.snapenhance.core.R -import me.rhunk.snapenhance.SharedContext -import me.rhunk.snapenhance.config.ConfigCategory -import me.rhunk.snapenhance.config.impl.ConfigIntegerValue -import me.rhunk.snapenhance.config.impl.ConfigStateValue -import me.rhunk.snapenhance.config.impl.ConfigStringValue -import me.rhunk.snapenhance.ui.ItemHelper - -class DeviceSpooferActivity: Activity() { - private val itemHelper = ItemHelper(this) - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - SharedContext.ensureInitialized(this) - setContentView(R.layout.device_spoofer_activity) - - findViewById(R.id.title).text = "Device Spoofer" - findViewById(R.id.back_button).setOnClickListener { finish() } - val propertyListLayout = findViewById(R.id.spoof_property_list) - - SharedContext.config.entries().filter { it.key.category == ConfigCategory.DEVICE_SPOOFER }.forEach { (property, value) -> - val configItem = layoutInflater.inflate(R.layout.config_activity_item, propertyListLayout, false) - - val propertyName = SharedContext.translation["property.${property.translationKey}.name"] - - fun addSeparator() { - //add separator - propertyListLayout.addView(View(this).apply { - layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 1) - setBackgroundColor(getColor(R.color.tertiaryBackground)) - }) - } - - configItem.findViewById(R.id.name).text = propertyName - configItem.findViewById(R.id.description).also { - it.text = SharedContext.translation["property.${property.translationKey}.description"] - it.visibility = if (it.text.isEmpty()) View.GONE else View.VISIBLE - } - - fun addValueView(view: View) { - configItem.findViewById(R.id.value).addView(view) - } - - when (value) { - is ConfigStateValue -> { - val switch = Switch(this) - switch.isChecked = value.value() - switch.trackTintList = ColorStateList( - arrayOf( - intArrayOf(android.R.attr.state_checked), - intArrayOf(-android.R.attr.state_checked) - ), - intArrayOf( - switch.highlightColor, - getColor(R.color.tertiaryBackground) - ) - ) - switch.setOnCheckedChangeListener { _, isChecked -> - value.writeFrom(isChecked.toString()) - } - configItem.setOnClickListener { switch.toggle() } - addValueView(switch) - } - is ConfigStringValue, is ConfigIntegerValue -> { - val textView = itemHelper.createTranslatedTextView(property, shouldTranslatePropertyValue = false).also { - it.text = value.value().toString() - } - configItem.setOnClickListener { - if (value is ConfigIntegerValue) { - itemHelper.askForValue(property, InputType.TYPE_CLASS_NUMBER) { - try { - value.writeFrom(it) - textView.text = value.value().toString() - } catch (e: NumberFormatException) { - itemHelper.longToast(SharedContext.translation["config_activity.invalid_number_toast"], this) - } - } - return@setOnClickListener - } - itemHelper.askForValue(property, InputType.TYPE_CLASS_TEXT) { - value.writeFrom(it) - textView.text = value.value().toString() - } - } - addValueView(textView) - } - } - - propertyListLayout.addView(configItem) - addSeparator() - } - } - - @Deprecated("Deprecated in Java") - @Suppress("DEPRECATION") - override fun onBackPressed() { - super.onBackPressed() - finish() - } -} \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/util/export/MessageExporter.kt b/core/src/main/kotlin/me/rhunk/snapenhance/util/export/MessageExporter.kt index 53ceb4236..8050dec00 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/util/export/MessageExporter.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/util/export/MessageExporter.kt @@ -10,9 +10,9 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.withContext -import me.rhunk.snapenhance.core.BuildConfig import me.rhunk.snapenhance.Logger import me.rhunk.snapenhance.ModContext +import me.rhunk.snapenhance.core.BuildConfig import me.rhunk.snapenhance.data.ContentType import me.rhunk.snapenhance.data.FileType import me.rhunk.snapenhance.data.MediaReferenceType diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 796de5bd6..2433cc528 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,10 +1,10 @@ [versions] -agp = "8.2.0-alpha13" +agp = "8.2.0-alpha14" androidx-material = "1.6.0-alpha02" junit = "4.13.2" kotlin = "1.8.22" kotlinx-coroutines-android = "1.7.2" -kotlin-reflect = "1.8.21" +kotlin-reflect = "1.8.22" material-icons-extended = "1.6.0-alpha03" navigation-compose = "2.6.0" recyclerview = "1.3.1" From 369ed0a8a640bea6e3728a1ccc3bc5225c020ad7 Mon Sep 17 00:00:00 2001 From: rhunk <101876869+rhunk@users.noreply.github.com> Date: Wed, 2 Aug 2023 00:15:57 +0200 Subject: [PATCH 12/81] setup activity first impl --- app/src/main/AndroidManifest.xml | 6 + .../snapenhance/manager/setup/Requirements.kt | 33 +++++ .../manager/setup/SetupActivity.kt | 136 ++++++++++++++++++ .../manager/setup/screens/SetupScreen.kt | 11 ++ .../setup/screens/impl/FfmpegScreen.kt | 17 +++ .../setup/screens/impl/LanguageScreen.kt | 12 ++ .../setup/screens/impl/MappingsScreen.kt | 16 +++ .../setup/screens/impl/SaveFolderScreen.kt | 17 +++ .../setup/screens/impl/WelcomeScreen.kt | 17 +++ 9 files changed, 265 insertions(+) create mode 100644 app/src/main/kotlin/me/rhunk/snapenhance/manager/setup/Requirements.kt create mode 100644 app/src/main/kotlin/me/rhunk/snapenhance/manager/setup/SetupActivity.kt create mode 100644 app/src/main/kotlin/me/rhunk/snapenhance/manager/setup/screens/SetupScreen.kt create mode 100644 app/src/main/kotlin/me/rhunk/snapenhance/manager/setup/screens/impl/FfmpegScreen.kt create mode 100644 app/src/main/kotlin/me/rhunk/snapenhance/manager/setup/screens/impl/LanguageScreen.kt create mode 100644 app/src/main/kotlin/me/rhunk/snapenhance/manager/setup/screens/impl/MappingsScreen.kt create mode 100644 app/src/main/kotlin/me/rhunk/snapenhance/manager/setup/screens/impl/SaveFolderScreen.kt create mode 100644 app/src/main/kotlin/me/rhunk/snapenhance/manager/setup/screens/impl/WelcomeScreen.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 63e45933c..0532e0b4a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -46,6 +46,12 @@ + + () + + with(requiredScreens) { + with(requirements) { + if (firstRun || language) add(LanguageScreen().apply { route = "language" }) + if (firstRun) add(WelcomeScreen().apply { route = "welcome" }) + if (firstRun || saveFolder) add(SaveFolderScreen().apply { route = "saveFolder" }) + if (firstRun || mappings) add(MappingsScreen().apply { route = "mappings" }) + if (firstRun || ffmpeg) add(FfmpegScreen().apply { route = "ffmpeg" }) + } + } + + if (requiredScreens.isEmpty()) { + finish() + return + } + + setContent { + val navController = rememberNavController() + val canGoNext = remember { mutableStateOf(false) } + + fun nextScreen() { + if (!canGoNext.value) return + canGoNext.value = false + if (requiredScreens.size > 1) { + requiredScreens.removeFirst() + navController.navigate(requiredScreens.first().route) + } else { + finish() + } + } + + AppMaterialTheme { + Scaffold( + containerColor = MaterialTheme.colorScheme.background, + bottomBar = { + Column( + modifier = Modifier + .fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally + ) { + val alpha: Float by animateFloatAsState(if (canGoNext.value) 1f else 0f, + label = "NextButton" + ) + + FilledIconButton( + onClick = { nextScreen() }, + modifier = Modifier.padding(50.dp) + .width(60.dp) + .height(60.dp) + .alpha(alpha) + ) { + Icon( + imageVector = Icons.Default.ArrowForwardIos, + contentDescription = null + ) + } + } + }, + ) { paddingValues -> + Column( + modifier = Modifier + .background(MaterialTheme.colorScheme.background) + .fillMaxSize() + .padding(paddingValues) + ) { + NavHost( + navController = navController, + startDestination = requiredScreens.first().route + ) { + requiredScreens.forEach { screen -> + screen.allowNext = { canGoNext.value = it } + composable(screen.route) { + Column( + modifier = Modifier.fillMaxSize(), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + screen.Content() + } + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/setup/screens/SetupScreen.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/setup/screens/SetupScreen.kt new file mode 100644 index 000000000..d1600f8de --- /dev/null +++ b/app/src/main/kotlin/me/rhunk/snapenhance/manager/setup/screens/SetupScreen.kt @@ -0,0 +1,11 @@ +package me.rhunk.snapenhance.manager.setup.screens + +import androidx.compose.runtime.Composable + +abstract class SetupScreen { + lateinit var allowNext: (Boolean) -> Unit + lateinit var route: String + + @Composable + abstract fun Content() +} \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/setup/screens/impl/FfmpegScreen.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/setup/screens/impl/FfmpegScreen.kt new file mode 100644 index 000000000..3e9693b65 --- /dev/null +++ b/app/src/main/kotlin/me/rhunk/snapenhance/manager/setup/screens/impl/FfmpegScreen.kt @@ -0,0 +1,17 @@ +package me.rhunk.snapenhance.manager.setup.screens.impl + +import androidx.compose.material3.Button +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import me.rhunk.snapenhance.manager.setup.screens.SetupScreen + +class FfmpegScreen : SetupScreen() { + + @Composable + override fun Content() { + Text(text = "FFmpeg") + Button(onClick = { allowNext(true) }) { + Text(text = "Next") + } + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/setup/screens/impl/LanguageScreen.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/setup/screens/impl/LanguageScreen.kt new file mode 100644 index 000000000..f574b1156 --- /dev/null +++ b/app/src/main/kotlin/me/rhunk/snapenhance/manager/setup/screens/impl/LanguageScreen.kt @@ -0,0 +1,12 @@ +package me.rhunk.snapenhance.manager.setup.screens.impl + +import androidx.compose.runtime.Composable +import me.rhunk.snapenhance.manager.setup.screens.SetupScreen + +class LanguageScreen : SetupScreen(){ + + @Composable + override fun Content() { + + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/setup/screens/impl/MappingsScreen.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/setup/screens/impl/MappingsScreen.kt new file mode 100644 index 000000000..1b5d1748a --- /dev/null +++ b/app/src/main/kotlin/me/rhunk/snapenhance/manager/setup/screens/impl/MappingsScreen.kt @@ -0,0 +1,16 @@ +package me.rhunk.snapenhance.manager.setup.screens.impl + +import androidx.compose.material3.Button +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import me.rhunk.snapenhance.manager.setup.screens.SetupScreen + +class MappingsScreen : SetupScreen() { + @Composable + override fun Content() { + Text(text = "Mappings") + Button(onClick = { allowNext(true) }) { + Text(text = "Next") + } + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/setup/screens/impl/SaveFolderScreen.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/setup/screens/impl/SaveFolderScreen.kt new file mode 100644 index 000000000..62df1980f --- /dev/null +++ b/app/src/main/kotlin/me/rhunk/snapenhance/manager/setup/screens/impl/SaveFolderScreen.kt @@ -0,0 +1,17 @@ +package me.rhunk.snapenhance.manager.setup.screens.impl + +import androidx.compose.material3.Button +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import me.rhunk.snapenhance.manager.setup.screens.SetupScreen + +class SaveFolderScreen : SetupScreen() { + + @Composable + override fun Content() { + Text(text = "SaveFolder") + Button(onClick = {allowNext(true)}) { + Text(text = "Next") + } + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/setup/screens/impl/WelcomeScreen.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/setup/screens/impl/WelcomeScreen.kt new file mode 100644 index 000000000..64c348efb --- /dev/null +++ b/app/src/main/kotlin/me/rhunk/snapenhance/manager/setup/screens/impl/WelcomeScreen.kt @@ -0,0 +1,17 @@ +package me.rhunk.snapenhance.manager.setup.screens.impl + +import androidx.compose.material3.Button +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import me.rhunk.snapenhance.manager.setup.screens.SetupScreen + +class WelcomeScreen : SetupScreen() { + + @Composable + override fun Content() { + Text(text = "Welcome") + Button(onClick = { allowNext(true) }) { + Text(text = "Next") + } + } +} \ No newline at end of file From 5e827d1c46e75bcbae3e33b6ebfab8b1979fc6b3 Mon Sep 17 00:00:00 2001 From: rhunk <101876869+rhunk@users.noreply.github.com> Date: Wed, 2 Aug 2023 09:17:17 +0200 Subject: [PATCH 13/81] event dispatcher --- .../me/rhunk/snapenhance/EventDispatcher.kt | 37 +++++++++++++++++++ .../kotlin/me/rhunk/snapenhance/ModContext.kt | 3 ++ .../me/rhunk/snapenhance/SnapEnhance.kt | 1 + .../snapenhance/{ => core}/event/EventBus.kt | 26 +++++++------ .../core/event/impl/OnSnapInteractionEvent.kt | 9 +++++ .../event/impl/SendMessageWithContentEvent.kt | 8 ++++ .../me/rhunk/snapenhance/event/Events.kt | 3 -- .../snapenhance/features/impl/Messaging.kt | 7 ++-- .../impl/privacy/PreventMessageSending.kt | 10 ++--- .../impl/spying/PreventReadReceipts.kt | 8 ++-- 10 files changed, 87 insertions(+), 25 deletions(-) create mode 100644 core/src/main/kotlin/me/rhunk/snapenhance/EventDispatcher.kt rename core/src/main/kotlin/me/rhunk/snapenhance/{ => core}/event/EventBus.kt (63%) create mode 100644 core/src/main/kotlin/me/rhunk/snapenhance/core/event/impl/OnSnapInteractionEvent.kt create mode 100644 core/src/main/kotlin/me/rhunk/snapenhance/core/event/impl/SendMessageWithContentEvent.kt delete mode 100644 core/src/main/kotlin/me/rhunk/snapenhance/event/Events.kt diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/EventDispatcher.kt b/core/src/main/kotlin/me/rhunk/snapenhance/EventDispatcher.kt new file mode 100644 index 000000000..862ff28c6 --- /dev/null +++ b/core/src/main/kotlin/me/rhunk/snapenhance/EventDispatcher.kt @@ -0,0 +1,37 @@ +package me.rhunk.snapenhance + +import me.rhunk.snapenhance.core.event.impl.OnSnapInteractionEvent +import me.rhunk.snapenhance.core.event.impl.SendMessageWithContentEvent +import me.rhunk.snapenhance.data.wrapper.impl.MessageContent +import me.rhunk.snapenhance.data.wrapper.impl.SnapUUID +import me.rhunk.snapenhance.hook.HookStage +import me.rhunk.snapenhance.hook.hook +import me.rhunk.snapenhance.manager.Manager + +class EventDispatcher( + private val context: ModContext +) : Manager { + override fun init() { + context.classCache.conversationManager.hook("sendMessageWithContent", HookStage.BEFORE) { param -> + val messageContent = MessageContent(param.arg(1)) + context.event.post(SendMessageWithContentEvent(messageContent))?.let { + if (it.canceled) { + param.setResult(null) + } + } + } + + context.classCache.snapManager.hook("onSnapInteraction", HookStage.BEFORE) { param -> + val conversationId = SnapUUID(param.arg(1)) + val messageId = param.arg(2) + context.event.post(OnSnapInteractionEvent( + conversationId = conversationId, + messageId = messageId + ))?.let { + if (it.canceled) { + param.setResult(null) + } + } + } + } +} \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ModContext.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ModContext.kt index 7fa0f779c..a72537396 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/ModContext.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/ModContext.kt @@ -14,6 +14,7 @@ import kotlinx.coroutines.asCoroutineDispatcher import me.rhunk.snapenhance.bridge.BridgeClient import me.rhunk.snapenhance.bridge.wrapper.TranslationWrapper import me.rhunk.snapenhance.core.config.ModConfig +import me.rhunk.snapenhance.core.event.EventBus import me.rhunk.snapenhance.data.MessageSender import me.rhunk.snapenhance.database.DatabaseAccess import me.rhunk.snapenhance.features.Feature @@ -41,6 +42,8 @@ class ModContext { private val modConfig = ModConfig() val config by modConfig + val event = EventBus(this) + val eventDispatcher = EventDispatcher(this) val translation = TranslationWrapper() val features = FeatureManager(this) diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/SnapEnhance.kt b/core/src/main/kotlin/me/rhunk/snapenhance/SnapEnhance.kt index 97036e65d..a88578a20 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/SnapEnhance.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/SnapEnhance.kt @@ -99,6 +99,7 @@ class SnapEnhance { with(appContext) { reloadConfig() mappings.init() + eventDispatcher.init() //if mappings aren't loaded, we can't initialize features if (!mappings.areMappingsLoaded) return features.init() diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/event/EventBus.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/event/EventBus.kt similarity index 63% rename from core/src/main/kotlin/me/rhunk/snapenhance/event/EventBus.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/core/event/EventBus.kt index dca07ff97..7c171b53f 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/event/EventBus.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/core/event/EventBus.kt @@ -1,10 +1,12 @@ -package me.rhunk.snapenhance.event +package me.rhunk.snapenhance.core.event +import me.rhunk.snapenhance.Logger import me.rhunk.snapenhance.ModContext import kotlin.reflect.KClass abstract class Event { lateinit var context: ModContext + var canceled = false } interface IListener { @@ -23,12 +25,14 @@ class EventBus( subscribers[event]!!.add(listener) } - fun subscribe(event: KClass, listener: (T) -> Unit) { - subscribe(event, object : IListener { + inline fun subscribe(event: KClass, crossinline listener: (T) -> Unit): () -> Unit { + val obj = object : IListener { override fun handle(event: T) { listener(event) } - }) + } + subscribe(event, obj) + return { unsubscribe(event, obj) } } fun unsubscribe(event: KClass, listener: IListener) { @@ -38,22 +42,22 @@ class EventBus( subscribers[event]!!.remove(listener) } - fun post(event: T) { + fun post(event: T): T? { if (!subscribers.containsKey(event::class)) { - return + return null } event.context = context - subscribers[event::class]!!.forEach { listener -> + subscribers[event::class]?.forEach { listener -> @Suppress("UNCHECKED_CAST") - try { + runCatching { (listener as IListener).handle(event) - } catch (t: Throwable) { - println("Error while handling event ${event::class.simpleName} by ${listener::class.simpleName}") - t.printStackTrace() + }.onFailure { t -> + Logger.error("Error while handling event ${event::class.simpleName} by ${listener::class.simpleName}", t) } } + return event } fun clear() { diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/event/impl/OnSnapInteractionEvent.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/event/impl/OnSnapInteractionEvent.kt new file mode 100644 index 000000000..91a3f333d --- /dev/null +++ b/core/src/main/kotlin/me/rhunk/snapenhance/core/event/impl/OnSnapInteractionEvent.kt @@ -0,0 +1,9 @@ +package me.rhunk.snapenhance.core.event.impl + +import me.rhunk.snapenhance.core.event.Event +import me.rhunk.snapenhance.data.wrapper.impl.SnapUUID + +class OnSnapInteractionEvent( + val conversationId: SnapUUID, + val messageId: Long +) : Event() \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/event/impl/SendMessageWithContentEvent.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/event/impl/SendMessageWithContentEvent.kt new file mode 100644 index 000000000..c20292837 --- /dev/null +++ b/core/src/main/kotlin/me/rhunk/snapenhance/core/event/impl/SendMessageWithContentEvent.kt @@ -0,0 +1,8 @@ +package me.rhunk.snapenhance.core.event.impl + +import me.rhunk.snapenhance.core.event.Event +import me.rhunk.snapenhance.data.wrapper.impl.MessageContent + +class SendMessageWithContentEvent( + val messageContent: MessageContent +) : Event() \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/event/Events.kt b/core/src/main/kotlin/me/rhunk/snapenhance/event/Events.kt deleted file mode 100644 index aae56c2d4..000000000 --- a/core/src/main/kotlin/me/rhunk/snapenhance/event/Events.kt +++ /dev/null @@ -1,3 +0,0 @@ -package me.rhunk.snapenhance.event - -//TODO: addView event \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/Messaging.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/Messaging.kt index 99096ebef..ca98304c5 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/Messaging.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/Messaging.kt @@ -1,5 +1,6 @@ package me.rhunk.snapenhance.features.impl +import me.rhunk.snapenhance.core.event.impl.OnSnapInteractionEvent import me.rhunk.snapenhance.data.wrapper.impl.SnapUUID import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams @@ -72,9 +73,9 @@ class Messaging : Feature("Messaging", loadParams = FeatureLoadParams.ACTIVITY_C } //get last opened snap for media downloader - Hooker.hook(context.classCache.snapManager, "onSnapInteraction", HookStage.BEFORE) { param -> - openedConversationUUID = SnapUUID(param.arg(1)) - lastFocusedMessageId = param.arg(2) + context.event.subscribe(OnSnapInteractionEvent::class) { event -> + openedConversationUUID = event.conversationId + lastFocusedMessageId = event.messageId } Hooker.hook(context.classCache.conversationManager, "fetchMessage", HookStage.BEFORE) { param -> diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/privacy/PreventMessageSending.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/privacy/PreventMessageSending.kt index ada46e858..409ef0af4 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/privacy/PreventMessageSending.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/privacy/PreventMessageSending.kt @@ -1,6 +1,7 @@ package me.rhunk.snapenhance.features.impl.privacy import me.rhunk.snapenhance.Logger +import me.rhunk.snapenhance.core.event.impl.SendMessageWithContentEvent import me.rhunk.snapenhance.data.NotificationType import me.rhunk.snapenhance.data.wrapper.impl.MessageContent import me.rhunk.snapenhance.features.Feature @@ -23,14 +24,13 @@ class PreventMessageSending : Feature("Prevent message sending", loadParams = Fe } } - context.classCache.conversationManager.hook("sendMessageWithContent", HookStage.BEFORE) { param -> - val message = MessageContent(param.arg(1)) - val contentType = message.contentType - val associatedType = NotificationType.fromContentType(contentType) ?: return@hook + context.event.subscribe(SendMessageWithContentEvent::class) { event -> + val contentType = event.messageContent.contentType + val associatedType = NotificationType.fromContentType(contentType) ?: return@subscribe if (preventMessageSending.contains(associatedType.key)) { Logger.debug("Preventing message sending for $associatedType") - param.setResult(null) + event.canceled = true } } } diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/PreventReadReceipts.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/PreventReadReceipts.kt index aa5305297..11731d420 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/PreventReadReceipts.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/PreventReadReceipts.kt @@ -1,5 +1,6 @@ package me.rhunk.snapenhance.features.impl.spying +import me.rhunk.snapenhance.core.event.impl.OnSnapInteractionEvent import me.rhunk.snapenhance.data.wrapper.impl.SnapUUID import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams @@ -19,9 +20,10 @@ class PreventReadReceipts : Feature("PreventReadReceipts", loadParams = FeatureL it.setResult(null) } } - Hooker.hook(context.classCache.snapManager, "onSnapInteraction", HookStage.BEFORE) { - if (isConversationInStealthMode(SnapUUID(it.arg(1) as Any))) { - it.setResult(null) + + context.event.subscribe(OnSnapInteractionEvent::class) { event -> + if (isConversationInStealthMode(event.conversationId)) { + event.canceled = true } } } From c5def2e69db6aaa63903932340741af0ee9fa4fc Mon Sep 17 00:00:00 2001 From: rhunk <101876869+rhunk@users.noreply.github.com> Date: Wed, 2 Aug 2023 09:35:02 +0200 Subject: [PATCH 14/81] merge GalleryMediaSendOverride --- .../rhunk/snapenhance/manager/Navigation.kt | 2 +- .../me/rhunk/snapenhance/EventDispatcher.kt | 12 +++++---- .../kotlin/me/rhunk/snapenhance/ModContext.kt | 2 +- .../core/config/impl/MessagingTweaks.kt | 2 +- .../event/impl/SendMessageWithContentEvent.kt | 8 ------ .../core/{event => eventbus}/EventBus.kt | 7 ++++-- .../core/eventbus/events/AbstractHookEvent.kt | 8 ++++++ .../events}/impl/OnSnapInteractionEvent.kt | 6 ++--- .../impl/SendMessageWithContentEvent.kt | 8 ++++++ .../snapenhance/features/impl/Messaging.kt | 2 +- .../impl/privacy/PreventMessageSending.kt | 3 +-- .../impl/spying/PreventReadReceipts.kt | 2 +- .../impl/tweaks/GalleryMediaSendOverride.kt | 25 ++++++++----------- 13 files changed, 48 insertions(+), 39 deletions(-) delete mode 100644 core/src/main/kotlin/me/rhunk/snapenhance/core/event/impl/SendMessageWithContentEvent.kt rename core/src/main/kotlin/me/rhunk/snapenhance/core/{event => eventbus}/EventBus.kt (85%) create mode 100644 core/src/main/kotlin/me/rhunk/snapenhance/core/eventbus/events/AbstractHookEvent.kt rename core/src/main/kotlin/me/rhunk/snapenhance/core/{event => eventbus/events}/impl/OnSnapInteractionEvent.kt (50%) create mode 100644 core/src/main/kotlin/me/rhunk/snapenhance/core/eventbus/events/impl/SendMessageWithContentEvent.kt diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/Navigation.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/Navigation.kt index 064bf731c..e432aa533 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/manager/Navigation.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/manager/Navigation.kt @@ -60,7 +60,7 @@ class Navigation( NavigationBarItem( modifier = Modifier - .requiredWidth(120.dp) + .requiredWidth(75.dp) .fillMaxHeight(), icon = { val iconOffset by animateDpAsState( diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/EventDispatcher.kt b/core/src/main/kotlin/me/rhunk/snapenhance/EventDispatcher.kt index 862ff28c6..ebe8a506b 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/EventDispatcher.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/EventDispatcher.kt @@ -1,7 +1,7 @@ package me.rhunk.snapenhance -import me.rhunk.snapenhance.core.event.impl.OnSnapInteractionEvent -import me.rhunk.snapenhance.core.event.impl.SendMessageWithContentEvent +import me.rhunk.snapenhance.core.eventbus.events.impl.OnSnapInteractionEvent +import me.rhunk.snapenhance.core.eventbus.events.impl.SendMessageWithContentEvent import me.rhunk.snapenhance.data.wrapper.impl.MessageContent import me.rhunk.snapenhance.data.wrapper.impl.SnapUUID import me.rhunk.snapenhance.hook.HookStage @@ -14,7 +14,7 @@ class EventDispatcher( override fun init() { context.classCache.conversationManager.hook("sendMessageWithContent", HookStage.BEFORE) { param -> val messageContent = MessageContent(param.arg(1)) - context.event.post(SendMessageWithContentEvent(messageContent))?.let { + context.event.post(SendMessageWithContentEvent(messageContent).apply { adapter = param })?.let { if (it.canceled) { param.setResult(null) } @@ -24,10 +24,12 @@ class EventDispatcher( context.classCache.snapManager.hook("onSnapInteraction", HookStage.BEFORE) { param -> val conversationId = SnapUUID(param.arg(1)) val messageId = param.arg(2) - context.event.post(OnSnapInteractionEvent( + context.event.post( + OnSnapInteractionEvent( conversationId = conversationId, messageId = messageId - ))?.let { + ) + )?.let { if (it.canceled) { param.setResult(null) } diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ModContext.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ModContext.kt index a72537396..3b9b1fe76 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/ModContext.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/ModContext.kt @@ -14,7 +14,7 @@ import kotlinx.coroutines.asCoroutineDispatcher import me.rhunk.snapenhance.bridge.BridgeClient import me.rhunk.snapenhance.bridge.wrapper.TranslationWrapper import me.rhunk.snapenhance.core.config.ModConfig -import me.rhunk.snapenhance.core.event.EventBus +import me.rhunk.snapenhance.core.eventbus.EventBus import me.rhunk.snapenhance.data.MessageSender import me.rhunk.snapenhance.database.DatabaseAccess import me.rhunk.snapenhance.features.Feature diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/MessagingTweaks.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/MessagingTweaks.kt index 5530525f3..a559e99a2 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/MessagingTweaks.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/MessagingTweaks.kt @@ -19,7 +19,7 @@ class MessagingTweaks : ConfigContainer() { "STICKER" ) - val galleryMediaSendOverride = unique("gallery_media_send_override", "NOTE", "SNAP", "LIVE_SNAP") + val galleryMediaSendOverride = boolean("gallery_media_send_override") val messagePreviewLength = integer("message_preview_length", defaultValue = 20) } \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/event/impl/SendMessageWithContentEvent.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/event/impl/SendMessageWithContentEvent.kt deleted file mode 100644 index c20292837..000000000 --- a/core/src/main/kotlin/me/rhunk/snapenhance/core/event/impl/SendMessageWithContentEvent.kt +++ /dev/null @@ -1,8 +0,0 @@ -package me.rhunk.snapenhance.core.event.impl - -import me.rhunk.snapenhance.core.event.Event -import me.rhunk.snapenhance.data.wrapper.impl.MessageContent - -class SendMessageWithContentEvent( - val messageContent: MessageContent -) : Event() \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/event/EventBus.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/eventbus/EventBus.kt similarity index 85% rename from core/src/main/kotlin/me/rhunk/snapenhance/core/event/EventBus.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/core/eventbus/EventBus.kt index 7c171b53f..9358ec3ef 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/core/event/EventBus.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/core/eventbus/EventBus.kt @@ -1,4 +1,4 @@ -package me.rhunk.snapenhance.core.event +package me.rhunk.snapenhance.core.eventbus import me.rhunk.snapenhance.Logger import me.rhunk.snapenhance.ModContext @@ -25,9 +25,12 @@ class EventBus( subscribers[event]!!.add(listener) } - inline fun subscribe(event: KClass, crossinline listener: (T) -> Unit): () -> Unit { + inline fun subscribe(event: KClass, crossinline listener: (T) -> Unit) = subscribe(event, { true }, listener) + + inline fun subscribe(event: KClass, crossinline filter: (T) -> Boolean, crossinline listener: (T) -> Unit): () -> Unit { val obj = object : IListener { override fun handle(event: T) { + if (!filter(event)) return listener(event) } } diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/eventbus/events/AbstractHookEvent.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/eventbus/events/AbstractHookEvent.kt new file mode 100644 index 000000000..41465d57f --- /dev/null +++ b/core/src/main/kotlin/me/rhunk/snapenhance/core/eventbus/events/AbstractHookEvent.kt @@ -0,0 +1,8 @@ +package me.rhunk.snapenhance.core.eventbus.events + +import me.rhunk.snapenhance.core.eventbus.Event +import me.rhunk.snapenhance.hook.HookAdapter + +abstract class AbstractHookEvent : Event() { + lateinit var adapter: HookAdapter +} \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/event/impl/OnSnapInteractionEvent.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/eventbus/events/impl/OnSnapInteractionEvent.kt similarity index 50% rename from core/src/main/kotlin/me/rhunk/snapenhance/core/event/impl/OnSnapInteractionEvent.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/core/eventbus/events/impl/OnSnapInteractionEvent.kt index 91a3f333d..ebdaf4c86 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/core/event/impl/OnSnapInteractionEvent.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/core/eventbus/events/impl/OnSnapInteractionEvent.kt @@ -1,9 +1,9 @@ -package me.rhunk.snapenhance.core.event.impl +package me.rhunk.snapenhance.core.eventbus.events.impl -import me.rhunk.snapenhance.core.event.Event +import me.rhunk.snapenhance.core.eventbus.events.AbstractHookEvent import me.rhunk.snapenhance.data.wrapper.impl.SnapUUID class OnSnapInteractionEvent( val conversationId: SnapUUID, val messageId: Long -) : Event() \ No newline at end of file +) : AbstractHookEvent() \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/eventbus/events/impl/SendMessageWithContentEvent.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/eventbus/events/impl/SendMessageWithContentEvent.kt new file mode 100644 index 000000000..d2cb06f6b --- /dev/null +++ b/core/src/main/kotlin/me/rhunk/snapenhance/core/eventbus/events/impl/SendMessageWithContentEvent.kt @@ -0,0 +1,8 @@ +package me.rhunk.snapenhance.core.eventbus.events.impl + +import me.rhunk.snapenhance.core.eventbus.events.AbstractHookEvent +import me.rhunk.snapenhance.data.wrapper.impl.MessageContent + +class SendMessageWithContentEvent( + val messageContent: MessageContent +) : AbstractHookEvent() \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/Messaging.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/Messaging.kt index ca98304c5..d00cce3e0 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/Messaging.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/Messaging.kt @@ -1,6 +1,6 @@ package me.rhunk.snapenhance.features.impl -import me.rhunk.snapenhance.core.event.impl.OnSnapInteractionEvent +import me.rhunk.snapenhance.core.eventbus.events.impl.OnSnapInteractionEvent import me.rhunk.snapenhance.data.wrapper.impl.SnapUUID import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/privacy/PreventMessageSending.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/privacy/PreventMessageSending.kt index 409ef0af4..78dc70f62 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/privacy/PreventMessageSending.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/privacy/PreventMessageSending.kt @@ -1,9 +1,8 @@ package me.rhunk.snapenhance.features.impl.privacy import me.rhunk.snapenhance.Logger -import me.rhunk.snapenhance.core.event.impl.SendMessageWithContentEvent +import me.rhunk.snapenhance.core.eventbus.events.impl.SendMessageWithContentEvent import me.rhunk.snapenhance.data.NotificationType -import me.rhunk.snapenhance.data.wrapper.impl.MessageContent import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams import me.rhunk.snapenhance.hook.HookStage diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/PreventReadReceipts.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/PreventReadReceipts.kt index 11731d420..a26319fa2 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/PreventReadReceipts.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/spying/PreventReadReceipts.kt @@ -1,6 +1,6 @@ package me.rhunk.snapenhance.features.impl.spying -import me.rhunk.snapenhance.core.event.impl.OnSnapInteractionEvent +import me.rhunk.snapenhance.core.eventbus.events.impl.OnSnapInteractionEvent import me.rhunk.snapenhance.data.wrapper.impl.SnapUUID import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/GalleryMediaSendOverride.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/GalleryMediaSendOverride.kt index 344162fcb..23cd48103 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/GalleryMediaSendOverride.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/GalleryMediaSendOverride.kt @@ -1,14 +1,10 @@ package me.rhunk.snapenhance.features.impl.tweaks -import android.app.AlertDialog -import me.rhunk.snapenhance.config.ConfigProperty +import me.rhunk.snapenhance.core.eventbus.events.impl.SendMessageWithContentEvent import me.rhunk.snapenhance.data.ContentType import me.rhunk.snapenhance.data.MessageSender -import me.rhunk.snapenhance.data.wrapper.impl.MessageContent import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams -import me.rhunk.snapenhance.hook.HookStage -import me.rhunk.snapenhance.hook.Hooker import me.rhunk.snapenhance.ui.ViewAppearanceHelper import me.rhunk.snapenhance.util.protobuf.ProtoReader @@ -20,20 +16,21 @@ class GalleryMediaSendOverride : Feature("Gallery Media Send Override", loadPara "LIVE_SNAP", "NOTE" ).associateWith { - context.translation[ConfigProperty.GALLERY_MEDIA_SEND_OVERRIDE.getOptionTranslationKey(it)] + it } - Hooker.hook(context.classCache.conversationManager, "sendMessageWithContent", HookStage.BEFORE, { - context.config.bool(ConfigProperty.GALLERY_MEDIA_SEND_OVERRIDE) - }) { param -> - val localMessageContent = MessageContent(param.arg(1)) - if (localMessageContent.contentType != ContentType.EXTERNAL_MEDIA) return@hook + context.event.subscribe(SendMessageWithContentEvent::class, { + context.config.messaging.galleryMediaSendOverride.get() + }) { event -> + + val localMessageContent = event.messageContent + if (localMessageContent.contentType != ContentType.EXTERNAL_MEDIA) return@subscribe //prevent story replies val messageProtoReader = ProtoReader(localMessageContent.content) - if (messageProtoReader.exists(7)) return@hook + if (messageProtoReader.exists(7)) return@subscribe - param.setResult(null) + event.canceled = true context.runOnUiThread { ViewAppearanceHelper.newAlertDialogBuilder(context.mainActivity!!) @@ -66,7 +63,7 @@ class GalleryMediaSendOverride : Feature("Gallery Media Send Override", loadPara } } - param.invokeOriginal() + event.adapter.invokeOriginal() } .setNegativeButton(context.translation["button.cancel"], null) .show() From b35bbfb6b20cff5fd5631eff583ecbb13ad57b01 Mon Sep 17 00:00:00 2001 From: rhunk <101876869+rhunk@users.noreply.github.com> Date: Wed, 2 Aug 2023 10:14:36 +0200 Subject: [PATCH 15/81] ui: container state - integer dialogs --- .../manager/sections/features/Dialogs.kt | 9 +-- .../sections/features/FeaturesSection.kt | 71 +++++++++++++------ 2 files changed, 52 insertions(+), 28 deletions(-) diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/features/Dialogs.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/features/Dialogs.kt index 5e07232e9..336ac8cd9 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/features/Dialogs.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/features/Dialogs.kt @@ -125,12 +125,9 @@ class Dialogs { fieldValue.value = it }, keyboardOptions = when (property.key.dataType.type) { - DataProcessors.Type.INTEGER -> { - KeyboardOptions(keyboardType = KeyboardType.Number) - } - else -> { - KeyboardOptions(keyboardType = KeyboardType.Text) - } + DataProcessors.Type.INTEGER -> KeyboardOptions(keyboardType = KeyboardType.Number) + DataProcessors.Type.FLOAT -> KeyboardOptions(keyboardType = KeyboardType.Decimal) + else -> KeyboardOptions(keyboardType = KeyboardType.Text) }, singleLine = true ) diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/features/FeaturesSection.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/features/FeaturesSection.kt index 756292418..99fdff4b8 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/features/FeaturesSection.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/features/FeaturesSection.kt @@ -1,13 +1,17 @@ package me.rhunk.snapenhance.manager.sections.features +import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.layout.wrapContentWidth import androidx.compose.foundation.lazy.LazyColumn @@ -106,7 +110,7 @@ class FeaturesSection : Section() { ) } - DataProcessors.Type.STRING_MULTIPLE_SELECTION, DataProcessors.Type.STRING, DataProcessors.Type.INTEGER -> { + DataProcessors.Type.STRING_MULTIPLE_SELECTION, DataProcessors.Type.STRING, DataProcessors.Type.INTEGER, DataProcessors.Type.FLOAT -> { dialogComposable.value = { when (dataType) { DataProcessors.Type.STRING_MULTIPLE_SELECTION -> { @@ -120,7 +124,7 @@ class FeaturesSection : Section() { } registerDialogOnClickCallback().let { { it.invoke(true) } }.also { - if (dataType == DataProcessors.Type.INTEGER) { + if (dataType == DataProcessors.Type.INTEGER || dataType == DataProcessors.Type.FLOAT) { FilledIconButton(onClick = it) { Text( text = propertyValue.get().toString(), @@ -135,7 +139,36 @@ class FeaturesSection : Section() { } } } - else -> {} + DataProcessors.Type.CONTAINER -> { + val container = propertyValue.get() as ConfigContainer + + registerClickCallback { + navController.navigate("container/${property.name}") + } + + if (container.globalState == null) return + + val state = remember { mutableStateOf(container.globalState!!) } + + Box( + modifier = Modifier + .padding(end = 15.dp), + ) { + + Box(modifier = Modifier + .height(50.dp) + .width(1.dp) + .background(color = MaterialTheme.colors.onBackground.copy(alpha = 0.12f), shape = RoundedCornerShape(5.dp))) + } + + Switch( + checked = state.value, + onCheckedChange = { + state.value = state.value.not() + container.globalState = state.value + } + ) + } } } @@ -175,24 +208,16 @@ class FeaturesSection : Section() { ) } - when (property.key.dataType.type) { - DataProcessors.Type.CONTAINER -> { - clickCallback.value = { - navController.navigate("container/${property.name}") - } - } - else -> { - Column( - modifier = Modifier - .align(Alignment.CenterVertically) - .padding(all = 10.dp) - ) { - PropertyAction(property, registerClickCallback = { callback -> - clickCallback.value = callback - callback - }) - } - } + Row( + modifier = Modifier + .align(Alignment.CenterVertically) + .padding(all = 10.dp), + verticalAlignment = Alignment.CenterVertically + ) { + PropertyAction(property, registerClickCallback = { callback -> + clickCallback.value = callback + callback + }) } } } @@ -249,7 +274,9 @@ class FeaturesSection : Section() { }, content = { innerPadding -> LazyColumn( - modifier = Modifier.fillMaxHeight().padding(innerPadding), + modifier = Modifier + .fillMaxHeight() + .padding(innerPadding), verticalArrangement = Arrangement.Center ) { items(properties) { From 853ceec290c698ea7ba787abfb3e196832a575e5 Mon Sep 17 00:00:00 2001 From: rhunk <101876869+rhunk@users.noreply.github.com> Date: Wed, 2 Aug 2023 21:44:07 +0200 Subject: [PATCH 16/81] refactor: packages --- app/src/main/AndroidManifest.xml | 4 ++-- .../manager/sections/DebugSection.kt | 4 ---- .../snapenhance/{manager => ui}/Theme.kt | 2 +- .../{ => ui}/manager/MainActivity.kt | 6 +++--- .../data => ui/manager}/ManagerContext.kt | 9 ++++++--- .../{ => ui}/manager/Navigation.kt | 3 +-- .../snapenhance/{ => ui}/manager/Section.kt | 9 ++++----- .../manager/data/InstallationSummary.kt | 2 +- .../ui/manager/sections/DebugSection.kt | 4 ++++ .../{ => ui}/manager/sections/HomeSection.kt | 6 +++--- .../manager/sections/NotImplemented.kt | 4 ++-- .../sections/features/CallbackAlias.kt | 2 +- .../manager/sections/features/Dialogs.kt | 2 +- .../sections/features/FeaturesSection.kt | 4 ++-- .../manager/util/SaveFolderChecker.kt | 2 +- .../{manager => ui}/setup/Requirements.kt | 2 +- .../{manager => ui}/setup/SetupActivity.kt | 16 +++++++-------- .../snapenhance/ui/setup/SetupContext.kt | 14 +++++++++++++ .../setup/screens/SetupScreen.kt | 2 +- .../setup/screens/impl/FfmpegScreen.kt | 4 ++-- .../setup/screens/impl/LanguageScreen.kt | 5 ++--- .../setup/screens/impl/MappingsScreen.kt | 4 ++-- .../setup/screens/impl/SaveFolderScreen.kt | 4 ++-- .../setup/screens/impl/WelcomeScreen.kt | 4 ++-- .../kotlin/me/rhunk/snapenhance/ModContext.kt | 4 ++-- .../me/rhunk/snapenhance/SharedContext.kt | 6 +++--- .../rhunk/snapenhance/bridge/BridgeService.kt | 4 ++-- ...TranslationWrapper.kt => LocaleWrapper.kt} | 10 +++++----- .../snapenhance/core/config/ModConfig.kt | 20 ++++++++++++------- .../ui/download/DownloadManagerActivity.kt | 4 ++-- 30 files changed, 93 insertions(+), 73 deletions(-) delete mode 100644 app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/DebugSection.kt rename app/src/main/kotlin/me/rhunk/snapenhance/{manager => ui}/Theme.kt (99%) rename app/src/main/kotlin/me/rhunk/snapenhance/{ => ui}/manager/MainActivity.kt (91%) rename app/src/main/kotlin/me/rhunk/snapenhance/{manager/data => ui/manager}/ManagerContext.kt (75%) rename app/src/main/kotlin/me/rhunk/snapenhance/{ => ui}/manager/Navigation.kt (97%) rename app/src/main/kotlin/me/rhunk/snapenhance/{ => ui}/manager/Section.kt (86%) rename app/src/main/kotlin/me/rhunk/snapenhance/{ => ui}/manager/data/InstallationSummary.kt (86%) create mode 100644 app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/DebugSection.kt rename app/src/main/kotlin/me/rhunk/snapenhance/{ => ui}/manager/sections/HomeSection.kt (96%) rename app/src/main/kotlin/me/rhunk/snapenhance/{ => ui}/manager/sections/NotImplemented.kt (75%) rename app/src/main/kotlin/me/rhunk/snapenhance/{ => ui}/manager/sections/features/CallbackAlias.kt (65%) rename app/src/main/kotlin/me/rhunk/snapenhance/{ => ui}/manager/sections/features/Dialogs.kt (99%) rename app/src/main/kotlin/me/rhunk/snapenhance/{ => ui}/manager/sections/features/FeaturesSection.kt (99%) rename app/src/main/kotlin/me/rhunk/snapenhance/{ => ui}/manager/util/SaveFolderChecker.kt (97%) rename app/src/main/kotlin/me/rhunk/snapenhance/{manager => ui}/setup/Requirements.kt (96%) rename app/src/main/kotlin/me/rhunk/snapenhance/{manager => ui}/setup/SetupActivity.kt (91%) create mode 100644 app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/SetupContext.kt rename app/src/main/kotlin/me/rhunk/snapenhance/{manager => ui}/setup/screens/SetupScreen.kt (79%) rename app/src/main/kotlin/me/rhunk/snapenhance/{manager => ui}/setup/screens/impl/FfmpegScreen.kt (73%) rename app/src/main/kotlin/me/rhunk/snapenhance/{manager => ui}/setup/screens/impl/LanguageScreen.kt (53%) rename app/src/main/kotlin/me/rhunk/snapenhance/{manager => ui}/setup/screens/impl/MappingsScreen.kt (74%) rename app/src/main/kotlin/me/rhunk/snapenhance/{manager => ui}/setup/screens/impl/SaveFolderScreen.kt (74%) rename app/src/main/kotlin/me/rhunk/snapenhance/{manager => ui}/setup/screens/impl/WelcomeScreen.kt (74%) rename core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/{TranslationWrapper.kt => LocaleWrapper.kt} (92%) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 0532e0b4a..151fd6488 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -38,7 +38,7 @@ @@ -47,7 +47,7 @@ diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/DebugSection.kt b/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/DebugSection.kt deleted file mode 100644 index b9b0f6065..000000000 --- a/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/DebugSection.kt +++ /dev/null @@ -1,4 +0,0 @@ -package me.rhunk.snapenhance.manager.sections - -class DebugSection { -} \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/Theme.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/Theme.kt similarity index 99% rename from app/src/main/kotlin/me/rhunk/snapenhance/manager/Theme.kt rename to app/src/main/kotlin/me/rhunk/snapenhance/ui/Theme.kt index eedc6b140..a03cef8ed 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/manager/Theme.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/Theme.kt @@ -1,4 +1,4 @@ -package me.rhunk.snapenhance.manager +package me.rhunk.snapenhance.ui import android.os.Build import androidx.compose.foundation.isSystemInDarkTheme diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/MainActivity.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/MainActivity.kt similarity index 91% rename from app/src/main/kotlin/me/rhunk/snapenhance/manager/MainActivity.kt rename to app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/MainActivity.kt index 4f81103b2..7befc61ac 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/manager/MainActivity.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/MainActivity.kt @@ -1,4 +1,4 @@ -package me.rhunk.snapenhance.manager +package me.rhunk.snapenhance.ui.manager import android.annotation.SuppressLint import android.os.Bundle @@ -7,8 +7,8 @@ import androidx.activity.compose.setContent import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.navigation.compose.rememberNavController -import me.rhunk.snapenhance.manager.data.ManagerContext -import me.rhunk.snapenhance.manager.util.SaveFolderChecker +import me.rhunk.snapenhance.ui.AppMaterialTheme +import me.rhunk.snapenhance.ui.manager.util.SaveFolderChecker import me.rhunk.snapenhance.util.ActivityResultCallback class MainActivity : ComponentActivity() { diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/data/ManagerContext.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/ManagerContext.kt similarity index 75% rename from app/src/main/kotlin/me/rhunk/snapenhance/manager/data/ManagerContext.kt rename to app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/ManagerContext.kt index c8643a7c3..42166d264 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/manager/data/ManagerContext.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/ManagerContext.kt @@ -1,15 +1,18 @@ -package me.rhunk.snapenhance.manager.data +package me.rhunk.snapenhance.ui.manager import android.content.Context import me.rhunk.snapenhance.bridge.wrapper.MappingsWrapper -import me.rhunk.snapenhance.bridge.wrapper.TranslationWrapper +import me.rhunk.snapenhance.bridge.wrapper.LocaleWrapper import me.rhunk.snapenhance.core.config.ModConfig +import me.rhunk.snapenhance.ui.manager.data.InstallationSummary +import me.rhunk.snapenhance.ui.manager.data.ModMappingsInfo +import me.rhunk.snapenhance.ui.manager.data.SnapchatAppInfo class ManagerContext( private val context: Context ) { val config = ModConfig() - val translation = TranslationWrapper() + val translation = LocaleWrapper() val mappings = MappingsWrapper(context) init { diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/Navigation.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/Navigation.kt similarity index 97% rename from app/src/main/kotlin/me/rhunk/snapenhance/manager/Navigation.kt rename to app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/Navigation.kt index e432aa533..e6cbdce47 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/manager/Navigation.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/Navigation.kt @@ -1,4 +1,4 @@ -package me.rhunk.snapenhance.manager +package me.rhunk.snapenhance.ui.manager import androidx.compose.animation.core.animateDpAsState import androidx.compose.foundation.layout.PaddingValues @@ -21,7 +21,6 @@ import androidx.navigation.NavGraph.Companion.findStartDestination import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import androidx.navigation.compose.currentBackStackEntryAsState -import me.rhunk.snapenhance.manager.data.ManagerContext class Navigation( diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/Section.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/Section.kt similarity index 86% rename from app/src/main/kotlin/me/rhunk/snapenhance/manager/Section.kt rename to app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/Section.kt index ab9d338cb..064e51dd1 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/manager/Section.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/Section.kt @@ -1,4 +1,4 @@ -package me.rhunk.snapenhance.manager +package me.rhunk.snapenhance.ui.manager import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.BugReport @@ -11,10 +11,9 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable -import me.rhunk.snapenhance.manager.data.ManagerContext -import me.rhunk.snapenhance.manager.sections.HomeSection -import me.rhunk.snapenhance.manager.sections.NotImplemented -import me.rhunk.snapenhance.manager.sections.features.FeaturesSection +import me.rhunk.snapenhance.ui.manager.sections.HomeSection +import me.rhunk.snapenhance.ui.manager.sections.NotImplemented +import me.rhunk.snapenhance.ui.manager.sections.features.FeaturesSection import kotlin.reflect.KClass enum class EnumSection( diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/data/InstallationSummary.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/data/InstallationSummary.kt similarity index 86% rename from app/src/main/kotlin/me/rhunk/snapenhance/manager/data/InstallationSummary.kt rename to app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/data/InstallationSummary.kt index f7b0150da..a5dcfd226 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/manager/data/InstallationSummary.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/data/InstallationSummary.kt @@ -1,4 +1,4 @@ -package me.rhunk.snapenhance.manager.data +package me.rhunk.snapenhance.ui.manager.data data class SnapchatAppInfo( diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/DebugSection.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/DebugSection.kt new file mode 100644 index 000000000..e37e169d4 --- /dev/null +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/DebugSection.kt @@ -0,0 +1,4 @@ +package me.rhunk.snapenhance.ui.manager.sections + +class DebugSection { +} \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/HomeSection.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/HomeSection.kt similarity index 96% rename from app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/HomeSection.kt rename to app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/HomeSection.kt index 5e89be9bf..e493a30c6 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/HomeSection.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/HomeSection.kt @@ -1,4 +1,4 @@ -package me.rhunk.snapenhance.manager.sections +package me.rhunk.snapenhance.ui.manager.sections import androidx.compose.foundation.ScrollState import androidx.compose.foundation.layout.Arrangement @@ -23,8 +23,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import me.rhunk.snapenhance.manager.Section -import me.rhunk.snapenhance.manager.data.InstallationSummary +import me.rhunk.snapenhance.ui.manager.Section +import me.rhunk.snapenhance.ui.manager.data.InstallationSummary class HomeSection : Section() { companion object { diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/NotImplemented.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/NotImplemented.kt similarity index 75% rename from app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/NotImplemented.kt rename to app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/NotImplemented.kt index be57ff21f..83bceda9c 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/NotImplemented.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/NotImplemented.kt @@ -1,9 +1,9 @@ -package me.rhunk.snapenhance.manager.sections +package me.rhunk.snapenhance.ui.manager.sections import androidx.compose.foundation.layout.Column import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import me.rhunk.snapenhance.manager.Section +import me.rhunk.snapenhance.ui.manager.Section class NotImplemented : Section() { @Composable diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/features/CallbackAlias.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/features/CallbackAlias.kt similarity index 65% rename from app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/features/CallbackAlias.kt rename to app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/features/CallbackAlias.kt index 9e93b545f..d56e39798 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/features/CallbackAlias.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/features/CallbackAlias.kt @@ -1,4 +1,4 @@ -package me.rhunk.snapenhance.manager.sections.features +package me.rhunk.snapenhance.ui.manager.sections.features typealias ClickCallback = (Boolean) -> Unit typealias RegisterClickCallback = (ClickCallback) -> ClickCallback \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/features/Dialogs.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/features/Dialogs.kt similarity index 99% rename from app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/features/Dialogs.kt rename to app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/features/Dialogs.kt index 336ac8cd9..6958666c2 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/features/Dialogs.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/features/Dialogs.kt @@ -1,4 +1,4 @@ -package me.rhunk.snapenhance.manager.sections.features +package me.rhunk.snapenhance.ui.manager.sections.features import androidx.compose.foundation.ScrollState import androidx.compose.foundation.clickable diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/features/FeaturesSection.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/features/FeaturesSection.kt similarity index 99% rename from app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/features/FeaturesSection.kt rename to app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/features/FeaturesSection.kt index 99fdff4b8..e619dc78d 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/manager/sections/features/FeaturesSection.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/features/FeaturesSection.kt @@ -1,4 +1,4 @@ -package me.rhunk.snapenhance.manager.sections.features +package me.rhunk.snapenhance.ui.manager.sections.features import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -54,7 +54,7 @@ import kotlinx.coroutines.launch import me.rhunk.snapenhance.core.config.ConfigContainer import me.rhunk.snapenhance.core.config.DataProcessors import me.rhunk.snapenhance.core.config.PropertyPair -import me.rhunk.snapenhance.manager.Section +import me.rhunk.snapenhance.ui.manager.Section class FeaturesSection : Section() { private val dialogs by lazy { Dialogs() } diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/util/SaveFolderChecker.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/util/SaveFolderChecker.kt similarity index 97% rename from app/src/main/kotlin/me/rhunk/snapenhance/manager/util/SaveFolderChecker.kt rename to app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/util/SaveFolderChecker.kt index 476012954..952b19975 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/manager/util/SaveFolderChecker.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/util/SaveFolderChecker.kt @@ -1,4 +1,4 @@ -package me.rhunk.snapenhance.manager.util +package me.rhunk.snapenhance.ui.manager.util import android.app.Activity import android.app.AlertDialog diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/setup/Requirements.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/Requirements.kt similarity index 96% rename from app/src/main/kotlin/me/rhunk/snapenhance/manager/setup/Requirements.kt rename to app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/Requirements.kt index a9c9e98c8..00121d12b 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/manager/setup/Requirements.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/Requirements.kt @@ -1,4 +1,4 @@ -package me.rhunk.snapenhance.manager.setup +package me.rhunk.snapenhance.ui.setup import android.os.Bundle diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/setup/SetupActivity.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/SetupActivity.kt similarity index 91% rename from app/src/main/kotlin/me/rhunk/snapenhance/manager/setup/SetupActivity.kt rename to app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/SetupActivity.kt index 6a91b1924..046d13921 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/manager/setup/SetupActivity.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/SetupActivity.kt @@ -1,4 +1,4 @@ -package me.rhunk.snapenhance.manager.setup +package me.rhunk.snapenhance.ui.setup import android.os.Bundle import androidx.activity.ComponentActivity @@ -28,13 +28,13 @@ import androidx.compose.ui.unit.dp import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController -import me.rhunk.snapenhance.manager.AppMaterialTheme -import me.rhunk.snapenhance.manager.setup.screens.SetupScreen -import me.rhunk.snapenhance.manager.setup.screens.impl.FfmpegScreen -import me.rhunk.snapenhance.manager.setup.screens.impl.LanguageScreen -import me.rhunk.snapenhance.manager.setup.screens.impl.MappingsScreen -import me.rhunk.snapenhance.manager.setup.screens.impl.SaveFolderScreen -import me.rhunk.snapenhance.manager.setup.screens.impl.WelcomeScreen +import me.rhunk.snapenhance.ui.AppMaterialTheme +import me.rhunk.snapenhance.ui.setup.screens.SetupScreen +import me.rhunk.snapenhance.ui.setup.screens.impl.FfmpegScreen +import me.rhunk.snapenhance.ui.setup.screens.impl.LanguageScreen +import me.rhunk.snapenhance.ui.setup.screens.impl.MappingsScreen +import me.rhunk.snapenhance.ui.setup.screens.impl.SaveFolderScreen +import me.rhunk.snapenhance.ui.setup.screens.impl.WelcomeScreen class SetupActivity : ComponentActivity() { diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/SetupContext.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/SetupContext.kt new file mode 100644 index 000000000..022761ba5 --- /dev/null +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/SetupContext.kt @@ -0,0 +1,14 @@ +package me.rhunk.snapenhance.ui.setup + +import android.content.Context +import me.rhunk.snapenhance.core.config.ModConfig + +class SetupContext( + private val context: Context +) { + val config = ModConfig() + + init { + config.loadFromContext(context) + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/setup/screens/SetupScreen.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/SetupScreen.kt similarity index 79% rename from app/src/main/kotlin/me/rhunk/snapenhance/manager/setup/screens/SetupScreen.kt rename to app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/SetupScreen.kt index d1600f8de..1acdc3b19 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/manager/setup/screens/SetupScreen.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/SetupScreen.kt @@ -1,4 +1,4 @@ -package me.rhunk.snapenhance.manager.setup.screens +package me.rhunk.snapenhance.ui.setup.screens import androidx.compose.runtime.Composable diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/setup/screens/impl/FfmpegScreen.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/impl/FfmpegScreen.kt similarity index 73% rename from app/src/main/kotlin/me/rhunk/snapenhance/manager/setup/screens/impl/FfmpegScreen.kt rename to app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/impl/FfmpegScreen.kt index 3e9693b65..000a5f4bb 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/manager/setup/screens/impl/FfmpegScreen.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/impl/FfmpegScreen.kt @@ -1,9 +1,9 @@ -package me.rhunk.snapenhance.manager.setup.screens.impl +package me.rhunk.snapenhance.ui.setup.screens.impl import androidx.compose.material3.Button import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import me.rhunk.snapenhance.manager.setup.screens.SetupScreen +import me.rhunk.snapenhance.ui.setup.screens.SetupScreen class FfmpegScreen : SetupScreen() { diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/setup/screens/impl/LanguageScreen.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/impl/LanguageScreen.kt similarity index 53% rename from app/src/main/kotlin/me/rhunk/snapenhance/manager/setup/screens/impl/LanguageScreen.kt rename to app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/impl/LanguageScreen.kt index f574b1156..e5eeaccd4 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/manager/setup/screens/impl/LanguageScreen.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/impl/LanguageScreen.kt @@ -1,10 +1,9 @@ -package me.rhunk.snapenhance.manager.setup.screens.impl +package me.rhunk.snapenhance.ui.setup.screens.impl import androidx.compose.runtime.Composable -import me.rhunk.snapenhance.manager.setup.screens.SetupScreen +import me.rhunk.snapenhance.ui.setup.screens.SetupScreen class LanguageScreen : SetupScreen(){ - @Composable override fun Content() { diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/setup/screens/impl/MappingsScreen.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/impl/MappingsScreen.kt similarity index 74% rename from app/src/main/kotlin/me/rhunk/snapenhance/manager/setup/screens/impl/MappingsScreen.kt rename to app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/impl/MappingsScreen.kt index 1b5d1748a..c49494f23 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/manager/setup/screens/impl/MappingsScreen.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/impl/MappingsScreen.kt @@ -1,9 +1,9 @@ -package me.rhunk.snapenhance.manager.setup.screens.impl +package me.rhunk.snapenhance.ui.setup.screens.impl import androidx.compose.material3.Button import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import me.rhunk.snapenhance.manager.setup.screens.SetupScreen +import me.rhunk.snapenhance.ui.setup.screens.SetupScreen class MappingsScreen : SetupScreen() { @Composable diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/setup/screens/impl/SaveFolderScreen.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/impl/SaveFolderScreen.kt similarity index 74% rename from app/src/main/kotlin/me/rhunk/snapenhance/manager/setup/screens/impl/SaveFolderScreen.kt rename to app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/impl/SaveFolderScreen.kt index 62df1980f..2d2884a3f 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/manager/setup/screens/impl/SaveFolderScreen.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/impl/SaveFolderScreen.kt @@ -1,9 +1,9 @@ -package me.rhunk.snapenhance.manager.setup.screens.impl +package me.rhunk.snapenhance.ui.setup.screens.impl import androidx.compose.material3.Button import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import me.rhunk.snapenhance.manager.setup.screens.SetupScreen +import me.rhunk.snapenhance.ui.setup.screens.SetupScreen class SaveFolderScreen : SetupScreen() { diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/manager/setup/screens/impl/WelcomeScreen.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/impl/WelcomeScreen.kt similarity index 74% rename from app/src/main/kotlin/me/rhunk/snapenhance/manager/setup/screens/impl/WelcomeScreen.kt rename to app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/impl/WelcomeScreen.kt index 64c348efb..40df16473 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/manager/setup/screens/impl/WelcomeScreen.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/impl/WelcomeScreen.kt @@ -1,9 +1,9 @@ -package me.rhunk.snapenhance.manager.setup.screens.impl +package me.rhunk.snapenhance.ui.setup.screens.impl import androidx.compose.material3.Button import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import me.rhunk.snapenhance.manager.setup.screens.SetupScreen +import me.rhunk.snapenhance.ui.setup.screens.SetupScreen class WelcomeScreen : SetupScreen() { diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ModContext.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ModContext.kt index 3b9b1fe76..d4c903341 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/ModContext.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/ModContext.kt @@ -12,7 +12,7 @@ import com.google.gson.Gson import com.google.gson.GsonBuilder import kotlinx.coroutines.asCoroutineDispatcher import me.rhunk.snapenhance.bridge.BridgeClient -import me.rhunk.snapenhance.bridge.wrapper.TranslationWrapper +import me.rhunk.snapenhance.bridge.wrapper.LocaleWrapper import me.rhunk.snapenhance.core.config.ModConfig import me.rhunk.snapenhance.core.eventbus.EventBus import me.rhunk.snapenhance.data.MessageSender @@ -45,7 +45,7 @@ class ModContext { val event = EventBus(this) val eventDispatcher = EventDispatcher(this) - val translation = TranslationWrapper() + val translation = LocaleWrapper() val features = FeatureManager(this) val mappings = MappingManager(this) val actionManager = ActionManager(this) diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/SharedContext.kt b/core/src/main/kotlin/me/rhunk/snapenhance/SharedContext.kt index 77d3a289d..660969f30 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/SharedContext.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/SharedContext.kt @@ -7,7 +7,7 @@ import android.content.Intent import android.os.Build import android.os.Environment import android.provider.Settings -import me.rhunk.snapenhance.bridge.wrapper.TranslationWrapper +import me.rhunk.snapenhance.bridge.wrapper.LocaleWrapper import me.rhunk.snapenhance.download.DownloadTaskManager import kotlin.system.exitProcess @@ -16,7 +16,7 @@ import kotlin.system.exitProcess */ object SharedContext { lateinit var downloadTaskManager: DownloadTaskManager - lateinit var translation: TranslationWrapper + lateinit var translation: LocaleWrapper private fun askForStoragePermission(context: Context) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { @@ -70,7 +70,7 @@ object SharedContext { } } if (!this::translation.isInitialized) { - translation = TranslationWrapper().apply { + translation = LocaleWrapper().apply { loadFromContext(context) } } diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/bridge/BridgeService.kt b/core/src/main/kotlin/me/rhunk/snapenhance/bridge/BridgeService.kt index 1aa1003f8..0a61a4e4a 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/bridge/BridgeService.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/bridge/BridgeService.kt @@ -6,7 +6,7 @@ import android.os.IBinder import me.rhunk.snapenhance.SharedContext import me.rhunk.snapenhance.bridge.types.BridgeFileType import me.rhunk.snapenhance.bridge.wrapper.MessageLoggerWrapper -import me.rhunk.snapenhance.bridge.wrapper.TranslationWrapper +import me.rhunk.snapenhance.bridge.wrapper.LocaleWrapper import me.rhunk.snapenhance.download.DownloadProcessor class BridgeService : Service() { @@ -85,7 +85,7 @@ class BridgeService : Service() { override fun clearMessageLogger() = messageLoggerWrapper.clearMessages() - override fun fetchTranslations() = TranslationWrapper.fetchLocales(context = this@BridgeService).associate { + override fun fetchTranslations() = LocaleWrapper.fetchLocales(context = this@BridgeService).associate { it.locale to it.content } diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/TranslationWrapper.kt b/core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/LocaleWrapper.kt similarity index 92% rename from core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/TranslationWrapper.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/LocaleWrapper.kt index 9bd2cc671..bc7968ddc 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/TranslationWrapper.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/LocaleWrapper.kt @@ -9,9 +9,9 @@ import me.rhunk.snapenhance.data.LocalePair import java.util.Locale -class TranslationWrapper { +class LocaleWrapper { companion object { - private const val DEFAULT_LOCALE = "en_US" + const val DEFAULT_LOCALE = "en_US" fun fetchLocales(context: Context): List { val deviceLocale = Locale.getDefault().toString() @@ -85,10 +85,10 @@ class TranslationWrapper { } } - fun getCategory(key: String): TranslationWrapper { - return TranslationWrapper().apply { + fun getCategory(key: String): LocaleWrapper { + return LocaleWrapper().apply { translationMap.putAll( - this@TranslationWrapper.translationMap + this@LocaleWrapper.translationMap .filterKeys { it.startsWith("$key.") } .mapKeys { it.key.substring(key.length + 1) } ) diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/config/ModConfig.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/ModConfig.kt index 29fd998b8..8d4890a0e 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/core/config/ModConfig.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/ModConfig.kt @@ -8,17 +8,21 @@ import me.rhunk.snapenhance.Logger import me.rhunk.snapenhance.bridge.BridgeClient import me.rhunk.snapenhance.bridge.FileLoaderWrapper import me.rhunk.snapenhance.bridge.types.BridgeFileType +import me.rhunk.snapenhance.bridge.wrapper.LocaleWrapper import me.rhunk.snapenhance.core.config.impl.RootConfig -class ModConfig() { - val root = RootConfig() +class ModConfig { - companion object { - val gson: Gson = GsonBuilder().setPrettyPrinting().create() - } + var locale: String = LocaleWrapper.DEFAULT_LOCALE + set(value) { + field = value + writeConfig() + } + private val gson: Gson = GsonBuilder().setPrettyPrinting().create() private val file = FileLoaderWrapper(BridgeFileType.CONFIG, "{}".toByteArray(Charsets.UTF_8)) + val root = RootConfig() operator fun getValue(thisRef: Any?, property: Any?) = root private fun load() { @@ -36,12 +40,14 @@ class ModConfig() { } private fun loadConfig() { - val configContent = file.read() - root.fromJson(gson.fromJson(configContent.toString(Charsets.UTF_8), JsonObject::class.java)) + val configFileContent = file.read() + val configObject = gson.fromJson(configFileContent.toString(Charsets.UTF_8), JsonObject::class.java) + locale = configObject.get("language")?.asString ?: LocaleWrapper.DEFAULT_LOCALE } fun writeConfig() { val configObject = root.toJson() + configObject.addProperty("language", locale) file.write(configObject.toString().toByteArray(Charsets.UTF_8)) } diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadManagerActivity.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadManagerActivity.kt index be7e1898c..f456e96d4 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadManagerActivity.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadManagerActivity.kt @@ -16,13 +16,13 @@ import android.widget.TextView import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.RecyclerView import me.rhunk.snapenhance.SharedContext -import me.rhunk.snapenhance.bridge.wrapper.TranslationWrapper +import me.rhunk.snapenhance.bridge.wrapper.LocaleWrapper import me.rhunk.snapenhance.core.BuildConfig import me.rhunk.snapenhance.core.R import me.rhunk.snapenhance.download.data.PendingDownload class DownloadManagerActivity : Activity() { - lateinit var translation: TranslationWrapper + lateinit var translation: LocaleWrapper private val backCallbacks = mutableListOf<() -> Unit>() private val fetchedDownloadTasks = mutableListOf() From 641d66b208e67768e16beab0e54a84a332debcb3 Mon Sep 17 00:00:00 2001 From: rhunk <101876869+rhunk@users.noreply.github.com> Date: Thu, 3 Aug 2023 21:48:23 +0200 Subject: [PATCH 17/81] feat(ui): setup activity - remote side context - fix float dialogs - fix choose folder --- app/build.gradle.kts | 2 + app/src/main/AndroidManifest.xml | 3 +- .../me/rhunk/snapenhance/RemoteSideContext.kt | 94 ++++++++++++++ .../rhunk/snapenhance/SharedContextHolder.kt | 15 +++ .../rhunk/snapenhance/bridge/BridgeService.kt | 11 +- .../snapenhance/download/DownloadProcessor.kt | 12 +- .../snapenhance/ui/manager/MainActivity.kt | 36 +++--- .../snapenhance/ui/manager/ManagerContext.kt | 38 ------ .../snapenhance/ui/manager/Navigation.kt | 15 +-- .../rhunk/snapenhance/ui/manager/Section.kt | 5 +- .../ui/manager/sections/HomeSection.kt | 7 +- .../ui/manager/sections/features/Dialogs.kt | 22 ++-- .../sections/features/FeaturesSection.kt | 35 +++++- .../ui/manager/util/SaveFolderChecker.kt | 42 ------- .../snapenhance/ui/setup/Requirements.kt | 42 +++---- .../snapenhance/ui/setup/SetupActivity.kt | 53 +++++--- .../snapenhance/ui/setup/SetupContext.kt | 14 --- .../ui/setup/screens/SetupScreen.kt | 20 +++ .../ui/setup/screens/impl/LanguageScreen.kt | 11 -- .../ui/setup/screens/impl/MappingsScreen.kt | 88 +++++++++++++- .../setup/screens/impl/PickLanguageScreen.kt | 115 ++++++++++++++++++ .../ui/setup/screens/impl/SaveFolderScreen.kt | 36 +++++- .../snapenhance/ui/util/ChooseFolderHelper.kt | 26 ++++ .../ui/util/ObservableMutableState.kt | 19 +++ .../snapenhance/bridge/BridgeInterface.aidl | 6 +- core/src/main/assets/lang/en_US.json | 17 +++ core/src/main/assets/lang/fr_FR.json | 8 ++ .../kotlin/me/rhunk/snapenhance/Logger.kt | 5 + .../kotlin/me/rhunk/snapenhance/ModContext.kt | 4 + .../me/rhunk/snapenhance/SnapEnhance.kt | 10 +- .../rhunk/snapenhance/bridge/BridgeClient.kt | 5 +- .../bridge/wrapper/LocaleWrapper.kt | 38 +++--- .../bridge/wrapper/MappingsWrapper.kt | 5 +- .../core/config/ConfigContainer.kt | 2 +- .../snapenhance/core/config/ModConfig.kt | 13 +- .../download/DownloadManagerClient.kt | 9 +- .../snapenhance/features/FeatureLoadParams.kt | 8 +- .../ui/menu/impl/FriendFeedInfoMenu.kt | 2 +- .../ui/menu/impl/SettingsGearInjector.kt | 3 +- 39 files changed, 648 insertions(+), 248 deletions(-) create mode 100644 app/src/main/kotlin/me/rhunk/snapenhance/RemoteSideContext.kt create mode 100644 app/src/main/kotlin/me/rhunk/snapenhance/SharedContextHolder.kt rename {core => app}/src/main/kotlin/me/rhunk/snapenhance/bridge/BridgeService.kt (87%) rename {core => app}/src/main/kotlin/me/rhunk/snapenhance/download/DownloadProcessor.kt (97%) delete mode 100644 app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/ManagerContext.kt delete mode 100644 app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/util/SaveFolderChecker.kt delete mode 100644 app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/SetupContext.kt delete mode 100644 app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/impl/LanguageScreen.kt create mode 100644 app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/impl/PickLanguageScreen.kt create mode 100644 app/src/main/kotlin/me/rhunk/snapenhance/ui/util/ChooseFolderHelper.kt create mode 100644 app/src/main/kotlin/me/rhunk/snapenhance/ui/util/ObservableMutableState.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index ad029fc34..5ead87a79 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -85,6 +85,8 @@ dependencies { implementation(libs.androidx.material) implementation(libs.androidx.activity.ktx) implementation(libs.androidx.navigation.compose) + implementation(libs.androidx.documentfile) + implementation(libs.gson) debugImplementation("androidx.compose.ui:ui-tooling:1.4.3") debugImplementation("androidx.compose.ui:ui-tooling-preview:1.4.3") diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 151fd6488..e7f622484 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -50,8 +50,7 @@ android:name=".ui.setup.SetupActivity" android:exported="true" android:theme="@style/AppTheme" - android:excludeFromRecents="true"> - + android:excludeFromRecents="true" /> ? = null + var activity: Activity? + get() = _activity?.get() + set(value) { _activity = WeakReference(value) } + + val config = ModConfig() + val translation = LocaleWrapper() + val mappings = MappingsWrapper(androidContext) + + init { + config.loadFromContext(androidContext) + translation.userLocale = config.locale + translation.loadFromContext(androidContext) + mappings.apply { + loadFromContext(androidContext) + init() + } + } + + fun getInstallationSummary() = InstallationSummary( + snapchatInfo = mappings.getSnapchatPackageInfo()?.let { + SnapchatAppInfo( + version = it.versionName, + versionCode = it.longVersionCode + ) + }, + mappingsInfo = if (mappings.isMappingsLoaded()) { + ModMappingsInfo( + generatedSnapchatVersion = mappings.getGeneratedBuildNumber(), + isOutdated = mappings.isMappingsOutdated() + ) + } else null + ) + + fun checkForRequirements(overrideRequirements: Int? = null) { + var requirements = overrideRequirements ?: 0 + + if (!config.wasPresent) { + requirements = requirements or Requirements.FIRST_RUN + } + + config.root.downloader.saveFolder.get().let { + if (it.isEmpty() || run { + val documentFile = runCatching { DocumentFile.fromTreeUri(androidContext, Uri.parse(it)) }.getOrNull() + documentFile == null || !documentFile.exists() || !documentFile.canWrite() + }) { + requirements = requirements or Requirements.SAVE_FOLDER + } + } + + if (mappings.isMappingsOutdated() || !mappings.isMappingsLoaded()) { + requirements = requirements or Requirements.MAPPINGS + } + + if (requirements == 0) return + + val currentContext = activity ?: androidContext + + Intent(currentContext, SetupActivity::class.java).apply { + putExtra("requirements", requirements) + if (currentContext !is Activity) { + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + currentContext.startActivity(this) + return@apply + } + currentContext.startActivityForResult(this, 22) + } + + if (currentContext !is Activity) { + exitProcess(0) + } + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/SharedContextHolder.kt b/app/src/main/kotlin/me/rhunk/snapenhance/SharedContextHolder.kt new file mode 100644 index 000000000..918ec1b53 --- /dev/null +++ b/app/src/main/kotlin/me/rhunk/snapenhance/SharedContextHolder.kt @@ -0,0 +1,15 @@ +package me.rhunk.snapenhance + +import android.content.Context +import java.lang.ref.WeakReference + +object SharedContextHolder { + private lateinit var _remoteSideContext: WeakReference + + fun remote(context: Context): RemoteSideContext { + if (!::_remoteSideContext.isInitialized || _remoteSideContext.get() == null) { + _remoteSideContext = WeakReference(RemoteSideContext(context.applicationContext)) + } + return _remoteSideContext.get()!! + } +} \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/bridge/BridgeService.kt b/app/src/main/kotlin/me/rhunk/snapenhance/bridge/BridgeService.kt similarity index 87% rename from core/src/main/kotlin/me/rhunk/snapenhance/bridge/BridgeService.kt rename to app/src/main/kotlin/me/rhunk/snapenhance/bridge/BridgeService.kt index 0a61a4e4a..196ec6d9b 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/bridge/BridgeService.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/bridge/BridgeService.kt @@ -3,7 +3,9 @@ package me.rhunk.snapenhance.bridge import android.app.Service import android.content.Intent import android.os.IBinder +import me.rhunk.snapenhance.RemoteSideContext import me.rhunk.snapenhance.SharedContext +import me.rhunk.snapenhance.SharedContextHolder import me.rhunk.snapenhance.bridge.types.BridgeFileType import me.rhunk.snapenhance.bridge.wrapper.MessageLoggerWrapper import me.rhunk.snapenhance.bridge.wrapper.LocaleWrapper @@ -11,7 +13,12 @@ import me.rhunk.snapenhance.download.DownloadProcessor class BridgeService : Service() { private lateinit var messageLoggerWrapper: MessageLoggerWrapper + private lateinit var remoteSideContext: RemoteSideContext + override fun onBind(intent: Intent): IBinder { + remoteSideContext = SharedContextHolder.remote(this).apply { + checkForRequirements() + } messageLoggerWrapper = MessageLoggerWrapper(getDatabasePath(BridgeFileType.MESSAGE_LOGGER_DATABASE.fileName)).also { it.init() } return BridgeBinder() } @@ -85,7 +92,7 @@ class BridgeService : Service() { override fun clearMessageLogger() = messageLoggerWrapper.clearMessages() - override fun fetchTranslations() = LocaleWrapper.fetchLocales(context = this@BridgeService).associate { + override fun fetchLocales(userLocale: String) = LocaleWrapper.fetchLocales(context = this@BridgeService, userLocale).associate { it.locale to it.content } @@ -98,6 +105,8 @@ class BridgeService : Service() { } override fun enqueueDownload(intent: Intent, callback: DownloadCallback) { + SharedContextHolder.remote(this@BridgeService) + //TODO: refactor shared context SharedContext.ensureInitialized(this@BridgeService) DownloadProcessor(this@BridgeService, callback).onReceive(intent) } diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/download/DownloadProcessor.kt b/app/src/main/kotlin/me/rhunk/snapenhance/download/DownloadProcessor.kt similarity index 97% rename from core/src/main/kotlin/me/rhunk/snapenhance/download/DownloadProcessor.kt rename to app/src/main/kotlin/me/rhunk/snapenhance/download/DownloadProcessor.kt index 9fc7038f0..a9376e8c5 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/download/DownloadProcessor.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/download/DownloadProcessor.kt @@ -59,10 +59,6 @@ class DownloadProcessor ( private val context: Context, private val callback: DownloadCallback ) { - companion object { - const val DOWNLOAD_REQUEST_EXTRA = "request" - const val DOWNLOAD_METADATA_EXTRA = "metadata" - } private val translation by lazy { SharedContext.translation.getCategory("download_processor") @@ -184,7 +180,9 @@ class DownloadProcessor ( fun handleInputStream(inputStream: InputStream) { createMediaTempFile().apply { if (inputMedia.encryption != null) { - decryptInputStream(inputStream, inputMedia.encryption).use { decryptedInputStream -> + decryptInputStream(inputStream, + inputMedia.encryption!! + ).use { decryptedInputStream -> decryptedInputStream.copyTo(outputStream()) } } else { @@ -286,8 +284,8 @@ class DownloadProcessor ( fun onReceive(intent: Intent) { CoroutineScope(Dispatchers.IO).launch { - val downloadMetadata = gson.fromJson(intent.getStringExtra(DOWNLOAD_METADATA_EXTRA)!!, DownloadMetadata::class.java) - val downloadRequest = gson.fromJson(intent.getStringExtra(DOWNLOAD_REQUEST_EXTRA)!!, DownloadRequest::class.java) + val downloadMetadata = gson.fromJson(intent.getStringExtra(DownloadManagerClient.DOWNLOAD_METADATA_EXTRA)!!, DownloadMetadata::class.java) + val downloadRequest = gson.fromJson(intent.getStringExtra(DownloadManagerClient.DOWNLOAD_REQUEST_EXTRA)!!, DownloadRequest::class.java) SharedContext.downloadTaskManager.canDownloadMedia(downloadMetadata.mediaIdentifier)?.let { downloadStage -> translation[if (downloadStage.isFinalStage) { diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/MainActivity.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/MainActivity.kt index 7befc61ac..51c9ed3c3 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/MainActivity.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/MainActivity.kt @@ -1,43 +1,49 @@ package me.rhunk.snapenhance.ui.manager -import android.annotation.SuppressLint import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold +import androidx.compose.runtime.remember import androidx.navigation.compose.rememberNavController +import me.rhunk.snapenhance.SharedContextHolder import me.rhunk.snapenhance.ui.AppMaterialTheme -import me.rhunk.snapenhance.ui.manager.util.SaveFolderChecker -import me.rhunk.snapenhance.util.ActivityResultCallback class MainActivity : ComponentActivity() { - private val activityResultCallbacks = mutableMapOf() - - @SuppressLint("UnusedMaterialScaffoldPaddingParameter") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val startDestination = intent.getStringExtra("route")?.let { EnumSection.fromRoute(it) } ?: EnumSection.HOME - val managerContext = ManagerContext(this) + val managerContext = SharedContextHolder.remote(this).apply { + activity = this@MainActivity + checkForRequirements() + } - //FIXME: temporary save folder - SaveFolderChecker.askForFolder( - this, - managerContext.config.root.downloader.saveFolder) - { - managerContext.config.writeConfig() + val sections = EnumSection.values().toList().associateWith { + it.section.constructors.first().call() + }.onEach { (section, instance) -> + with(instance) { + enumSection = section + context = managerContext + init() + } } setContent { val navController = rememberNavController() - val navigation = Navigation(managerContext) + val navigation = remember { Navigation() } AppMaterialTheme { Scaffold( containerColor = MaterialTheme.colorScheme.background, bottomBar = { navigation.NavBar(navController = navController) } ) { innerPadding -> - navigation.NavigationHost(navController = navController, innerPadding = innerPadding, startDestination = startDestination) + navigation.NavigationHost( + sections = sections, + navController = navController, + innerPadding = innerPadding, + startDestination = startDestination + ) } } } diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/ManagerContext.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/ManagerContext.kt deleted file mode 100644 index 42166d264..000000000 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/ManagerContext.kt +++ /dev/null @@ -1,38 +0,0 @@ -package me.rhunk.snapenhance.ui.manager - -import android.content.Context -import me.rhunk.snapenhance.bridge.wrapper.MappingsWrapper -import me.rhunk.snapenhance.bridge.wrapper.LocaleWrapper -import me.rhunk.snapenhance.core.config.ModConfig -import me.rhunk.snapenhance.ui.manager.data.InstallationSummary -import me.rhunk.snapenhance.ui.manager.data.ModMappingsInfo -import me.rhunk.snapenhance.ui.manager.data.SnapchatAppInfo - -class ManagerContext( - private val context: Context -) { - val config = ModConfig() - val translation = LocaleWrapper() - val mappings = MappingsWrapper(context) - - init { - config.loadFromContext(context) - translation.loadFromContext(context) - mappings.apply { loadFromContext(context) }.init() - } - - fun getInstallationSummary() = InstallationSummary( - snapchatInfo = mappings.getSnapchatPackageInfo()?.let { - SnapchatAppInfo( - version = it.versionName, - versionCode = it.longVersionCode - ) - }, - mappingsInfo = if (mappings.isMappingsLoaded()) { - ModMappingsInfo( - generatedSnapchatVersion = mappings.getGeneratedBuildNumber(), - isOutdated = mappings.isMappingsOutdated() - ) - } else null - ) -} \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/Navigation.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/Navigation.kt index e6cbdce47..faf8d88d1 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/Navigation.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/Navigation.kt @@ -12,7 +12,6 @@ import androidx.compose.material3.NavigationBarItem import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue -import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.navigation.NavController @@ -23,25 +22,17 @@ import androidx.navigation.compose.NavHost import androidx.navigation.compose.currentBackStackEntryAsState -class Navigation( - private val context: ManagerContext -) { +class Navigation{ @Composable fun NavigationHost( + sections: Map, startDestination: EnumSection, navController: NavHostController, innerPadding: PaddingValues ) { - val sections = remember { EnumSection.values().toList().map { - it to it.section.constructors.first().call() - }.onEach { (section, instance) -> - instance.enumSection = section - instance.manager = context - instance.navController = navController - } } - NavHost(navController, startDestination = startDestination.route, Modifier.padding(innerPadding)) { sections.forEach { (_, instance) -> + instance.navController = navController instance.build(this) } } diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/Section.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/Section.kt index 064e51dd1..ecef8d7f8 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/Section.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/Section.kt @@ -11,6 +11,7 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable +import me.rhunk.snapenhance.RemoteSideContext import me.rhunk.snapenhance.ui.manager.sections.HomeSection import me.rhunk.snapenhance.ui.manager.sections.NotImplemented import me.rhunk.snapenhance.ui.manager.sections.features.FeaturesSection @@ -61,9 +62,11 @@ enum class EnumSection( open class Section { lateinit var enumSection: EnumSection - lateinit var manager: ManagerContext + lateinit var context: RemoteSideContext lateinit var navController: NavController + open fun init() {} + @Composable open fun Content() { NotImplemented() } diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/HomeSection.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/HomeSection.kt index e493a30c6..34350152e 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/HomeSection.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/HomeSection.kt @@ -25,6 +25,7 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import me.rhunk.snapenhance.ui.manager.Section import me.rhunk.snapenhance.ui.manager.data.InstallationSummary +import me.rhunk.snapenhance.ui.setup.Requirements class HomeSection : Section() { companion object { @@ -76,7 +77,9 @@ class HomeSection : Section() { ) //inline button - Button(onClick = {}, modifier = Modifier.height(40.dp)) { + Button(onClick = { + context.checkForRequirements(Requirements.MAPPINGS) + }, modifier = Modifier.height(40.dp)) { Icon(Icons.Filled.Refresh, contentDescription = "Refresh") } } @@ -102,7 +105,7 @@ class HomeSection : Section() { modifier = Modifier.padding(16.dp) ) - SummaryCards(manager.getInstallationSummary()) + SummaryCards(context.getInstallationSummary()) } } } \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/features/Dialogs.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/features/Dialogs.kt index 6958666c2..c18a968e1 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/features/Dialogs.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/features/Dialogs.kt @@ -140,14 +140,22 @@ class Dialogs { Text(text = "Cancel") } Button(onClick = { - if (property.key.dataType.type == DataProcessors.Type.INTEGER) { - runCatching { - property.value.setAny(fieldValue.value.text.toInt()) - }.onFailure { - property.value.setAny(0) + when (property.key.dataType.type) { + DataProcessors.Type.INTEGER -> { + runCatching { + property.value.setAny(fieldValue.value.text.toInt()) + }.onFailure { + property.value.setAny(0) + } } - } else { - property.value.setAny(fieldValue.value.text) + DataProcessors.Type.FLOAT -> { + runCatching { + property.value.setAny(fieldValue.value.text.toFloat()) + }.onFailure { + property.value.setAny(0f) + } + } + else -> property.value.setAny(fieldValue.value.text) } dismiss() }) { diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/features/FeaturesSection.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/features/FeaturesSection.kt index e619dc78d..9f5003050 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/features/FeaturesSection.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/features/FeaturesSection.kt @@ -1,5 +1,6 @@ package me.rhunk.snapenhance.ui.manager.sections.features +import androidx.activity.ComponentActivity import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement @@ -20,6 +21,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.MaterialTheme import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material.icons.filled.FolderOpen import androidx.compose.material.icons.filled.OpenInNew import androidx.compose.material.icons.rounded.Save import androidx.compose.material3.Card @@ -55,6 +57,7 @@ import me.rhunk.snapenhance.core.config.ConfigContainer import me.rhunk.snapenhance.core.config.DataProcessors import me.rhunk.snapenhance.core.config.PropertyPair import me.rhunk.snapenhance.ui.manager.Section +import me.rhunk.snapenhance.ui.util.ChooseFolderHelper class FeaturesSection : Section() { private val dialogs by lazy { Dialogs() } @@ -63,6 +66,15 @@ class FeaturesSection : Section() { private const val MAIN_ROUTE = "root" } + private lateinit var openFolderCallback: (uri: String) -> Unit + private lateinit var openFolderLauncher: () -> Unit + + override fun init() { + openFolderLauncher = ChooseFolderHelper.createChooseFolder(context.activity!! as ComponentActivity) { + openFolderCallback(it) + } + } + @Composable private fun PropertyAction(property: PropertyPair<*>, registerClickCallback: RegisterClickCallback) { val showDialog = remember { mutableStateOf(false) } @@ -83,6 +95,18 @@ class FeaturesSection : Section() { val propertyValue = property.value + if (property.key.params.isFolder) { + IconButton(onClick = registerClickCallback { + openFolderCallback = { uri -> + propertyValue.setAny(uri) + } + openFolderLauncher() + }.let { { it.invoke(true) } }) { + Icon(Icons.Filled.FolderOpen, contentDescription = null) + } + return + } + when (val dataType = remember { property.key.dataType.type }) { DataProcessors.Type.BOOLEAN -> { val state = remember { mutableStateOf(propertyValue.get() as Boolean) } @@ -116,7 +140,7 @@ class FeaturesSection : Section() { DataProcessors.Type.STRING_MULTIPLE_SELECTION -> { dialogs.MultipleSelectionDialog(property) } - DataProcessors.Type.STRING, DataProcessors.Type.INTEGER -> { + DataProcessors.Type.STRING, DataProcessors.Type.INTEGER, DataProcessors.Type.FLOAT -> { dialogs.KeyboardInputDialog(property) { showDialog.value = false } } else -> {} @@ -124,7 +148,8 @@ class FeaturesSection : Section() { } registerDialogOnClickCallback().let { { it.invoke(true) } }.also { - if (dataType == DataProcessors.Type.INTEGER || dataType == DataProcessors.Type.FLOAT) { + if (dataType == DataProcessors.Type.INTEGER || + dataType == DataProcessors.Type.FLOAT) { FilledIconButton(onClick = it) { Text( text = propertyValue.get().toString(), @@ -256,7 +281,7 @@ class FeaturesSection : Section() { floatingActionButton = { FloatingActionButton( onClick = { - manager.config.writeConfig() + context.config.writeConfig() scope.launch { scaffoldState.snackbarHostState.showSnackbar("Saved") } @@ -299,13 +324,13 @@ class FeaturesSection : Section() { } } } - queryContainerRecursive(manager.config.root) + queryContainerRecursive(context.config.root) containers } navGraphBuilder.navigation(route = "features", startDestination = MAIN_ROUTE) { composable(MAIN_ROUTE) { - Container(MAIN_ROUTE, manager.config.root) + Container(MAIN_ROUTE, context.config.root) } composable("container/{name}") { backStackEntry -> diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/util/SaveFolderChecker.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/util/SaveFolderChecker.kt deleted file mode 100644 index 952b19975..000000000 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/util/SaveFolderChecker.kt +++ /dev/null @@ -1,42 +0,0 @@ -package me.rhunk.snapenhance.ui.manager.util - -import android.app.Activity -import android.app.AlertDialog -import android.content.Intent -import android.widget.Toast -import androidx.activity.ComponentActivity -import androidx.activity.result.contract.ActivityResultContracts -import me.rhunk.snapenhance.core.config.PropertyValue -import kotlin.system.exitProcess - -object SaveFolderChecker { - fun askForFolder(activity: ComponentActivity, property: PropertyValue, saveConfig: () -> Unit) { - if (property.get().isEmpty() || !property.get().startsWith("content://")) { - val startActivity = activity.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) result@{ - if (it.resultCode != Activity.RESULT_OK) return@result - val uri = it.data?.data ?: return@result - val value = uri.toString() - activity.contentResolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION) - property.set(value) - saveConfig() - Toast.makeText(activity, "save folder set!", Toast.LENGTH_SHORT).show() - activity.finish() - } - - AlertDialog.Builder(activity) - .setTitle("Save folder") - .setMessage("Please select a folder where you want to save downloaded files.") - .setPositiveButton("Select") { _, _ -> - startActivity.launch( - Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) - .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - .addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION) - ) - } - .setNegativeButton("Cancel") { _, _ -> - exitProcess(0) - } - .show() - } - } -} \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/Requirements.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/Requirements.kt index 00121d12b..8c7ca51ac 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/Requirements.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/Requirements.kt @@ -1,33 +1,21 @@ package me.rhunk.snapenhance.ui.setup -import android.os.Bundle +object Requirements { + const val FIRST_RUN = 0b00001 + const val LANGUAGE = 0b00010 + const val MAPPINGS = 0b00100 + const val SAVE_FOLDER = 0b01000 + const val FFMPEG = 0b10000 -data class Requirements( - val firstRun: Boolean = false, - val language: Boolean = false, - val mappings: Boolean = false, - val saveFolder: Boolean = false, - val ffmpeg: Boolean = false -) { - companion object { - fun fromBundle(bundle: Bundle): Requirements { - return Requirements( - firstRun = bundle.getBoolean("firstRun"), - language = bundle.getBoolean("language"), - mappings = bundle.getBoolean("mappings"), - saveFolder = bundle.getBoolean("saveFolder"), - ffmpeg = bundle.getBoolean("ffmpeg") - ) - } - - fun toBundle(requirements: Requirements): Bundle { - return Bundle().apply { - putBoolean("firstRun", requirements.firstRun) - putBoolean("language", requirements.language) - putBoolean("mappings", requirements.mappings) - putBoolean("saveFolder", requirements.saveFolder) - putBoolean("ffmpeg", requirements.ffmpeg) - } + fun getName(requirement: Int): String { + return when (requirement) { + FIRST_RUN -> "FIRST_RUN" + LANGUAGE -> "LANGUAGE" + MAPPINGS -> "MAPPINGS" + SAVE_FOLDER -> "SAVE_FOLDER" + FFMPEG -> "FFMPEG" + else -> "UNKNOWN" } } } + diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/SetupActivity.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/SetupActivity.kt index 046d13921..a05bcdf7b 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/SetupActivity.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/SetupActivity.kt @@ -1,7 +1,9 @@ package me.rhunk.snapenhance.ui.setup +import android.annotation.SuppressLint import android.os.Bundle import androidx.activity.ComponentActivity +import androidx.activity.compose.BackHandler import androidx.activity.compose.setContent import androidx.compose.animation.core.animateFloatAsState import androidx.compose.foundation.background @@ -14,6 +16,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowForwardIos +import androidx.compose.material.icons.filled.Check import androidx.compose.material3.FilledIconButton import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme @@ -28,48 +31,64 @@ import androidx.compose.ui.unit.dp import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController +import me.rhunk.snapenhance.SharedContextHolder import me.rhunk.snapenhance.ui.AppMaterialTheme import me.rhunk.snapenhance.ui.setup.screens.SetupScreen import me.rhunk.snapenhance.ui.setup.screens.impl.FfmpegScreen -import me.rhunk.snapenhance.ui.setup.screens.impl.LanguageScreen import me.rhunk.snapenhance.ui.setup.screens.impl.MappingsScreen +import me.rhunk.snapenhance.ui.setup.screens.impl.PickLanguageScreen import me.rhunk.snapenhance.ui.setup.screens.impl.SaveFolderScreen -import me.rhunk.snapenhance.ui.setup.screens.impl.WelcomeScreen class SetupActivity : ComponentActivity() { + @SuppressLint("UnusedMaterial3ScaffoldPaddingParameter") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - val requirements = intent.getBundleExtra("requirements")?.let { - Requirements.fromBundle(it) - } ?: Requirements(firstRun = true) + val setupContext = SharedContextHolder.remote(this).apply { + activity = this@SetupActivity + } + val requirements = intent.getIntExtra("requirements", Requirements.FIRST_RUN) + + fun hasRequirement(requirement: Int) = requirements and requirement == requirement val requiredScreens = mutableListOf() with(requiredScreens) { - with(requirements) { - if (firstRun || language) add(LanguageScreen().apply { route = "language" }) - if (firstRun) add(WelcomeScreen().apply { route = "welcome" }) - if (firstRun || saveFolder) add(SaveFolderScreen().apply { route = "saveFolder" }) - if (firstRun || mappings) add(MappingsScreen().apply { route = "mappings" }) - if (firstRun || ffmpeg) add(FfmpegScreen().apply { route = "ffmpeg" }) + val isFirstRun = hasRequirement(Requirements.FIRST_RUN) + if (isFirstRun || hasRequirement(Requirements.LANGUAGE)) { + add(PickLanguageScreen().apply { route = "language" }) + } + if (isFirstRun || hasRequirement(Requirements.SAVE_FOLDER)) { + add(SaveFolderScreen().apply { route = "saveFolder" }) + } + if (isFirstRun || hasRequirement(Requirements.MAPPINGS)) { + add(MappingsScreen().apply { route = "mappings" }) + } + if (isFirstRun || hasRequirement(Requirements.FFMPEG)) { + add(FfmpegScreen().apply { route = "ffmpeg" }) } } + // If there are no required screens, we can just finish the activity if (requiredScreens.isEmpty()) { finish() return } + requiredScreens.forEach { screen -> + screen.context = setupContext + screen.init() + } + setContent { val navController = rememberNavController() val canGoNext = remember { mutableStateOf(false) } fun nextScreen() { if (!canGoNext.value) return - canGoNext.value = false if (requiredScreens.size > 1) { + canGoNext.value = false requiredScreens.removeFirst() navController.navigate(requiredScreens.first().route) } else { @@ -98,18 +117,21 @@ class SetupActivity : ComponentActivity() { .alpha(alpha) ) { Icon( - imageVector = Icons.Default.ArrowForwardIos, + imageVector = if (requiredScreens.size <= 1 && canGoNext.value) { + Icons.Default.Check + } else { + Icons.Default.ArrowForwardIos + }, contentDescription = null ) } } }, - ) { paddingValues -> + ) { Column( modifier = Modifier .background(MaterialTheme.colorScheme.background) .fillMaxSize() - .padding(paddingValues) ) { NavHost( navController = navController, @@ -118,6 +140,7 @@ class SetupActivity : ComponentActivity() { requiredScreens.forEach { screen -> screen.allowNext = { canGoNext.value = it } composable(screen.route) { + BackHandler(true) {} Column( modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.Center, diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/SetupContext.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/SetupContext.kt deleted file mode 100644 index 022761ba5..000000000 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/SetupContext.kt +++ /dev/null @@ -1,14 +0,0 @@ -package me.rhunk.snapenhance.ui.setup - -import android.content.Context -import me.rhunk.snapenhance.core.config.ModConfig - -class SetupContext( - private val context: Context -) { - val config = ModConfig() - - init { - config.loadFromContext(context) - } -} \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/SetupScreen.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/SetupScreen.kt index 1acdc3b19..5b0375a4d 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/SetupScreen.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/SetupScreen.kt @@ -1,11 +1,31 @@ package me.rhunk.snapenhance.ui.setup.screens +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import me.rhunk.snapenhance.RemoteSideContext abstract class SetupScreen { + lateinit var context: RemoteSideContext lateinit var allowNext: (Boolean) -> Unit lateinit var route: String + @Composable + fun DialogText(text: String, modifier: Modifier = Modifier) { + Text( + text = text, + fontSize = 16.sp, + fontWeight = FontWeight.Normal, + modifier = Modifier.padding(16.dp).then(modifier) + ) + } + + open fun init() {} + @Composable abstract fun Content() } \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/impl/LanguageScreen.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/impl/LanguageScreen.kt deleted file mode 100644 index e5eeaccd4..000000000 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/impl/LanguageScreen.kt +++ /dev/null @@ -1,11 +0,0 @@ -package me.rhunk.snapenhance.ui.setup.screens.impl - -import androidx.compose.runtime.Composable -import me.rhunk.snapenhance.ui.setup.screens.SetupScreen - -class LanguageScreen : SetupScreen(){ - @Composable - override fun Content() { - - } -} \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/impl/MappingsScreen.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/impl/MappingsScreen.kt index c49494f23..b7e4cddcb 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/impl/MappingsScreen.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/impl/MappingsScreen.kt @@ -1,16 +1,98 @@ package me.rhunk.snapenhance.ui.setup.screens.impl +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.MaterialTheme import androidx.compose.material3.Button +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Dialog +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import me.rhunk.snapenhance.Logger import me.rhunk.snapenhance.ui.setup.screens.SetupScreen class MappingsScreen : SetupScreen() { @Composable override fun Content() { - Text(text = "Mappings") - Button(onClick = { allowNext(true) }) { - Text(text = "Next") + val coroutineScope = rememberCoroutineScope() + val infoText = remember { mutableStateOf(null as String?) } + val isGenerating = remember { mutableStateOf(false) } + + if (infoText.value != null) { + Dialog(onDismissRequest = { + infoText.value = null + }) { + Surface( + modifier = Modifier.padding(16.dp).fillMaxWidth(), + color = MaterialTheme.colors.surface, + shape = RoundedCornerShape(16.dp), + ) { + Column( + modifier = Modifier.padding(16.dp) + ) { + Text(text = infoText.value!!) + Button(onClick = { + infoText.value = null + }, + modifier = Modifier.padding(top = 5.dp).align(alignment = androidx.compose.ui.Alignment.End)) { + Text(text = "OK") + } + } + } + } + } + + fun tryToGenerateMappings() { + //check for snapchat installation + val installationSummary = context.getInstallationSummary() + if (installationSummary.snapchatInfo == null) { + throw Exception(context.translation["setup.mappings.generate_failure_no_snapchat"]) + } + with(context.mappings) { + refresh() + } + } + + val hasMappings = remember { mutableStateOf(false) } + + DialogText(text = context.translation["setup.mappings.dialog"]) + if (hasMappings.value) return + Button(onClick = { + if (isGenerating.value) return@Button + isGenerating.value = true + coroutineScope.launch(Dispatchers.IO) { + runCatching { + tryToGenerateMappings() + allowNext(true) + infoText.value = context.translation["setup.mappings.generate_success"] + hasMappings.value = true + }.onFailure { + isGenerating.value = false + infoText.value = context.translation["setup.mappings.generate_failure"] + "\n\n" + it.message + Logger.error("Failed to generate mappings", it) + } + } + }) { + if (isGenerating.value) { + CircularProgressIndicator( + modifier = Modifier.padding(end = 5.dp).size(25.dp), + strokeWidth = 2.dp, + color = MaterialTheme.colors.onPrimary + ) + } else { + Text(text = context.translation["setup.mappings.generate_button"]) + } } } } \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/impl/PickLanguageScreen.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/impl/PickLanguageScreen.kt new file mode 100644 index 000000000..dc5591d17 --- /dev/null +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/impl/PickLanguageScreen.kt @@ -0,0 +1,115 @@ +package me.rhunk.snapenhance.ui.setup.screens.impl + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.gestures.Orientation +import androidx.compose.foundation.gestures.scrollable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.rememberScrollState +import androidx.compose.material.Surface +import androidx.compose.material.Text +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedButton +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.compose.ui.window.Dialog +import me.rhunk.snapenhance.bridge.wrapper.LocaleWrapper +import me.rhunk.snapenhance.ui.util.ObservableMutableState +import me.rhunk.snapenhance.ui.setup.screens.SetupScreen +import java.util.Locale + + +class PickLanguageScreen : SetupScreen(){ + @Composable + override fun Content() { + val androidContext = LocalContext.current + val availableLocales = remember { LocaleWrapper.fetchAvailableLocales(androidContext) } + + allowNext(true) + + fun getLocaleDisplayName(locale: String): String { + locale.split("_").let { + return java.util.Locale(it[0], it[1]).getDisplayName(java.util.Locale.getDefault()) + } + } + + val selectedLocale = remember { + val deviceLocale = Locale.getDefault().toString() + fun reloadTranslation(selectedLocale: String) { + context.translation.reloadFromContext(androidContext, selectedLocale) + } + ObservableMutableState( + defaultValue = availableLocales.firstOrNull { + locale -> locale == deviceLocale + } ?: LocaleWrapper.DEFAULT_LOCALE + ) { _, newValue -> + context.config.locale = newValue + context.config.writeConfig() + reloadTranslation(newValue) + }.also { reloadTranslation(it.value) } + } + + DialogText(text = context.translation["setup.dialogs.select_language"]) + + val isDialog = remember { mutableStateOf(false) } + + if (isDialog.value) { + Dialog(onDismissRequest = { isDialog.value = false }) { + Surface( + modifier = Modifier + .padding(10.dp) + .fillMaxWidth(), + elevation = 8.dp, + shape = MaterialTheme.shapes.medium + ) { + LazyColumn( + modifier = Modifier.scrollable(rememberScrollState(), orientation = Orientation.Vertical) + ) { + items(availableLocales) { locale -> + Box( + modifier = Modifier + .height(70.dp) + .fillMaxWidth() + .clickable { + selectedLocale.value = locale + isDialog.value = false + }, + contentAlignment = Alignment.Center + ) { + Text( + text = getLocaleDisplayName(locale), + fontSize = 16.sp, + fontWeight = FontWeight.Light, + ) + } + } + } + } + } + } + + Box( + modifier = Modifier + .padding(top = 40.dp) + .fillMaxWidth(), + contentAlignment = Alignment.Center + ) { + OutlinedButton(onClick = { + isDialog.value = true + }) { + Text(text = getLocaleDisplayName(selectedLocale.value), fontSize = 16.sp, fontWeight = FontWeight.Light) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/impl/SaveFolderScreen.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/impl/SaveFolderScreen.kt index 2d2884a3f..aded8b869 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/impl/SaveFolderScreen.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/impl/SaveFolderScreen.kt @@ -1,17 +1,47 @@ package me.rhunk.snapenhance.ui.setup.screens.impl +import androidx.activity.ComponentActivity +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height import androidx.compose.material3.Button import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import me.rhunk.snapenhance.Logger +import me.rhunk.snapenhance.ui.util.ObservableMutableState import me.rhunk.snapenhance.ui.setup.screens.SetupScreen +import me.rhunk.snapenhance.ui.util.ChooseFolderHelper class SaveFolderScreen : SetupScreen() { + private lateinit var saveFolder: ObservableMutableState + private lateinit var openFolderLauncher: () -> Unit + + override fun init() { + saveFolder = ObservableMutableState( + defaultValue = "", + onChange = { _, newValue -> + Logger.debug(newValue) + if (newValue.isNotBlank()) { + context.config.root.downloader.saveFolder.set(newValue) + context.config.writeConfig() + allowNext(true) + } + } + ) + openFolderLauncher = ChooseFolderHelper.createChooseFolder(context.activity as ComponentActivity) { uri -> + saveFolder.value = uri + } + } @Composable override fun Content() { - Text(text = "SaveFolder") - Button(onClick = {allowNext(true)}) { - Text(text = "Next") + DialogText(text = context.translation["setup.dialogs.save_folder"]) + Spacer(modifier = Modifier.height(16.dp)) + Button(onClick = { + openFolderLauncher() + }) { + Text(text = context.translation["setup.dialogs.select_save_folder_button"]) } } } \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/util/ChooseFolderHelper.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/util/ChooseFolderHelper.kt new file mode 100644 index 000000000..179591c9f --- /dev/null +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/util/ChooseFolderHelper.kt @@ -0,0 +1,26 @@ +package me.rhunk.snapenhance.ui.util + +import android.app.Activity +import android.content.Intent +import androidx.activity.ComponentActivity +import androidx.activity.result.contract.ActivityResultContracts + +object ChooseFolderHelper { + fun createChooseFolder(activity: ComponentActivity, callback: (uri: String) -> Unit): () -> Unit { + val activityResultLauncher = activity.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) result@{ + if (it.resultCode != Activity.RESULT_OK) return@result + val uri = it.data?.data ?: return@result + val value = uri.toString() + activity.contentResolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION) + callback(value) + } + + return { + activityResultLauncher.launch( + Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) + .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + .addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION) + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/util/ObservableMutableState.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/util/ObservableMutableState.kt new file mode 100644 index 000000000..6dc6af45c --- /dev/null +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/util/ObservableMutableState.kt @@ -0,0 +1,19 @@ +package me.rhunk.snapenhance.ui.util + +import androidx.compose.runtime.MutableState + +class ObservableMutableState( + defaultValue: T, + inline val onChange: (T, T) -> Unit = { _, _ -> }, +) : MutableState { + private var mutableValue: T = defaultValue + override var value: T + get() = mutableValue + set(value) { + val oldValue = mutableValue + mutableValue = value + onChange(oldValue, value) + } + override fun component1() = value + override fun component2(): (T) -> Unit = { value = it } +} \ No newline at end of file diff --git a/core/src/main/aidl/me/rhunk/snapenhance/bridge/BridgeInterface.aidl b/core/src/main/aidl/me/rhunk/snapenhance/bridge/BridgeInterface.aidl index 3e015a6a6..846de0d23 100644 --- a/core/src/main/aidl/me/rhunk/snapenhance/bridge/BridgeInterface.aidl +++ b/core/src/main/aidl/me/rhunk/snapenhance/bridge/BridgeInterface.aidl @@ -83,11 +83,11 @@ interface BridgeInterface { void clearMessageLogger(); /** - * Fetch the translations + * Fetch the locales * - * @return the translations result + * @return the locale result */ - Map fetchTranslations(); + Map fetchLocales(String userLocale); /** * Get check for updates last time diff --git a/core/src/main/assets/lang/en_US.json b/core/src/main/assets/lang/en_US.json index b3010e561..c4d7aa762 100644 --- a/core/src/main/assets/lang/en_US.json +++ b/core/src/main/assets/lang/en_US.json @@ -1,4 +1,21 @@ { + "setup": { + "dialogs": { + "select_language": "Select Language", + "save_folder": "For downloading snapchat media, you'll need to choose a save location. This can be changed later in the application settings.", + "select_save_folder_button": "Select Save Folder", + "mappings": "To support a wide range of versions, mappings need to be generated for the current snapchat version." + }, + "mappings": { + "dialog": "To support a wide range of versions, mappings need to be generated for the current snapchat version.", + "snapchat_not_found": "Snapchat could not be found on your device. Please install Snapchat and try again.", + "snapchat_not_supported": "Snapchat is not supported. Please update Snapchat and try again.", + "generate_button": "Generate", + "generate_error": "An error occurred while generating mappings. Please try again.", + "generate_success": "Mappings generated successfully." + } + }, + "category": { "spying_privacy": "Spying & Privacy", "media_manager": "Media Manager", diff --git a/core/src/main/assets/lang/fr_FR.json b/core/src/main/assets/lang/fr_FR.json index e81f513d9..a68882ebf 100644 --- a/core/src/main/assets/lang/fr_FR.json +++ b/core/src/main/assets/lang/fr_FR.json @@ -1,4 +1,12 @@ { + "setup": { + "dialogs": { + "select_language": "Selectionner une langue", + "save_folder": "Pour télécharger les médias Snapchat, vous devez choisir un emplacement de sauvegarde. Cela peut être modifié plus tard dans les paramètres de l'application.", + "select_save_folder_button": "Choisir un emplacement de sauvegarde" + } + }, + "category": { "spying_privacy": "Espionnage et vie privée", "media_manager": "Gestionnaire de média", diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/Logger.kt b/core/src/main/kotlin/me/rhunk/snapenhance/Logger.kt index 9fcdf474d..f4756fffb 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/Logger.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/Logger.kt @@ -16,6 +16,11 @@ object Logger { Log.d(TAG, message.toString()) } + fun debug(tag: String, message: Any?) { + if (!BuildConfig.DEBUG) return + Log.d(tag, message.toString()) + } + fun error(throwable: Throwable) { Log.e(TAG, "", throwable) } diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ModContext.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ModContext.kt index d4c903341..b95247cd3 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/ModContext.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/ModContext.kt @@ -122,4 +122,8 @@ class ModContext { fun reloadConfig() { modConfig.loadFromBridge(bridgeClient) } + + fun getConfigLocale(): String { + return modConfig.locale + } } \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/SnapEnhance.kt b/core/src/main/kotlin/me/rhunk/snapenhance/SnapEnhance.kt index a88578a20..20f01f6b6 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/SnapEnhance.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/SnapEnhance.kt @@ -90,14 +90,14 @@ class SnapEnhance { @OptIn(ExperimentalTime::class) private suspend fun init() { - //load translations in a coroutine to speed up initialization - withContext(appContext.coroutineDispatcher) { - appContext.translation.loadFromBridge(appContext.bridgeClient) - } - measureTime { with(appContext) { reloadConfig() + withContext(appContext.coroutineDispatcher) { + translation.userLocale = getConfigLocale() + translation.loadFromBridge(appContext.bridgeClient) + } + mappings.init() eventDispatcher.init() //if mappings aren't loaded, we can't initialize features diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/bridge/BridgeClient.kt b/core/src/main/kotlin/me/rhunk/snapenhance/bridge/BridgeClient.kt index b3819ce35..e08b1bfa1 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/bridge/BridgeClient.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/bridge/BridgeClient.kt @@ -36,8 +36,9 @@ class BridgeClient( .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_MULTIPLE_TASK) ) + //TODO: randomize package name val intent = Intent() - .setClassName(BuildConfig.APPLICATION_ID, BridgeService::class.java.name) + .setClassName(BuildConfig.APPLICATION_ID, "me.rhunk.snapenhance.bridge.BridgeService") if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { bindService( intent, @@ -103,7 +104,7 @@ class BridgeClient( fun clearMessageLogger() = service.clearMessageLogger() - fun fetchTranslations() = service.fetchTranslations().map { + fun fetchLocales(userLocale: String) = service.fetchLocales(userLocale).map { LocalePair(it.key, it.value) } diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/LocaleWrapper.kt b/core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/LocaleWrapper.kt index bc7968ddc..289247602 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/LocaleWrapper.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/LocaleWrapper.kt @@ -13,15 +13,14 @@ class LocaleWrapper { companion object { const val DEFAULT_LOCALE = "en_US" - fun fetchLocales(context: Context): List { - val deviceLocale = Locale.getDefault().toString() - val locales = mutableListOf() - - locales.add(LocalePair(DEFAULT_LOCALE, context.resources.assets.open("lang/$DEFAULT_LOCALE.json").bufferedReader().use { it.readText() })) + fun fetchLocales(context: Context, locale: String = DEFAULT_LOCALE): List { + val locales = mutableListOf().apply { + add(LocalePair(DEFAULT_LOCALE, context.resources.assets.open("lang/$DEFAULT_LOCALE.json").bufferedReader().use { it.readText() })) + } - if (deviceLocale == DEFAULT_LOCALE) return locales + if (locale == DEFAULT_LOCALE) return locales - val compatibleLocale = context.resources.assets.list("lang")?.firstOrNull { it.startsWith(deviceLocale) }?.substring(0, 5) ?: return locales + val compatibleLocale = context.resources.assets.list("lang")?.firstOrNull { it.startsWith(locale) }?.substring(0, 5) ?: return locales context.resources.assets.open("lang/$compatibleLocale.json").use { inputStream -> locales.add(LocalePair(compatibleLocale, inputStream.bufferedReader().use { it.readText() })) @@ -29,19 +28,24 @@ class LocaleWrapper { return locales } + + fun fetchAvailableLocales(context: Context): List { + return context.resources.assets.list("lang")?.map { it.substring(0, 5) } ?: listOf() + } } + var userLocale = DEFAULT_LOCALE private val translationMap = linkedMapOf() - private lateinit var _locale: String + private lateinit var _loadedLocaleString: String - val locale by lazy { - Locale(_locale.substring(0, 2), _locale.substring(3, 5)) + val loadedLocale by lazy { + Locale(_loadedLocaleString.substring(0, 2), _loadedLocaleString.substring(3, 5)) } private fun load(localePair: LocalePair) { - if (!::_locale.isInitialized) { - _locale = localePair.locale + if (!::_loadedLocaleString.isInitialized) { + _loadedLocaleString = localePair.locale } val translations = JsonParser.parseString(localePair.content).asJsonObject @@ -64,17 +68,23 @@ class LocaleWrapper { } fun loadFromBridge(bridgeClient: BridgeClient) { - bridgeClient.fetchTranslations().forEach { + bridgeClient.fetchLocales(userLocale).forEach { load(it) } } fun loadFromContext(context: Context) { - fetchLocales(context).forEach { + fetchLocales(context, userLocale).forEach { load(it) } } + fun reloadFromContext(context: Context, locale: String) { + userLocale = locale + translationMap.clear() + loadFromContext(context) + } + operator fun get(key: String): String { return translationMap[key] ?: key.also { Logger.debug("Missing translation for $key") } } diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/MappingsWrapper.kt b/core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/MappingsWrapper.kt index ecf08d1fe..f410513ac 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/MappingsWrapper.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/MappingsWrapper.kt @@ -46,7 +46,6 @@ class MappingsWrapper( private val mappings = ConcurrentHashMap() private var snapBuildNumber: Long = 0 - @Suppress("deprecation") fun init() { snapBuildNumber = getSnapchatVersionCode() @@ -54,6 +53,7 @@ class MappingsWrapper( runCatching { loadCached() }.onFailure { + Logger.error("Failed to load cached mappings", it) delete() } } @@ -100,6 +100,7 @@ class MappingsWrapper( } fun refresh() { + snapBuildNumber = getSnapchatVersionCode() val mapper = Mapper(*mappers) runCatching { @@ -114,7 +115,7 @@ class MappingsWrapper( } write(result.toString().toByteArray()) }.also { - Logger.xposedLog("Generated mappings in $it ms") + Logger.debug("Generated mappings in $it ms") } } diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/config/ConfigContainer.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/ConfigContainer.kt index bba5bbfae..0b9683902 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/core/config/ConfigContainer.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/ConfigContainer.kt @@ -38,7 +38,7 @@ open class ConfigContainer( vararg values: String = emptyArray(), params: ConfigParamsBuilder = {} ) = registerProperty(key, - DataProcessors.STRING_MULTIPLE_SELECTION, PropertyValue(emptyList(), defaultValues = values.toList()), params) + DataProcessors.STRING_MULTIPLE_SELECTION, PropertyValue(mutableListOf(), defaultValues = values.toList()), params) //null value is considered as Off/Disabled protected fun unique( diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/config/ModConfig.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/ModConfig.kt index 8d4890a0e..11e7bfb9e 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/core/config/ModConfig.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/ModConfig.kt @@ -10,22 +10,20 @@ import me.rhunk.snapenhance.bridge.FileLoaderWrapper import me.rhunk.snapenhance.bridge.types.BridgeFileType import me.rhunk.snapenhance.bridge.wrapper.LocaleWrapper import me.rhunk.snapenhance.core.config.impl.RootConfig +import kotlin.properties.Delegates class ModConfig { - var locale: String = LocaleWrapper.DEFAULT_LOCALE - set(value) { - field = value - writeConfig() - } private val gson: Gson = GsonBuilder().setPrettyPrinting().create() private val file = FileLoaderWrapper(BridgeFileType.CONFIG, "{}".toByteArray(Charsets.UTF_8)) + var wasPresent by Delegates.notNull() val root = RootConfig() operator fun getValue(thisRef: Any?, property: Any?) = root private fun load() { + wasPresent = file.isFileExists() if (!file.isFileExists()) { writeConfig() return @@ -42,12 +40,13 @@ class ModConfig { private fun loadConfig() { val configFileContent = file.read() val configObject = gson.fromJson(configFileContent.toString(Charsets.UTF_8), JsonObject::class.java) - locale = configObject.get("language")?.asString ?: LocaleWrapper.DEFAULT_LOCALE + locale = configObject.get("_locale")?.asString ?: LocaleWrapper.DEFAULT_LOCALE + root.fromJson(configObject) } fun writeConfig() { val configObject = root.toJson() - configObject.addProperty("language", locale) + configObject.addProperty("_locale", locale) file.write(configObject.toString().toByteArray(Charsets.UTF_8)) } diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/download/DownloadManagerClient.kt b/core/src/main/kotlin/me/rhunk/snapenhance/download/DownloadManagerClient.kt index d973b2f4a..7256a5a2d 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/download/DownloadManagerClient.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/download/DownloadManagerClient.kt @@ -16,11 +16,16 @@ class DownloadManagerClient ( private val metadata: DownloadMetadata, private val callback: DownloadCallback ) { + companion object { + const val DOWNLOAD_REQUEST_EXTRA = "request" + const val DOWNLOAD_METADATA_EXTRA = "metadata" + } + private fun enqueueDownloadRequest(request: DownloadRequest) { context.bridgeClient.enqueueDownload(Intent().apply { putExtras(Bundle().apply { - putString(DownloadProcessor.DOWNLOAD_REQUEST_EXTRA, context.gson.toJson(request)) - putString(DownloadProcessor.DOWNLOAD_METADATA_EXTRA, context.gson.toJson(metadata)) + putString(DOWNLOAD_REQUEST_EXTRA, context.gson.toJson(request)) + putString(DOWNLOAD_METADATA_EXTRA, context.gson.toJson(metadata)) }) }, callback) } diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/FeatureLoadParams.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/FeatureLoadParams.kt index fbbbc2f4b..46e201e6a 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/FeatureLoadParams.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/FeatureLoadParams.kt @@ -3,9 +3,9 @@ package me.rhunk.snapenhance.features object FeatureLoadParams { const val NO_INIT = 0 - const val INIT_SYNC = 1 - const val ACTIVITY_CREATE_SYNC = 2 + const val INIT_SYNC = 0b0001 + const val ACTIVITY_CREATE_SYNC = 0b0010 - const val INIT_ASYNC = 3 - const val ACTIVITY_CREATE_ASYNC = 4 + const val INIT_ASYNC = 0b0100 + const val ACTIVITY_CREATE_ASYNC = 0b1000 } \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/FriendFeedInfoMenu.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/FriendFeedInfoMenu.kt index 3d2c3f011..2c1156bf0 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/FriendFeedInfoMenu.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/FriendFeedInfoMenu.kt @@ -84,7 +84,7 @@ class FriendFeedInfoMenu : AbstractMenu() { ${birthday.getDisplayName( Calendar.MONTH, Calendar.LONG, - context.translation.locale + context.translation.loadedLocale )?.let { context.translation.format("profile_info.birthday", "month" to it, diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/SettingsGearInjector.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/SettingsGearInjector.kt index a1887cafb..0a68d1034 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/SettingsGearInjector.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/ui/menu/impl/SettingsGearInjector.kt @@ -47,9 +47,8 @@ class SettingsGearInjector : AbstractMenu() { setOnClickListener { val intent = Intent().apply { - setClassName(BuildConfig.APPLICATION_ID, "me.rhunk.snapenhance.manager.MainActivity") + setClassName(BuildConfig.APPLICATION_ID, "me.rhunk.snapenhance.ui.manager.MainActivity") putExtra("route", "features") - putExtra("lspatched", File(context.cacheDir, "lspatch/origin").exists()) } context.startActivity(intent) } From 2ff8a6940364a997f6c8515dc83bb7303609a930 Mon Sep 17 00:00:00 2001 From: rhunk <101876869+rhunk@users.noreply.github.com> Date: Fri, 4 Aug 2023 11:33:20 +0200 Subject: [PATCH 18/81] fix(startup_page_override): camera in background --- .../features/impl/ui/StartupPageOverride.kt | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/StartupPageOverride.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/StartupPageOverride.kt index 707d9643b..11ae48dcc 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/StartupPageOverride.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/StartupPageOverride.kt @@ -1,6 +1,7 @@ package me.rhunk.snapenhance.features.impl.ui import android.annotation.SuppressLint +import android.os.Handler import android.view.View import android.view.ViewGroup import android.widget.LinearLayout @@ -23,17 +24,17 @@ class StartupPageOverride : Feature("StartupPageOverride", loadParams = FeatureL ngs_search_icon_container */ + private fun clickNgsIcon() { + Handler(context.androidContext.mainLooper).postDelayed({ + ngsIcon?.callOnClick() + }, 300) + } + override fun onActivityCreate() { val ngsIconName = context.config.userInterface.startupTab.getNullable() ?: return context.androidContext.classLoader.loadClass("com.snap.mushroom.MainActivity").apply { - hook("onPostCreate", HookStage.AFTER) { - ngsIcon?.callOnClick() - } - - hook("onResume", HookStage.AFTER) { - ngsIcon?.callOnClick() - } + hook("onResume", HookStage.AFTER) { clickNgsIcon() } } val ngsIconId = context.androidContext.resources.getIdentifier(ngsIconName, "id", Constants.SNAPCHAT_PACKAGE_NAME) From 3df11aadb8546f5997e2f36b9790f4e96e12b1df Mon Sep 17 00:00:00 2001 From: rhunk <101876869+rhunk@users.noreply.github.com> Date: Fri, 4 Aug 2023 12:17:19 +0200 Subject: [PATCH 19/81] refactor: download - download task manager - fix installation summary update --- .../me/rhunk/snapenhance/RemoteSideContext.kt | 33 +++++-- .../rhunk/snapenhance/SharedContextHolder.kt | 14 +-- .../rhunk/snapenhance/bridge/BridgeService.kt | 11 ++- .../snapenhance/download/DownloadProcessor.kt | 90 +++++++++---------- .../snapenhance/ui/manager/MainActivity.kt | 15 +++- .../rhunk/snapenhance/ui/manager/Section.kt | 5 +- .../ui/manager/sections/HomeSection.kt | 16 +++- .../sections/download/DownloadSection.kt | 11 +++ .../snapenhance/ui/setup/Requirements.kt | 2 - .../snapenhance/ui/setup/SetupActivity.kt | 4 - .../ui/setup/screens/impl/FfmpegScreen.kt | 17 ---- .../setup/screens/impl/PickLanguageScreen.kt | 2 +- .../me/rhunk/snapenhance/SharedContext.kt | 45 ---------- .../core/config/impl/DownloaderConfig.kt | 6 +- .../download/DownloadManagerClient.kt | 2 +- .../download/DownloadTaskManager.kt | 3 +- .../{enums => data}/DownloadMediaType.kt | 2 +- .../download/data/DownloadRequest.kt | 2 - .../download/{enums => data}/DownloadStage.kt | 2 +- .../download/data/PendingDownload.kt | 15 ++-- .../impl/downloader/MediaDownloader.kt | 33 +++---- .../ui/download/DownloadListAdapter.kt | 2 +- 22 files changed, 152 insertions(+), 180 deletions(-) create mode 100644 app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/download/DownloadSection.kt delete mode 100644 app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/impl/FfmpegScreen.kt rename core/src/main/kotlin/me/rhunk/snapenhance/download/{enums => data}/DownloadMediaType.kt (92%) rename core/src/main/kotlin/me/rhunk/snapenhance/download/{enums => data}/DownloadStage.kt (84%) diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/RemoteSideContext.kt b/app/src/main/kotlin/me/rhunk/snapenhance/RemoteSideContext.kt index 3302e3305..667f5534c 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/RemoteSideContext.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/RemoteSideContext.kt @@ -8,6 +8,7 @@ import androidx.documentfile.provider.DocumentFile import me.rhunk.snapenhance.bridge.wrapper.LocaleWrapper import me.rhunk.snapenhance.bridge.wrapper.MappingsWrapper import me.rhunk.snapenhance.core.config.ModConfig +import me.rhunk.snapenhance.download.DownloadTaskManager import me.rhunk.snapenhance.ui.manager.data.InstallationSummary import me.rhunk.snapenhance.ui.manager.data.ModMappingsInfo import me.rhunk.snapenhance.ui.manager.data.SnapchatAppInfo @@ -17,24 +18,40 @@ import java.lang.ref.WeakReference import kotlin.system.exitProcess class RemoteSideContext( - val androidContext: Context + ctx: Context ) { + private var _context: WeakReference = WeakReference(ctx) private var _activity: WeakReference? = null + + var androidContext: Context + get() = synchronized(this) { + _context.get() ?: error("Context is null") + } + set(value) { synchronized(this) { + _context.clear(); _context = WeakReference(value) + } } + var activity: Activity? get() = _activity?.get() - set(value) { _activity = WeakReference(value) } + set(value) { _activity?.clear(); _activity = WeakReference(value) } val config = ModConfig() val translation = LocaleWrapper() val mappings = MappingsWrapper(androidContext) + val downloadTaskManager = DownloadTaskManager() init { - config.loadFromContext(androidContext) - translation.userLocale = config.locale - translation.loadFromContext(androidContext) - mappings.apply { - loadFromContext(androidContext) - init() + runCatching { + config.loadFromContext(androidContext) + translation.userLocale = config.locale + translation.loadFromContext(androidContext) + mappings.apply { + loadFromContext(androidContext) + init() + } + downloadTaskManager.init(androidContext) + }.onFailure { + Logger.error("Failed to initialize RemoteSideContext", it) } } diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/SharedContextHolder.kt b/app/src/main/kotlin/me/rhunk/snapenhance/SharedContextHolder.kt index 918ec1b53..738639f75 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/SharedContextHolder.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/SharedContextHolder.kt @@ -1,15 +1,19 @@ package me.rhunk.snapenhance import android.content.Context -import java.lang.ref.WeakReference object SharedContextHolder { - private lateinit var _remoteSideContext: WeakReference + private lateinit var _remoteSideContext: RemoteSideContext fun remote(context: Context): RemoteSideContext { - if (!::_remoteSideContext.isInitialized || _remoteSideContext.get() == null) { - _remoteSideContext = WeakReference(RemoteSideContext(context.applicationContext)) + if (!::_remoteSideContext.isInitialized) { + _remoteSideContext = RemoteSideContext(context) } - return _remoteSideContext.get()!! + + if (_remoteSideContext.androidContext != context) { + _remoteSideContext.androidContext = context + } + + return _remoteSideContext } } \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/bridge/BridgeService.kt b/app/src/main/kotlin/me/rhunk/snapenhance/bridge/BridgeService.kt index 196ec6d9b..ea763a4c9 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/bridge/BridgeService.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/bridge/BridgeService.kt @@ -4,11 +4,10 @@ import android.app.Service import android.content.Intent import android.os.IBinder import me.rhunk.snapenhance.RemoteSideContext -import me.rhunk.snapenhance.SharedContext import me.rhunk.snapenhance.SharedContextHolder import me.rhunk.snapenhance.bridge.types.BridgeFileType -import me.rhunk.snapenhance.bridge.wrapper.MessageLoggerWrapper import me.rhunk.snapenhance.bridge.wrapper.LocaleWrapper +import me.rhunk.snapenhance.bridge.wrapper.MessageLoggerWrapper import me.rhunk.snapenhance.download.DownloadProcessor class BridgeService : Service() { @@ -105,10 +104,10 @@ class BridgeService : Service() { } override fun enqueueDownload(intent: Intent, callback: DownloadCallback) { - SharedContextHolder.remote(this@BridgeService) - //TODO: refactor shared context - SharedContext.ensureInitialized(this@BridgeService) - DownloadProcessor(this@BridgeService, callback).onReceive(intent) + DownloadProcessor( + remoteSideContext = SharedContextHolder.remote(this@BridgeService), + callback = callback + ).onReceive(intent) } } } diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/download/DownloadProcessor.kt b/app/src/main/kotlin/me/rhunk/snapenhance/download/DownloadProcessor.kt index a9376e8c5..410e58baa 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/download/DownloadProcessor.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/download/DownloadProcessor.kt @@ -1,7 +1,6 @@ package me.rhunk.snapenhance.download import android.annotation.SuppressLint -import android.content.Context import android.content.Intent import android.net.Uri import android.widget.Toast @@ -16,17 +15,17 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import me.rhunk.snapenhance.Constants import me.rhunk.snapenhance.Logger +import me.rhunk.snapenhance.RemoteSideContext import me.rhunk.snapenhance.SharedContext import me.rhunk.snapenhance.bridge.DownloadCallback -import me.rhunk.snapenhance.core.config.ModConfig import me.rhunk.snapenhance.data.FileType +import me.rhunk.snapenhance.download.data.DownloadMediaType import me.rhunk.snapenhance.download.data.DownloadMetadata import me.rhunk.snapenhance.download.data.DownloadRequest +import me.rhunk.snapenhance.download.data.DownloadStage import me.rhunk.snapenhance.download.data.InputMedia import me.rhunk.snapenhance.download.data.MediaEncryptionKeyPair import me.rhunk.snapenhance.download.data.PendingDownload -import me.rhunk.snapenhance.download.enums.DownloadMediaType -import me.rhunk.snapenhance.download.enums.DownloadStage import me.rhunk.snapenhance.util.download.RemoteMediaResolver import me.rhunk.snapenhance.util.snap.MediaDownloaderHelper import java.io.File @@ -56,7 +55,7 @@ data class DownloadedFile( */ @OptIn(ExperimentalEncodingApi::class) class DownloadProcessor ( - private val context: Context, + private val remoteSideContext: RemoteSideContext, private val callback: DownloadCallback ) { @@ -69,11 +68,29 @@ class DownloadProcessor ( } private fun fallbackToast(message: Any) { - android.os.Handler(context.mainLooper).post { - Toast.makeText(context, message.toString(), Toast.LENGTH_SHORT).show() + android.os.Handler(remoteSideContext.androidContext.mainLooper).post { + Toast.makeText(remoteSideContext.androidContext, message.toString(), Toast.LENGTH_SHORT).show() } } + private fun callbackOnSuccess(path: String) = runCatching { + callback.onSuccess(path) + }.onFailure { + fallbackToast(it) + } + + private fun callbackOnFailure(message: String, throwable: String? = null) = runCatching { + callback.onFailure(message, throwable) + }.onFailure { + fallbackToast("$message\n$throwable") + } + + private fun callbackOnProgress(message: String) = runCatching { + callback.onProgress(message) + }.onFailure { + fallbackToast(it) + } + private fun extractZip(inputStream: InputStream): List { val files = mutableListOf() val zipInputStream = ZipInputStream(inputStream) @@ -100,30 +117,20 @@ class DownloadProcessor ( return CipherInputStream(inputStream, cipher) } - private fun createNeededDirectories(file: File): File { - val directory = file.parentFile ?: return file - if (!directory.exists()) { - directory.mkdirs() - } - return file - } - @SuppressLint("UnspecifiedRegisterReceiverFlag") private suspend fun saveMediaToGallery(inputFile: File, pendingDownload: PendingDownload) { if (coroutineContext.job.isCancelled) return - val config by ModConfig().apply { loadFromContext(context) } - runCatching { val fileType = FileType.fromFile(inputFile) if (fileType == FileType.UNKNOWN) { - callback.onFailure(translation.format("failed_gallery_toast", "error" to "Unknown media type"), null) + callbackOnFailure(translation.format("failed_gallery_toast", "error" to "Unknown media type"), null) return } val fileName = pendingDownload.metadata.outputPath.substringAfterLast("/") + "." + fileType.fileExtension - val outputFolder = DocumentFile.fromTreeUri(context, Uri.parse(config.downloader.saveFolder.get())) + val outputFolder = DocumentFile.fromTreeUri(remoteSideContext.androidContext, Uri.parse(remoteSideContext.config.root.downloader.saveFolder.get())) ?: throw Exception("Failed to open output folder") val outputFileFolder = pendingDownload.metadata.outputPath.let { @@ -137,7 +144,7 @@ class DownloadProcessor ( } val outputFile = outputFileFolder.createFile(fileType.mimeType, fileName)!! - val outputStream = context.contentResolver.openOutputStream(outputFile.uri)!! + val outputStream = remoteSideContext.androidContext.contentResolver.openOutputStream(outputFile.uri)!! inputFile.inputStream().use { inputStream -> inputStream.copyTo(outputStream) @@ -149,21 +156,17 @@ class DownloadProcessor ( runCatching { val mediaScanIntent = Intent("android.intent.action.MEDIA_SCANNER_SCAN_FILE") mediaScanIntent.setData(outputFile.uri) - context.sendBroadcast(mediaScanIntent) + remoteSideContext.androidContext.sendBroadcast(mediaScanIntent) }.onFailure { Logger.error("Failed to scan media file", it) - callback.onFailure(translation.format("failed_gallery_toast", "error" to it.toString()), it.message) + callbackOnFailure(translation.format("failed_gallery_toast", "error" to it.toString()), it.message) } Logger.debug("download complete") - fileName.let { - runCatching { callback.onSuccess(it) }.onFailure { fallbackToast(it) } - } + callbackOnSuccess(fileName) }.onFailure { exception -> Logger.error(exception) - translation.format("failed_gallery_toast", "error" to exception.toString()).let { - runCatching { callback.onFailure(it, exception.message) }.onFailure { fallbackToast(it) } - } + callbackOnFailure(translation.format("failed_gallery_toast", "error" to exception.toString()), exception.message) pendingDownload.downloadStage = DownloadStage.FAILED } } @@ -250,9 +253,7 @@ class DownloadProcessor ( val xmlData = dashPlaylistFile.outputStream() TransformerFactory.newInstance().newTransformer().transform(DOMSource(playlistXml), StreamResult(xmlData)) - translation.format("download_toast", "path" to dashPlaylistFile.nameWithoutExtension).let { - runCatching { callback.onProgress(it) }.onFailure { fallbackToast(it) } - } + callbackOnProgress(translation.format("download_toast", "path" to dashPlaylistFile.nameWithoutExtension)) val outputFile = File.createTempFile("dash", ".mp4") runCatching { MediaDownloaderHelper.downloadDashChapterFile( @@ -264,9 +265,7 @@ class DownloadProcessor ( }.onFailure { exception -> if (coroutineContext.job.isCancelled) return@onFailure Logger.error(exception) - translation.format("failed_processing_toast", "error" to exception.toString()).let { - runCatching { callback.onFailure(it, exception.message) }.onFailure { fallbackToast(it) } - } + callbackOnFailure(translation.format("failed_processing_toast", "error" to exception.toString()), exception.message) pendingDownloadObject.downloadStage = DownloadStage.FAILED } @@ -287,23 +286,24 @@ class DownloadProcessor ( val downloadMetadata = gson.fromJson(intent.getStringExtra(DownloadManagerClient.DOWNLOAD_METADATA_EXTRA)!!, DownloadMetadata::class.java) val downloadRequest = gson.fromJson(intent.getStringExtra(DownloadManagerClient.DOWNLOAD_REQUEST_EXTRA)!!, DownloadRequest::class.java) - SharedContext.downloadTaskManager.canDownloadMedia(downloadMetadata.mediaIdentifier)?.let { downloadStage -> + remoteSideContext.downloadTaskManager.canDownloadMedia(downloadMetadata.mediaIdentifier)?.let { downloadStage -> translation[if (downloadStage.isFinalStage) { "already_downloaded_toast" } else { "already_queued_toast" }].let { - runCatching { callback.onFailure(it, null) }.onFailure { fallbackToast(it) } + callbackOnFailure(it, null) } return@launch } val pendingDownloadObject = PendingDownload( metadata = downloadMetadata - ) + ).apply { downloadTaskManager = remoteSideContext.downloadTaskManager } - SharedContext.downloadTaskManager.addTask(pendingDownloadObject) - pendingDownloadObject.apply { + pendingDownloadObject.also { + remoteSideContext.downloadTaskManager.addTask(it) + }.apply { job = coroutineContext.job downloadStage = DownloadStage.DOWNLOADING } @@ -344,9 +344,7 @@ class DownloadProcessor ( val renamedOverlayMedia = renameFromFileType(overlayMedia.file, overlayMedia.fileType) val mergedOverlay: File = File.createTempFile("merged", "." + media.fileType.fileExtension) runCatching { - translation.format("download_toast", "path" to media.file.nameWithoutExtension).let { - runCatching { callback.onProgress(it) }.onFailure { fallbackToast(it) } - } + callbackOnProgress(translation.format("download_toast", "path" to media.file.nameWithoutExtension)) pendingDownloadObject.downloadStage = DownloadStage.MERGING MediaDownloaderHelper.mergeOverlayFile( @@ -359,9 +357,7 @@ class DownloadProcessor ( }.onFailure { exception -> if (coroutineContext.job.isCancelled) return@onFailure Logger.error(exception) - translation.format("failed_processing_toast", "error" to exception.toString()).let { - runCatching { callback.onFailure(it, exception.message) }.onFailure { fallbackToast(it) } - } + callbackOnFailure(translation.format("failed_processing_toast", "error" to exception.toString()), exception.message) pendingDownloadObject.downloadStage = DownloadStage.MERGE_FAILED } @@ -375,9 +371,7 @@ class DownloadProcessor ( }.onFailure { exception -> pendingDownloadObject.downloadStage = DownloadStage.FAILED Logger.error(exception) - translation["failed_generic_toast"].let { - runCatching { callback.onFailure(it, exception.message) }.onFailure { fallbackToast(it) } - } + callbackOnFailure(translation["failed_generic_toast"], exception.message) } } } diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/MainActivity.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/MainActivity.kt index 51c9ed3c3..9b7ea8a4f 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/MainActivity.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/MainActivity.kt @@ -11,6 +11,13 @@ import me.rhunk.snapenhance.SharedContextHolder import me.rhunk.snapenhance.ui.AppMaterialTheme class MainActivity : ComponentActivity() { + lateinit var sections: Map + + override fun onPostResume() { + super.onPostResume() + sections.values.forEach { it.onResumed() } + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -20,8 +27,12 @@ class MainActivity : ComponentActivity() { checkForRequirements() } - val sections = EnumSection.values().toList().associateWith { - it.section.constructors.first().call() + sections = EnumSection.values().toList().associateWith { + runCatching { + it.section.constructors.first().call() + }.onFailure { + it.printStackTrace() + }.getOrThrow() }.onEach { (section, instance) -> with(instance) { enumSection = section diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/Section.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/Section.kt index ecef8d7f8..756a44229 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/Section.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/Section.kt @@ -14,6 +14,7 @@ import androidx.navigation.compose.composable import me.rhunk.snapenhance.RemoteSideContext import me.rhunk.snapenhance.ui.manager.sections.HomeSection import me.rhunk.snapenhance.ui.manager.sections.NotImplemented +import me.rhunk.snapenhance.ui.manager.sections.download.DownloadSection import me.rhunk.snapenhance.ui.manager.sections.features.FeaturesSection import kotlin.reflect.KClass @@ -26,7 +27,8 @@ enum class EnumSection( DOWNLOADS( route = "downloads", title = "Downloads", - icon = Icons.Filled.Download + icon = Icons.Filled.Download, + section = DownloadSection::class ), FEATURES( route = "features", @@ -66,6 +68,7 @@ open class Section { lateinit var navController: NavController open fun init() {} + open fun onResumed() {} @Composable open fun Content() { NotImplemented() } diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/HomeSection.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/HomeSection.kt index 34350152e..bf514f4ed 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/HomeSection.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/HomeSection.kt @@ -18,11 +18,13 @@ import androidx.compose.material3.Icon import androidx.compose.material3.OutlinedCard import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import me.rhunk.snapenhance.Logger import me.rhunk.snapenhance.ui.manager.Section import me.rhunk.snapenhance.ui.manager.data.InstallationSummary import me.rhunk.snapenhance.ui.setup.Requirements @@ -31,6 +33,7 @@ class HomeSection : Section() { companion object { val cardMargin = 10.dp } + private val installationSummary = mutableStateOf(null as InstallationSummary?) @OptIn(ExperimentalLayoutApi::class) @Composable @@ -72,7 +75,8 @@ class HomeSection : Section() { "Mappings ${if (installationSummary.mappingsInfo == null) "not generated" else "outdated"}" } else { "Mappings version ${installationSummary.mappingsInfo.generatedSnapchatVersion}" - }, modifier = Modifier.weight(1f) + }, modifier = Modifier + .weight(1f) .align(Alignment.CenterVertically) ) @@ -86,6 +90,14 @@ class HomeSection : Section() { } } + override fun onResumed() { + Logger.debug("HomeSection resumed") + if (!context.mappings.isMappingsLoaded()) { + context.mappings.init() + } + installationSummary.value = context.getInstallationSummary() + } + @Composable @Preview override fun Content() { @@ -105,7 +117,7 @@ class HomeSection : Section() { modifier = Modifier.padding(16.dp) ) - SummaryCards(context.getInstallationSummary()) + SummaryCards(installationSummary = installationSummary.value ?: return) } } } \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/download/DownloadSection.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/download/DownloadSection.kt new file mode 100644 index 000000000..b695bd59c --- /dev/null +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/download/DownloadSection.kt @@ -0,0 +1,11 @@ +package me.rhunk.snapenhance.ui.manager.sections.download + +import androidx.compose.runtime.Composable +import me.rhunk.snapenhance.ui.manager.Section + +class DownloadSection : Section() { + @Composable + override fun Content() { + + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/Requirements.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/Requirements.kt index 8c7ca51ac..8207f6c3c 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/Requirements.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/Requirements.kt @@ -5,7 +5,6 @@ object Requirements { const val LANGUAGE = 0b00010 const val MAPPINGS = 0b00100 const val SAVE_FOLDER = 0b01000 - const val FFMPEG = 0b10000 fun getName(requirement: Int): String { return when (requirement) { @@ -13,7 +12,6 @@ object Requirements { LANGUAGE -> "LANGUAGE" MAPPINGS -> "MAPPINGS" SAVE_FOLDER -> "SAVE_FOLDER" - FFMPEG -> "FFMPEG" else -> "UNKNOWN" } } diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/SetupActivity.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/SetupActivity.kt index a05bcdf7b..7e4656ba3 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/SetupActivity.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/SetupActivity.kt @@ -34,7 +34,6 @@ import androidx.navigation.compose.rememberNavController import me.rhunk.snapenhance.SharedContextHolder import me.rhunk.snapenhance.ui.AppMaterialTheme import me.rhunk.snapenhance.ui.setup.screens.SetupScreen -import me.rhunk.snapenhance.ui.setup.screens.impl.FfmpegScreen import me.rhunk.snapenhance.ui.setup.screens.impl.MappingsScreen import me.rhunk.snapenhance.ui.setup.screens.impl.PickLanguageScreen import me.rhunk.snapenhance.ui.setup.screens.impl.SaveFolderScreen @@ -65,9 +64,6 @@ class SetupActivity : ComponentActivity() { if (isFirstRun || hasRequirement(Requirements.MAPPINGS)) { add(MappingsScreen().apply { route = "mappings" }) } - if (isFirstRun || hasRequirement(Requirements.FFMPEG)) { - add(FfmpegScreen().apply { route = "ffmpeg" }) - } } // If there are no required screens, we can just finish the activity diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/impl/FfmpegScreen.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/impl/FfmpegScreen.kt deleted file mode 100644 index 000a5f4bb..000000000 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/impl/FfmpegScreen.kt +++ /dev/null @@ -1,17 +0,0 @@ -package me.rhunk.snapenhance.ui.setup.screens.impl - -import androidx.compose.material3.Button -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import me.rhunk.snapenhance.ui.setup.screens.SetupScreen - -class FfmpegScreen : SetupScreen() { - - @Composable - override fun Content() { - Text(text = "FFmpeg") - Button(onClick = { allowNext(true) }) { - Text(text = "Next") - } - } -} \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/impl/PickLanguageScreen.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/impl/PickLanguageScreen.kt index dc5591d17..b9422b0b1 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/impl/PickLanguageScreen.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/impl/PickLanguageScreen.kt @@ -40,7 +40,7 @@ class PickLanguageScreen : SetupScreen(){ fun getLocaleDisplayName(locale: String): String { locale.split("_").let { - return java.util.Locale(it[0], it[1]).getDisplayName(java.util.Locale.getDefault()) + return Locale(it[0], it[1]).getDisplayName(Locale.getDefault()) } } diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/SharedContext.kt b/core/src/main/kotlin/me/rhunk/snapenhance/SharedContext.kt index 660969f30..0bd0a8921 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/SharedContext.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/SharedContext.kt @@ -18,51 +18,6 @@ object SharedContext { lateinit var downloadTaskManager: DownloadTaskManager lateinit var translation: LocaleWrapper - private fun askForStoragePermission(context: Context) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION) - intent.addCategory("android.intent.category.DEFAULT") - intent.data = android.net.Uri.parse("package:${context.packageName}") - if (context !is Activity) { - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - } - context.startActivity(intent) - exitProcess(0) - } - if (context !is Activity) { - Logger.log("Storage permission not granted, exiting") - exitProcess(0) - } - context.requestPermissions(arrayOf(android.Manifest.permission.WRITE_EXTERNAL_STORAGE, android.Manifest.permission.READ_EXTERNAL_STORAGE), 0) - } - - private fun askForPermissions(context: Context) { - - //ask for storage permission - val hasStoragePermission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - Environment.isExternalStorageManager() - } else { - context.checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == android.content.pm.PackageManager.PERMISSION_GRANTED - } - - if (hasStoragePermission) return - - if (context !is Activity) { - askForStoragePermission(context) - return - } - AlertDialog.Builder(context) - .setTitle("Storage permission") - .setMessage("App needs storage permission to download files and save them to your device. Please allow it in the next screen.") - .setPositiveButton("Grant") { _, _ -> - askForStoragePermission(context) - } - .setNegativeButton("Cancel") { _, _ -> - exitProcess(0) - } - .show() - } - fun ensureInitialized(context: Context) { if (!this::downloadTaskManager.isInitialized) { downloadTaskManager = DownloadTaskManager().apply { diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/DownloaderConfig.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/DownloaderConfig.kt index 791bc774c..9c2d44566 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/DownloaderConfig.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/DownloaderConfig.kt @@ -16,9 +16,11 @@ class DownloaderConfig : ConfigContainer() { "append_date_time", "append_type", "append_username" - ) + ).apply { set(mutableListOf("append_hash", "append_date_time", "append_type", "append_username")) } val allowDuplicate = boolean("allow_duplicate") val mergeOverlays = boolean("merge_overlays") val chatDownloadContextMenu = boolean("chat_download_context_menu") - val logging = multiple("logging", "started", "success", "progress", "failure") + val logging = multiple("logging", "started", "success", "progress", "failure").apply { + set(mutableListOf("started", "success")) + } } \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/download/DownloadManagerClient.kt b/core/src/main/kotlin/me/rhunk/snapenhance/download/DownloadManagerClient.kt index 7256a5a2d..ed616c7ff 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/download/DownloadManagerClient.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/download/DownloadManagerClient.kt @@ -9,7 +9,7 @@ import me.rhunk.snapenhance.download.data.DownloadMetadata import me.rhunk.snapenhance.download.data.DownloadRequest import me.rhunk.snapenhance.download.data.InputMedia import me.rhunk.snapenhance.download.data.MediaEncryptionKeyPair -import me.rhunk.snapenhance.download.enums.DownloadMediaType +import me.rhunk.snapenhance.download.data.DownloadMediaType class DownloadManagerClient ( private val context: ModContext, diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/download/DownloadTaskManager.kt b/core/src/main/kotlin/me/rhunk/snapenhance/download/DownloadTaskManager.kt index 952bbaeab..80e331687 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/download/DownloadTaskManager.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/download/DownloadTaskManager.kt @@ -5,7 +5,7 @@ import android.content.Context import android.database.sqlite.SQLiteDatabase import me.rhunk.snapenhance.download.data.DownloadMetadata import me.rhunk.snapenhance.download.data.PendingDownload -import me.rhunk.snapenhance.download.enums.DownloadStage +import me.rhunk.snapenhance.download.data.DownloadStage import me.rhunk.snapenhance.ui.download.MediaFilter import me.rhunk.snapenhance.util.SQLiteDatabaseHelper @@ -158,6 +158,7 @@ class DownloadTaskManager { iconUrl = cursor.getString(cursor.getColumnIndex("iconUrl")) ) ).apply { + downloadTaskManager = this@DownloadTaskManager downloadStage = DownloadStage.valueOf(cursor.getString(cursor.getColumnIndex("downloadStage"))) //if downloadStage is not saved, it means the app was killed before the download was finished if (downloadStage != DownloadStage.SAVED) { diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/download/enums/DownloadMediaType.kt b/core/src/main/kotlin/me/rhunk/snapenhance/download/data/DownloadMediaType.kt similarity index 92% rename from core/src/main/kotlin/me/rhunk/snapenhance/download/enums/DownloadMediaType.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/download/data/DownloadMediaType.kt index 666fa1f66..03c6c18d7 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/download/enums/DownloadMediaType.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/download/data/DownloadMediaType.kt @@ -1,4 +1,4 @@ -package me.rhunk.snapenhance.download.enums +package me.rhunk.snapenhance.download.data import android.net.Uri diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/download/data/DownloadRequest.kt b/core/src/main/kotlin/me/rhunk/snapenhance/download/data/DownloadRequest.kt index 653dba45a..4bc08bea9 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/download/data/DownloadRequest.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/download/data/DownloadRequest.kt @@ -1,7 +1,5 @@ package me.rhunk.snapenhance.download.data -import me.rhunk.snapenhance.download.enums.DownloadMediaType - data class DashOptions(val offsetTime: Long, val duration: Long?) data class InputMedia( diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/download/enums/DownloadStage.kt b/core/src/main/kotlin/me/rhunk/snapenhance/download/data/DownloadStage.kt similarity index 84% rename from core/src/main/kotlin/me/rhunk/snapenhance/download/enums/DownloadStage.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/download/data/DownloadStage.kt index f20862eff..c23c8d605 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/download/enums/DownloadStage.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/download/data/DownloadStage.kt @@ -1,4 +1,4 @@ -package me.rhunk.snapenhance.download.enums +package me.rhunk.snapenhance.download.data enum class DownloadStage( val isFinalStage: Boolean = false, diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/download/data/PendingDownload.kt b/core/src/main/kotlin/me/rhunk/snapenhance/download/data/PendingDownload.kt index 7960661ee..09dc4a263 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/download/data/PendingDownload.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/download/data/PendingDownload.kt @@ -2,15 +2,16 @@ package me.rhunk.snapenhance.download.data import kotlinx.coroutines.Job import me.rhunk.snapenhance.SharedContext -import me.rhunk.snapenhance.download.enums.DownloadStage +import me.rhunk.snapenhance.download.DownloadTaskManager data class PendingDownload( var downloadId: Int = 0, var outputFile: String? = null, - var job: Job? = null, - val metadata : DownloadMetadata ) { + lateinit var downloadTaskManager: DownloadTaskManager + var job: Job? = null + var changeListener = { _: DownloadStage, _: DownloadStage -> } private var _stage: DownloadStage = DownloadStage.PENDING var downloadStage: DownloadStage @@ -20,15 +21,13 @@ data class PendingDownload( set(value) = synchronized(this) { changeListener(_stage, value) _stage = value - SharedContext.downloadTaskManager.updateTask(this) + downloadTaskManager.updateTask(this) } - fun isJobActive(): Boolean { - return job?.isActive ?: false - } + fun isJobActive() = job?.isActive == true fun cancel() { - job?.cancel() downloadStage = DownloadStage.CANCELLED + job?.cancel() } } diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/downloader/MediaDownloader.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/downloader/MediaDownloader.kt index 9249fe396..2f6f836c5 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/downloader/MediaDownloader.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/downloader/MediaDownloader.kt @@ -5,7 +5,6 @@ import android.graphics.Bitmap import android.graphics.BitmapFactory import android.net.Uri import android.widget.ImageView -import com.arthenica.ffmpegkit.FFmpegKit import kotlinx.coroutines.runBlocking import me.rhunk.snapenhance.Constants.ARROYO_URL_KEY_PROTO_PATH import me.rhunk.snapenhance.Logger @@ -20,11 +19,11 @@ import me.rhunk.snapenhance.data.wrapper.impl.media.opera.Layer import me.rhunk.snapenhance.data.wrapper.impl.media.opera.ParamMap import me.rhunk.snapenhance.database.objects.FriendInfo import me.rhunk.snapenhance.download.DownloadManagerClient +import me.rhunk.snapenhance.download.data.DownloadMediaType import me.rhunk.snapenhance.download.data.DownloadMetadata import me.rhunk.snapenhance.download.data.InputMedia import me.rhunk.snapenhance.download.data.SplitMediaAssetType import me.rhunk.snapenhance.download.data.toKeyPair -import me.rhunk.snapenhance.download.enums.DownloadMediaType import me.rhunk.snapenhance.features.Feature import me.rhunk.snapenhance.features.FeatureLoadParams import me.rhunk.snapenhance.features.impl.Messaging @@ -52,11 +51,8 @@ import kotlin.io.encoding.ExperimentalEncodingApi class MediaDownloader : Feature("MediaDownloader", loadParams = FeatureLoadParams.ACTIVITY_CREATE_ASYNC) { private var lastSeenMediaInfoMap: MutableMap? = null private var lastSeenMapParams: ParamMap? = null - private val isFFmpegPresent by lazy { - runCatching { FFmpegKit.execute("-version") }.isSuccess - } - private fun provideClientDownloadManager( + private fun provideDownloadManagerClient( pathSuffix: String, mediaIdentifier: String, mediaDisplaySource: String? = null, @@ -115,10 +111,6 @@ class MediaDownloader : Feature("MediaDownloader", loadParams = FeatureLoadParam ) } - private fun canMergeOverlay(): Boolean { - if (!context.config.downloader.autoDownloadOptions.get().contains("merge_overlay")) return false - return isFFmpegPresent - } //TODO: implement subfolder argument private fun createNewFilePath(hexHash: String, mediaDisplayType: String?, pathPrefix: String): String { @@ -246,7 +238,7 @@ class MediaDownloader : Feature("MediaDownloader", loadParams = FeatureLoadParam val author = context.database.getFriendInfo(senderId) ?: return val authorUsername = author.usernameForSorting!! - downloadOperaMedia(provideClientDownloadManager( + downloadOperaMedia(provideDownloadManagerClient( pathSuffix = authorUsername, mediaIdentifier = "$conversationId$senderId${conversationMessage.server_message_id}", mediaDisplaySource = authorUsername, @@ -286,7 +278,7 @@ class MediaDownloader : Feature("MediaDownloader", loadParams = FeatureLoadParam ) ?: throw Exception("Friend not found in database") val authorName = author.usernameForSorting!! - downloadOperaMedia(provideClientDownloadManager( + downloadOperaMedia(provideDownloadManagerClient( pathSuffix = authorName, mediaIdentifier = paramMap["MEDIA_ID"].toString(), mediaDisplaySource = authorName, @@ -305,7 +297,7 @@ class MediaDownloader : Feature("MediaDownloader", loadParams = FeatureLoadParam "[^\\x00-\\x7F]".toRegex(), "") - downloadOperaMedia(provideClientDownloadManager( + downloadOperaMedia(provideDownloadManagerClient( pathSuffix = "Public-Stories/$userDisplayName", mediaIdentifier = paramMap["SNAP_ID"].toString(), mediaDisplayType = userDisplayName, @@ -316,7 +308,7 @@ class MediaDownloader : Feature("MediaDownloader", loadParams = FeatureLoadParam //spotlight if (snapSource == "SINGLE_SNAP_STORY" && (forceDownload || canAutoDownload("spotlight"))) { - downloadOperaMedia(provideClientDownloadManager( + downloadOperaMedia(provideDownloadManagerClient( pathSuffix = "Spotlight", mediaIdentifier = paramMap["SNAP_ID"].toString(), mediaDisplayType = MediaFilter.SPOTLIGHT.mediaDisplayType, @@ -328,11 +320,6 @@ class MediaDownloader : Feature("MediaDownloader", loadParams = FeatureLoadParam //stories with mpeg dash media //TODO: option to download multiple chapters if (paramMap.containsKey("LONGFORM_VIDEO_PLAYLIST_ITEM") && forceDownload) { - if (!isFFmpegPresent) { - context.shortToast("Can't download media. ffmpeg was not found") - return - } - val storyName = paramMap["STORY_NAME"].toString().replace( "[^\\x00-\\x7F]".toRegex(), "") @@ -361,7 +348,7 @@ class MediaDownloader : Feature("MediaDownloader", loadParams = FeatureLoadParam } } - provideClientDownloadManager( + provideDownloadManagerClient( pathSuffix = "Pro-Stories/${storyName}", mediaIdentifier = "${paramMap["STORY_ID"]}-${snapItem.snapId}", mediaDisplaySource = storyName, @@ -396,10 +383,12 @@ class MediaDownloader : Feature("MediaDownloader", loadParams = FeatureLoadParam val mediaInfoMap = mutableMapOf() val isVideo = mediaParamMap.containsKey("video_media_info_list") + val canMergeOverlay = context.config.downloader.autoDownloadOptions.get().contains("merge_overlay") + mediaInfoMap[SplitMediaAssetType.ORIGINAL] = MediaInfo( (if (isVideo) mediaParamMap["video_media_info_list"] else mediaParamMap["image_media_info"])!! ) - if (canMergeOverlay() && mediaParamMap.containsKey("overlay_image_media_info")) { + if (canMergeOverlay && mediaParamMap.containsKey("overlay_image_media_info")) { mediaInfoMap[SplitMediaAssetType.OVERLAY] = MediaInfo(mediaParamMap["overlay_image_media_info"]!!) } @@ -483,7 +472,7 @@ class MediaDownloader : Feature("MediaDownloader", loadParams = FeatureLoadParam runCatching { if (!isPreview) { val encryptionKeys = EncryptionHelper.getEncryptionKeys(contentType, messageReader, isArroyo = isArroyoMessage) - provideClientDownloadManager( + provideDownloadManagerClient( pathSuffix = authorName, mediaIdentifier = "${message.client_conversation_id}${message.sender_id}${message.server_message_id}", mediaDisplaySource = authorName, diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadListAdapter.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadListAdapter.kt index 60231301e..20acf5b64 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadListAdapter.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadListAdapter.kt @@ -27,7 +27,7 @@ import me.rhunk.snapenhance.SharedContext import me.rhunk.snapenhance.core.R import me.rhunk.snapenhance.data.FileType import me.rhunk.snapenhance.download.data.PendingDownload -import me.rhunk.snapenhance.download.enums.DownloadStage +import me.rhunk.snapenhance.download.data.DownloadStage import me.rhunk.snapenhance.util.snap.PreviewUtils import java.io.File import java.io.FileInputStream From 910cbd0bbadd45690c673aa81cdd3501be6cc95e Mon Sep 17 00:00:00 2001 From: rhunk <101876869+rhunk@users.noreply.github.com> Date: Fri, 4 Aug 2023 13:17:21 +0200 Subject: [PATCH 20/81] fix: friend menu injection --- .../rhunk/snapenhance/database/DatabaseAccess.kt | 16 ++++++++++++++++ .../rhunk/snapenhance/features/impl/Messaging.kt | 4 +++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/database/DatabaseAccess.kt b/core/src/main/kotlin/me/rhunk/snapenhance/database/DatabaseAccess.kt index 4a7bc9b0a..7815f48ff 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/database/DatabaseAccess.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/database/DatabaseAccess.kt @@ -141,6 +141,22 @@ class DatabaseAccess(private val context: ModContext) : Manager { } } + fun getConversationType(conversationId: String): Int? { + return safeDatabaseOperation(openArroyo()) { + val cursor = it.rawQuery( + "SELECT * FROM user_conversation WHERE client_conversation_id = ?", + arrayOf(conversationId) + ) + if (!cursor.moveToFirst()) { + cursor.close() + return@safeDatabaseOperation null + } + val type = cursor.getInt(cursor.getColumnIndex("conversation_type")) + cursor.close() + type + } + } + fun getDMConversationIdFromUserId(userId: String): UserConversationLink? { return safeDatabaseOperation(openArroyo()) { readDatabaseObject( diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/Messaging.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/Messaging.kt index d00cce3e0..80f078db6 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/Messaging.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/Messaging.kt @@ -33,7 +33,9 @@ class Messaging : Feature("Messaging", loadParams = FeatureLoadParams.ACTIVITY_C val conversationId = viewItem.substringAfter("conversationId: ").substring(0, 36).also { if (it.startsWith("null")) return@hook } - lastFetchGroupConversationUUID = SnapUUID.fromString(conversationId) + context.database.getConversationType(conversationId)?.takeIf { it == 1 }?.run { + lastFetchGroupConversationUUID = SnapUUID.fromString(conversationId) + } } } From 9aef7a1b86e66fde70e31805ace4cff36025542a Mon Sep 17 00:00:00 2001 From: rhunk <101876869+rhunk@users.noreply.github.com> Date: Fri, 4 Aug 2023 13:18:45 +0200 Subject: [PATCH 21/81] build: proguard rules - minify release build - upgrade compose --- app/build.gradle.kts | 33 ++++++++++++------- app/proguard-rules.pro | 2 ++ .../snapenhance/ui/manager/MainActivity.kt | 6 +--- .../sections/download/DownloadSection.kt | 3 +- gradle/libs.versions.toml | 2 +- 5 files changed, 28 insertions(+), 18 deletions(-) create mode 100644 app/proguard-rules.pro diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 5ead87a79..2b5b93a35 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -28,31 +28,42 @@ android { buildTypes { release { + isMinifyEnabled = true + proguardFiles += file("proguard-rules.pro") + } + debug { + isDebuggable = true isMinifyEnabled = false - isShrinkResources = false } } flavorDimensions += "abi" + //noinspection ChromeOsAbiSupport productFlavors { - - + packaging { + jniLibs { + excludes += "**/*_neon.so" + } + resources { + excludes += "DebugProbesKt.bin" + excludes += "okhttp3/internal/publicsuffix/**" + excludes += "META-INF/*.version" + excludes += "META-INF/services/**" + excludes += "META-INF/*.kotlin_builtins" + excludes += "META-INF/*.kotlin_module" + } + } create("armv8") { ndk { - abiFilters.add("arm64-v8a") + abiFilters += "arm64-v8a" } dimension = "abi" } create("armv7") { ndk { - abiFilters.add("armeabi-v7a") - } - packaging { - jniLibs { - excludes += "**/*_neon.so" - } + abiFilters += "armeabi-v7a" } dimension = "abi" } @@ -89,7 +100,7 @@ dependencies { implementation(libs.gson) debugImplementation("androidx.compose.ui:ui-tooling:1.4.3") - debugImplementation("androidx.compose.ui:ui-tooling-preview:1.4.3") + implementation("androidx.compose.ui:ui-tooling-preview:1.4.3") implementation(kotlin("reflect")) } diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 000000000..09acd6cf7 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,2 @@ +-dontwarn de.robv.android.xposed.** +-keep class me.rhunk.** { *; } \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/MainActivity.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/MainActivity.kt index 9b7ea8a4f..06d61005e 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/MainActivity.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/MainActivity.kt @@ -28,11 +28,7 @@ class MainActivity : ComponentActivity() { } sections = EnumSection.values().toList().associateWith { - runCatching { - it.section.constructors.first().call() - }.onFailure { - it.printStackTrace() - }.getOrThrow() + it.section.constructors.first().call() }.onEach { (section, instance) -> with(instance) { enumSection = section diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/download/DownloadSection.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/download/DownloadSection.kt index b695bd59c..89ca1313c 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/download/DownloadSection.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/download/DownloadSection.kt @@ -2,10 +2,11 @@ package me.rhunk.snapenhance.ui.manager.sections.download import androidx.compose.runtime.Composable import me.rhunk.snapenhance.ui.manager.Section +import me.rhunk.snapenhance.ui.manager.sections.NotImplemented class DownloadSection : Section() { @Composable override fun Content() { - + NotImplemented() } } \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2433cc528..81639f1cb 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -6,7 +6,7 @@ kotlin = "1.8.22" kotlinx-coroutines-android = "1.7.2" kotlin-reflect = "1.8.22" material-icons-extended = "1.6.0-alpha03" -navigation-compose = "2.6.0" +navigation-compose = "2.7.0-rc01" recyclerview = "1.3.1" gson = "2.10.1" ffmpeg-kit = "5.1.LTS" From c88abd70d426abc95ace01e6aa6f22f10a70f69f Mon Sep 17 00:00:00 2001 From: rhunk <101876869+rhunk@users.noreply.github.com> Date: Fri, 4 Aug 2023 16:49:11 +0200 Subject: [PATCH 22/81] feat: property translation - add top bar --- .../snapenhance/ui/manager/MainActivity.kt | 7 +- .../snapenhance/ui/manager/Navigation.kt | 56 +- .../rhunk/snapenhance/ui/manager/Section.kt | 3 + .../ui/manager/sections/HomeSection.kt | 11 +- .../sections/features/FeaturesSection.kt | 123 +++-- core/src/main/assets/lang/en_US.json | 477 ++++++++++-------- .../core/config/ConfigContainer.kt | 11 +- .../snapenhance/core/config/ConfigObjects.kt | 17 +- .../core/config/impl/UserInterfaceTweaks.kt | 3 +- 9 files changed, 404 insertions(+), 304 deletions(-) diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/MainActivity.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/MainActivity.kt index 06d61005e..1cb159f40 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/MainActivity.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/MainActivity.kt @@ -39,15 +39,14 @@ class MainActivity : ComponentActivity() { setContent { val navController = rememberNavController() - val navigation = remember { Navigation() } + val navigation = remember { Navigation(sections, navController) } AppMaterialTheme { Scaffold( containerColor = MaterialTheme.colorScheme.background, - bottomBar = { navigation.NavBar(navController = navController) } + topBar = { navigation.TopBar() }, + bottomBar = { navigation.NavBar() } ) { innerPadding -> navigation.NavigationHost( - sections = sections, - navController = navController, innerPadding = innerPadding, startDestination = startDestination ) diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/Navigation.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/Navigation.kt index faf8d88d1..3364a280d 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/Navigation.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/Navigation.kt @@ -6,15 +6,20 @@ import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.requiredWidth +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton import androidx.compose.material3.NavigationBar import androidx.compose.material3.NavigationBarItem import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp -import androidx.navigation.NavController +import androidx.navigation.NavDestination import androidx.navigation.NavDestination.Companion.hierarchy import androidx.navigation.NavGraph.Companion.findStartDestination import androidx.navigation.NavHostController @@ -22,30 +27,55 @@ import androidx.navigation.compose.NavHost import androidx.navigation.compose.currentBackStackEntryAsState -class Navigation{ +class Navigation( + private val sections: Map, + private val navHostController: NavHostController +){ @Composable fun NavigationHost( - sections: Map, startDestination: EnumSection, - navController: NavHostController, innerPadding: PaddingValues ) { - NavHost(navController, startDestination = startDestination.route, Modifier.padding(innerPadding)) { + NavHost(navHostController, startDestination = startDestination.route, Modifier.padding(innerPadding)) { sections.forEach { (_, instance) -> - instance.navController = navController + instance.navController = navHostController instance.build(this) } } } + private fun getCurrentSection(navDestination: NavDestination) = sections.firstNotNullOf { (section, instance) -> + if (navDestination.hierarchy.any { it.route == section.route }) { + instance + } else { + null + } + } + + @OptIn(ExperimentalMaterial3Api::class) @Composable - fun NavBar( - navController: NavController - ) { + fun TopBar() { + val navBackStackEntry by navHostController.currentBackStackEntryAsState() + val currentDestination = navBackStackEntry?.destination ?: return + val currentSection = getCurrentSection(currentDestination) + + TopAppBar(title = { + Text(text = currentSection.sectionTopBarName()) + }, navigationIcon = { + if (currentSection.canGoBack()) { + IconButton(onClick = { navHostController.popBackStack() }) { + Icon(Icons.Filled.ArrowBack, contentDescription = null) + } + } + }) + } + + @Composable + fun NavBar() { NavigationBar { - val navBackStackEntry by navController.currentBackStackEntryAsState() + val navBackStackEntry by navHostController.currentBackStackEntryAsState() val currentDestination = navBackStackEntry?.destination - EnumSection.values().toList().forEach { section -> + sections.keys.forEach { section -> fun selected() = currentDestination?.hierarchy?.any { it.route == section.route } == true NavigationBarItem( @@ -73,8 +103,8 @@ class Navigation{ }, selected = selected(), onClick = { - navController.navigate(section.route) { - popUpTo(navController.graph.findStartDestination().id) { + navHostController.navigate(section.route) { + popUpTo(navHostController.graph.findStartDestination().id) { saveState = true } launchSingleTop = true diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/Section.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/Section.kt index 756a44229..9d270bfb9 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/Section.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/Section.kt @@ -70,6 +70,9 @@ open class Section { open fun init() {} open fun onResumed() {} + open fun sectionTopBarName(): String = context.translation["manager.routes.${enumSection.route}"] + open fun canGoBack(): Boolean = false + @Composable open fun Content() { NotImplemented() } diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/HomeSection.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/HomeSection.kt index bf514f4ed..b7df0d30f 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/HomeSection.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/HomeSection.kt @@ -23,8 +23,6 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import me.rhunk.snapenhance.Logger import me.rhunk.snapenhance.ui.manager.Section import me.rhunk.snapenhance.ui.manager.data.InstallationSummary import me.rhunk.snapenhance.ui.setup.Requirements @@ -91,13 +89,14 @@ class HomeSection : Section() { } override fun onResumed() { - Logger.debug("HomeSection resumed") if (!context.mappings.isMappingsLoaded()) { context.mappings.init() } installationSummary.value = context.getInstallationSummary() } + override fun sectionTopBarName() = "SnapEnhance" + @Composable @Preview override fun Content() { @@ -106,12 +105,6 @@ class HomeSection : Section() { .fillMaxSize() .verticalScroll(ScrollState(0)) ) { - Text( - "SnapEnhance", - fontSize = 32.sp, - modifier = Modifier.padding(32.dp) - ) - Text( text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec euismod, nisl eget ultricies ultrices, nunc nisl aliquam nunc, quis aliquam nisl nunc eu nisl. Donec euismod, nisl eget ultricies ultrices, nunc nisl aliquam nunc, quis aliquam nisl nunc eu nisl.", modifier = Modifier.padding(16.dp) diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/features/FeaturesSection.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/features/FeaturesSection.kt index 9f5003050..cb27b0eb2 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/features/FeaturesSection.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/features/FeaturesSection.kt @@ -6,6 +6,7 @@ import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize @@ -20,7 +21,6 @@ import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.MaterialTheme import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material.icons.filled.FolderOpen import androidx.compose.material.icons.filled.OpenInNew import androidx.compose.material.icons.rounded.Save @@ -34,7 +34,6 @@ import androidx.compose.material3.Scaffold import androidx.compose.material3.SnackbarHost import androidx.compose.material3.Switch import androidx.compose.material3.Text -import androidx.compose.material3.TopAppBar import androidx.compose.material3.rememberBottomSheetScaffoldState import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateOf @@ -43,7 +42,6 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp @@ -63,18 +61,75 @@ class FeaturesSection : Section() { private val dialogs by lazy { Dialogs() } companion object { - private const val MAIN_ROUTE = "root" + const val MAIN_ROUTE = "feature_root" + const val FEATURE_CONTAINER_ROOT = "feature_container/{name}" } private lateinit var openFolderCallback: (uri: String) -> Unit private lateinit var openFolderLauncher: () -> Unit + private val featuresRouteName by lazy { context.translation["manager.routes.features"] } + + private val allContainers by lazy { + val containers = mutableMapOf>() + fun queryContainerRecursive(container: ConfigContainer) { + container.properties.forEach { + if (it.key.dataType.type == DataProcessors.Type.CONTAINER) { + containers[it.key.name] = PropertyPair(it.key, it.value) + queryContainerRecursive(it.value.get() as ConfigContainer) + } + } + } + queryContainerRecursive(context.config.root) + containers + } + override fun init() { openFolderLauncher = ChooseFolderHelper.createChooseFolder(context.activity!! as ComponentActivity) { openFolderCallback(it) } } + override fun canGoBack() = sectionTopBarName() != featuresRouteName + + override fun sectionTopBarName(): String { + navController.currentBackStackEntry?.arguments?.getString("name")?.let { routeName -> + val currentContainerPair = allContainers[routeName] + val propertyTree = run { + var key = currentContainerPair?.key + val tree = mutableListOf() + while (key != null) { + tree.add(key.propertyTranslationPath()) + key = key.parentKey + } + tree + } + + val translatedKey = propertyTree.reversed().joinToString(" > ") { + context.translation["$it.name"] + } + + return "$featuresRouteName > $translatedKey" + } + return featuresRouteName + } + + override fun build(navGraphBuilder: NavGraphBuilder) { + navGraphBuilder.navigation(route = "features", startDestination = MAIN_ROUTE) { + composable(MAIN_ROUTE) { + Container(context.config.root) + } + + composable(FEATURE_CONTAINER_ROOT) { backStackEntry -> + backStackEntry.arguments?.getString("name")?.let { containerName -> + allContainers[containerName]?.let { + Container(it.value.get() as ConfigContainer) + } + } + } + } + } + @Composable private fun PropertyAction(property: PropertyPair<*>, registerClickCallback: RegisterClickCallback) { val showDialog = remember { mutableStateOf(false) } @@ -130,7 +185,7 @@ class FeaturesSection : Section() { overflow = TextOverflow.Ellipsis, maxLines = 1, modifier = Modifier.widthIn(0.dp, 120.dp), - text = (propertyValue.getNullable() as? String) ?: "Disabled", + text = (propertyValue.getNullable() as? String) ?: context.translation["manager.features.disabled"], ) } @@ -168,7 +223,7 @@ class FeaturesSection : Section() { val container = propertyValue.get() as ConfigContainer registerClickCallback { - navController.navigate("container/${property.name}") + navController.navigate(FEATURE_CONTAINER_ROOT.replace("{name}", property.name)) } if (container.globalState == null) return @@ -183,7 +238,10 @@ class FeaturesSection : Section() { Box(modifier = Modifier .height(50.dp) .width(1.dp) - .background(color = MaterialTheme.colors.onBackground.copy(alpha = 0.12f), shape = RoundedCornerShape(5.dp))) + .background( + color = MaterialTheme.colors.onBackground.copy(alpha = 0.12f), + shape = RoundedCornerShape(5.dp) + )) } Switch( @@ -222,12 +280,12 @@ class FeaturesSection : Section() { .padding(all = 10.dp) ) { Text( - text = property.name, + text = context.translation["${property.key.propertyTranslationPath()}.name"], fontSize = 16.sp, fontWeight = FontWeight.Bold ) Text( - text = property.name, + text = context.translation["${property.key.propertyTranslationPath()}.description"], fontSize = 12.sp, lineHeight = 15.sp ) @@ -252,7 +310,6 @@ class FeaturesSection : Section() { @OptIn(ExperimentalMaterial3Api::class) @Composable private fun Container( - containerName: String, configContainer: ConfigContainer ) { val properties = remember { @@ -264,20 +321,6 @@ class FeaturesSection : Section() { Scaffold( snackbarHost = { SnackbarHost(scaffoldState.snackbarHostState) }, modifier = Modifier.fillMaxSize(), - topBar = { - TopAppBar( - title = { - Text(text = containerName, textAlign = TextAlign.Center) - }, - navigationIcon = { - if (navController.currentBackStackEntry?.destination?.route != MAIN_ROUTE) { - IconButton(onClick = { navController.popBackStack() }) { - Icon(Icons.Filled.ArrowBack, contentDescription = null) - } - } - } - ) - }, floatingActionButton = { FloatingActionButton( onClick = { @@ -302,6 +345,8 @@ class FeaturesSection : Section() { modifier = Modifier .fillMaxHeight() .padding(innerPadding), + //save button space + contentPadding = PaddingValues(top = 10.dp, bottom = 110.dp), verticalArrangement = Arrangement.Center ) { items(properties) { @@ -310,36 +355,6 @@ class FeaturesSection : Section() { } } ) - } - override fun build(navGraphBuilder: NavGraphBuilder) { - val allContainers by lazy { - val containers = mutableMapOf() - fun queryContainerRecursive(container: ConfigContainer) { - container.properties.forEach { - if (it.key.dataType.type == DataProcessors.Type.CONTAINER) { - containers[it.key.name] = it.value.get() as ConfigContainer - queryContainerRecursive(it.value.get() as ConfigContainer) - } - } - } - queryContainerRecursive(context.config.root) - containers - } - - navGraphBuilder.navigation(route = "features", startDestination = MAIN_ROUTE) { - composable(MAIN_ROUTE) { - Container(MAIN_ROUTE, context.config.root) - } - - composable("container/{name}") { backStackEntry -> - backStackEntry.arguments?.getString("name")?.let { containerName -> - allContainers[containerName]?.let { - Container(containerName, it) - } - } - } - } - } } \ No newline at end of file diff --git a/core/src/main/assets/lang/en_US.json b/core/src/main/assets/lang/en_US.json index c4d7aa762..4eb9c5a44 100644 --- a/core/src/main/assets/lang/en_US.json +++ b/core/src/main/assets/lang/en_US.json @@ -16,13 +16,16 @@ } }, - "category": { - "spying_privacy": "Spying & Privacy", - "media_manager": "Media Manager", - "ui_tweaks": "UI & Tweaks", - "camera": "Camera", - "updates": "Updates", - "experimental_debugging": "Experimental" + "manager": { + "routes": { + "downloads": "Downloads", + "features": "Features", + "friends": "Friends", + "debug": "Debug" + }, + "features": { + "disabled": "Disabled" + } }, "action": { @@ -34,228 +37,270 @@ "export_chat_messages": "Export chat messages" }, - "property": { - "message_logger": { - "name": "Message Logger", - "description": "Prevents messages from being deleted" - }, - "prevent_read_receipts": { - "name": "Prevent Read Receipts", - "description": "Prevent anyone from knowing you've opened their Snaps" - }, - "hide_bitmoji_presence": { - "name": "Hide Bitmoji Presence", - "description": "Hides your Bitmoji presence from the chat" - }, - "better_notifications": { - "name": "Better Notifications", - "description": "Shows more information in notifications" - }, - "notification_blacklist": { - "name": "Notification Blacklist", - "description": "Hides selected notification type" + "features": { + "spoof": { + "name": "Spoof", + "description": "Spoof your information", + "properties": { + "location": { + "name": "Location", + "description": "Spoof your location" + }, + "device": { + "name": "Device", + "description": "Spoof your device" + } + } }, - "disable_metrics": { - "name": "Disable Metrics", - "description": "Disables metrics sent to Snapchat" + "downloader": { + "name": "Downloader", + "description": "Download Snaps and Stories", + "properties": { + "save_folder": { + "name": "Save Folder", + "description": "The directory where all media is saved" + }, + "auto_download_options": { + "name": "Auto Download Options", + "description": "Select which medias to auto download" + } + } }, - "block_ads": { - "name": "Block Ads", - "description": "Blocks ads from being displayed" + "user_interface": { + "name": "User Interface", + "description": "Change the look and feel of Snapchat", + "properties": { + "enable_app_appearance": { + "name": "Enable App Appearance Settings", + "description": "Enables the hidden app appearance settings" + }, + "amoled_dark_mode": { + "name": "AMOLED Dark Mode", + "description": "Enables AMOLED dark mode\nMake sure Snapchat's dark mode is enabled" + }, + "map_friend_nametags": { + "name": "Enhanced Friend Map Nametags", + "description": "Enhances the nametags of friends on the map" + }, + "streak_expiration_info": { + "name": "Show Streak Expiration Info", + "description": "Shows Streak expiration info next to streaks" + }, + "hide_story_section": { + "name": "Hide Story Section", + "description": "Hide certain UI Elements shown in the story section" + }, + "hide_ui_components": { + "name": "Hide UI Components", + "description": "Select which UI components to hide" + }, + "disable_spotlight": { + "name": "Disable Spotlight", + "description": "Disables the Spotlight page" + }, + "startup_tab": { + "name": "Startup Tab", + "description": "Change the tab that opens on startup" + }, + "story_viewer_override": { + "name": "Story Viewer Override", + "description": "Turns on certain features which Snapchat hid" + }, + "friend_feed_menu_buttons": { + "name": "Friend Feed Menu Buttons", + "description": "Select which buttons to show in the Friend Feed Menu Bar" + }, + "friend_feed_menu_position": { + "name": "Friend Feed Position Index", + "description": "The position of the Friend Feed Menu component" + }, + "enable_friend_feed_menu_bar": { + "name": "Friend Feed Menu Bar", + "description": "Enables the new Friend Feed Menu Bar" + } + } }, - "unlimited_snap_view_time": { - "name": "Unlimited Snap View Time", - "description": "Removes the time limit for viewing Snaps" + "messaging": { + "name": "Messaging", + "description": "Change how you interact with friends" }, - "prevent_sending_messages": { - "name": "Prevent Sending Messages", - "description": "Prevents sending certain types of messages" + "global": { + "name": "Global", + "description": "Tweak Snapchat globally" }, - "anonymous_story_view": { - "name": "Anonymous Story View", - "description": "Prevents anyone from knowing you've seen their story" + "camera": { + "name": "Camera", + "description": "Adjust the right settings for the perfect snap" }, - "hide_typing_notification": { - "name": "Hide Typing Notification", - "description": "Prevents typing notifications being sent" + "experimental": { + "name": "Experimental", + "description": "Experimental features" }, - "save_folder": { - "name": "Save Folder", - "description": "The directory where all media is saved" - }, - "auto_download_options": { - "name": "Auto Download Options", - "description": "Select which medias to auto download" - }, - "download_options": { - "name": "Download Options", - "description": "Specify the file path format" - }, - "chat_download_context_menu": { - "name": "Chat Download Context Menu", - "description": "Enable the chat download context menu" - }, - "gallery_media_send_override": { - "name": "Gallery Media Send Override", - "description": "Overrides media sent from the gallery" - }, - "auto_save_messages": { - "name": "Auto Save Messages", - "description": "Select which type of messages to auto save" - }, - "force_media_source_quality": { - "name": "Force Media Source Quality", - "description": "Overrides the media source quality" - }, - "download_logging": { - "name": "Download Logging", - "description": "Show a toast when media is downloading" - }, + "properties": { + "message_logger": { + "name": "Message Logger", + "description": "Prevents messages from being deleted" + }, + "prevent_read_receipts": { + "name": "Prevent Read Receipts", + "description": "Prevent anyone from knowing you've opened their Snaps" + }, + "hide_bitmoji_presence": { + "name": "Hide Bitmoji Presence", + "description": "Hides your Bitmoji presence from the chat" + }, + "better_notifications": { + "name": "Better Notifications", + "description": "Shows more information in notifications" + }, + "notification_blacklist": { + "name": "Notification Blacklist", + "description": "Hides selected notification type" + }, + "disable_metrics": { + "name": "Disable Metrics", + "description": "Disables metrics sent to Snapchat" + }, + "block_ads": { + "name": "Block Ads", + "description": "Blocks ads from being displayed" + }, + "unlimited_snap_view_time": { + "name": "Unlimited Snap View Time", + "description": "Removes the time limit for viewing Snaps" + }, + "prevent_sending_messages": { + "name": "Prevent Sending Messages", + "description": "Prevents sending certain types of messages" + }, + "anonymous_story_view": { + "name": "Anonymous Story View", + "description": "Prevents anyone from knowing you've seen their story" + }, + "hide_typing_notification": { + "name": "Hide Typing Notification", + "description": "Prevents typing notifications being sent" + }, - "enable_friend_feed_menu_bar": { - "name": "Friend Feed Menu Bar", - "description": "Enables the new Friend Feed Menu Bar" - }, - "friend_feed_menu_buttons": { - "name": "Friend Feed Menu Buttons", - "description": "Select which buttons to show in the Friend Feed Menu Bar" - }, - "friend_feed_menu_buttons_position": { - "name": "Friend Feed Buttons Position Index", - "description": "The position of the Friend Feed Menu Buttons" - }, - "hide_ui_elements": { - "name": "Hide UI Elements", - "description": "Select which UI elements to hide" - }, - "hide_story_section": { - "name": "Hide Story Section", - "description": "Hide certain UI Elements shown in the story section" - }, - "story_viewer_override": { - "name": "Story Viewer Override", - "description": "Turns on certain features which Snapchat hid" - }, - "streak_expiration_info": { - "name": "Show Streak Expiration Info", - "description": "Shows Streak expiration info next to streaks" - }, - "disable_snap_splitting": { - "name": "Disable Snap Splitting", - "description": "Prevents Snaps from being split into multiple parts" - }, - "disable_video_length_restriction": { - "name": "Disable Video Length Restriction", - "description": "Disables video length restrictions" - }, - "snapchat_plus": { - "name": "Snapchat Plus", - "description": "Enables Snapchat Plus features" - }, - "new_map_ui": { - "name": "New Map UI", - "description": "Enables the new map UI" - }, - "location_spoof": { - "name": "Snapmap Location Spoofer", - "description": "Spoofs your location on the Snapmap" - }, - "message_preview_length": { - "name": "Message Preview Length", - "description": "Specify the amount of messages to be previewed" - }, - "unlimited_conversation_pinning": { - "name": "Unlimited Conversation Pinning", - "description": "Enables the ability to pin unlimited conversations" - }, - "disable_spotlight": { - "name": "Disable Spotlight", - "description": "Disables the Spotlight page" - }, - "enable_app_appearance": { - "name": "Enable App Appearance Settings", - "description": "Enables the hidden app appearance settings" - }, - "startup_page_override": { - "name": "Override Startup Page", - "description": "Overrides the startup page" - }, - "disable_google_play_dialogs": { - "name": "Disable Google Play Services Dialogs", - "description": "Prevent Google Play Services availability dialogs from being shown" - }, + "download_options": { + "name": "Download Options", + "description": "Specify the file path format" + }, + "chat_download_context_menu": { + "name": "Chat Download Context Menu", + "description": "Enable the chat download context menu" + }, + "gallery_media_send_override": { + "name": "Gallery Media Send Override", + "description": "Overrides media sent from the gallery" + }, + "auto_save_messages": { + "name": "Auto Save Messages", + "description": "Select which type of messages to auto save" + }, + "force_media_source_quality": { + "name": "Force Media Source Quality", + "description": "Overrides the media source quality" + }, + "download_logging": { + "name": "Download Logging", + "description": "Show a toast when media is downloading" + }, - "auto_updater": { - "name": "Auto Updater", - "description": "The interval of checking for updates" - }, + "disable_snap_splitting": { + "name": "Disable Snap Splitting", + "description": "Prevents Snaps from being split into multiple parts" + }, + "disable_video_length_restriction": { + "name": "Disable Video Length Restriction", + "description": "Disables video length restrictions" + }, + "snapchat_plus": { + "name": "Snapchat Plus", + "description": "Enables Snapchat Plus features" + }, + "location_spoof": { + "name": "Snapmap Location Spoofer", + "description": "Spoofs your location on the Snapmap" + }, + "message_preview_length": { + "name": "Message Preview Length", + "description": "Specify the amount of messages to be previewed" + }, + "unlimited_conversation_pinning": { + "name": "Unlimited Conversation Pinning", + "description": "Enables the ability to pin unlimited conversations" + }, + "disable_google_play_dialogs": { + "name": "Disable Google Play Services Dialogs", + "description": "Prevent Google Play Services availability dialogs from being shown" + }, - "disable_camera": { - "name": "Disable Camera", - "description": "Prevents Snapchat from being able to use the camera" - }, - "immersive_camera_preview": { - "name": "Immersive Camera Preview", - "description": "Stops Snapchat from cropping the camera preview" - }, - "preview_resolution": { - "name": "Preview Resolution", - "description": "Overrides the camera preview resolution" - }, - "picture_resolution": { - "name": "Picture Resolution", - "description": "Overrides the picture resolution" - }, - "force_highest_frame_rate": { - "name": "Force Highest Frame Rate", - "description": "Forces the highest possible frame rate" - }, - "force_camera_source_encoding": { - "name": "Force Camera Source Encoding", - "description": "Forces the camera source encoding" - }, + "auto_updater": { + "name": "Auto Updater", + "description": "The interval of checking for updates" + }, - "app_passcode": { - "name": "Set App Passcode", - "description": "Sets a passcode to lock the app" - }, - "app_lock_on_resume": { - "name": "App Lock On Resume", - "description": "Locks the app when it's reopened" - }, - "infinite_story_boost": { - "name": "Infinite Story Boost", - "description": "Infinitely boosts your story" - }, - "meo_passcode_bypass": { - "name": "My Eyes Only Passcode Bypass", - "description": "Bypass the My Eyes Only passcode\nThis will only work if the passcode has been entered correctly before" - }, - "amoled_dark_mode": { - "name": "AMOLED Dark Mode", - "description": "Enables AMOLED dark mode\nMake sure Snapchat's dark mode is enabled" - }, - "unlimited_multi_snap": { - "name": "Unlimited Multi Snap", - "description": "Allows you to take an unlimited amount of multi snaps" - }, - "device_spoof": { - "name": "Spoof Device Values", - "description": "Spoofs the devices values" - }, - "device_fingerprint": { - "name": "Device Fingerprint", - "description": "Spoofs the device fingerprint" - }, - "android_id": { - "name": "Android ID", - "description": "Spoofs the devices Android ID" - } - }, + "disable_camera": { + "name": "Disable Camera", + "description": "Prevents Snapchat from being able to use the camera" + }, + "immersive_camera_preview": { + "name": "Immersive Camera Preview", + "description": "Stops Snapchat from cropping the camera preview" + }, + "preview_resolution": { + "name": "Preview Resolution", + "description": "Overrides the camera preview resolution" + }, + "picture_resolution": { + "name": "Picture Resolution", + "description": "Overrides the picture resolution" + }, + "force_highest_frame_rate": { + "name": "Force Highest Frame Rate", + "description": "Forces the highest possible frame rate" + }, + "force_camera_source_encoding": { + "name": "Force Camera Source Encoding", + "description": "Forces the camera source encoding" + }, - "option": { - "property": { + "app_passcode": { + "name": "Set App Passcode", + "description": "Sets a passcode to lock the app" + }, + "app_lock_on_resume": { + "name": "App Lock On Resume", + "description": "Locks the app when it's reopened" + }, + "infinite_story_boost": { + "name": "Infinite Story Boost", + "description": "Infinitely boosts your story" + }, + "meo_passcode_bypass": { + "name": "My Eyes Only Passcode Bypass", + "description": "Bypass the My Eyes Only passcode\nThis will only work if the passcode has been entered correctly before" + }, + "unlimited_multi_snap": { + "name": "Unlimited Multi Snap", + "description": "Allows you to take an unlimited amount of multi snaps" + }, + "device_spoof": { + "name": "Spoof Device Values", + "description": "Spoofs the devices values" + }, + "device_fingerprint": { + "name": "Device Fingerprint", + "description": "Spoofs the device fingerprint" + }, + "android_id": { + "name": "Android ID", + "description": "Spoofs the devices Android ID" + } + }, + "option": { "better_notifications": { "chat": "Show chat messages", "snap": "Show medias", diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/config/ConfigContainer.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/ConfigContainer.kt index 0b9683902..02cbede58 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/core/config/ConfigContainer.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/ConfigContainer.kt @@ -8,16 +8,19 @@ typealias ConfigParamsBuilder = ConfigParams.() -> Unit open class ConfigContainer( var globalState: Boolean? = null ) { + var parentContainerKey: PropertyKey<*>? = null val properties = mutableMapOf, PropertyValue<*>>() private inline fun registerProperty( key: String, type: DataProcessors.PropertyDataProcessor<*>, defaultValue: PropertyValue, - params: ConfigParams.() -> Unit = {} + params: ConfigParams.() -> Unit = {}, + propertyKeyCallback: (PropertyKey<*>) -> Unit = {} ): PropertyValue { - val propertyKey = PropertyKey(key, type, ConfigParams().also { it.params() }) + val propertyKey = PropertyKey({ parentContainerKey }, key, type, ConfigParams().also { it.params() }) properties[propertyKey] = defaultValue + propertyKeyCallback(propertyKey) return defaultValue } @@ -51,7 +54,9 @@ open class ConfigContainer( protected fun container( key: String, container: T - ) = registerProperty(key, DataProcessors.container(container), PropertyValue(container)).get() + ) = registerProperty(key, DataProcessors.container(container), PropertyValue(container)) { + container.parentContainerKey = it + }.get() fun toJson(): JsonObject { val json = JsonObject() diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/config/ConfigObjects.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/ConfigObjects.kt index dbd4ea6a8..052bd5f80 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/core/config/ConfigObjects.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/ConfigObjects.kt @@ -12,7 +12,7 @@ data class PropertyPair( class ConfigParams( var shouldTranslate: Boolean = false, - var hidden: Boolean = false, + var isHidden: Boolean = false, var isFolder: Boolean = false, val disabledKey: String? = null ) @@ -40,9 +40,20 @@ class PropertyValue( operator fun setValue(t: Any?, property: KProperty<*>, t1: T?) = set(t1) } -class PropertyKey( +data class PropertyKey( + private val _parent: () -> PropertyKey<*>?, val name: String, val dataType: DataProcessors.PropertyDataProcessor, val params: ConfigParams = ConfigParams(), -) +) { + val parentKey by lazy { _parent() } + + fun propertyTranslationPath(): String { + return if (parentKey != null) { + "${parentKey!!.propertyTranslationPath()}.properties.$name" + } else { + "features.$name" + } + } +} diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/UserInterfaceTweaks.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/UserInterfaceTweaks.kt index b8f5fd895..9b28e54f4 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/UserInterfaceTweaks.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/impl/UserInterfaceTweaks.kt @@ -27,8 +27,7 @@ class UserInterfaceTweaks : ConfigContainer() { "ngs_search_icon_container" ) val storyViewerOverride = unique("story_viewer_override", "DISCOVER_PLAYBACK_SEEKBAR", "VERTICAL_STORY_VIEWER") - val friendFeedMenuButtons = multiple("friend_feed_menu_buttons", "auto_download_blacklist", "anti_auto_save", "stealth_mode", "conversation_info") - val enableFriendFeedMenuBar = boolean("enable_friend_feed_menu_bar") val friendFeedMenuPosition = integer("friend_feed_menu_position", defaultValue = 1) + val enableFriendFeedMenuBar = boolean("enable_friend_feed_menu_bar") } From 2cb5872ab0875757293901f26195efbfe5c417ab Mon Sep 17 00:00:00 2001 From: rhunk <101876869+rhunk@users.noreply.github.com> Date: Fri, 4 Aug 2023 21:51:33 +0200 Subject: [PATCH 23/81] db cursor helper --- .../me/rhunk/snapenhance/util/DbCursorExt.kt | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 core/src/main/kotlin/me/rhunk/snapenhance/util/DbCursorExt.kt diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/util/DbCursorExt.kt b/core/src/main/kotlin/me/rhunk/snapenhance/util/DbCursorExt.kt new file mode 100644 index 000000000..c7d05106e --- /dev/null +++ b/core/src/main/kotlin/me/rhunk/snapenhance/util/DbCursorExt.kt @@ -0,0 +1,28 @@ +package me.rhunk.snapenhance.util + +import android.database.Cursor + +fun Cursor.getStringOrNull(columnName: String): String? { + val columnIndex = getColumnIndex(columnName) + return if (columnIndex == -1) null else getString(columnIndex) +} + +fun Cursor.getIntOrNull(columnName: String): Int? { + val columnIndex = getColumnIndex(columnName) + return if (columnIndex == -1) null else getInt(columnIndex) +} + +fun Cursor.getLongOrNull(columnName: String): Long? { + val columnIndex = getColumnIndex(columnName) + return if (columnIndex == -1) null else getLong(columnIndex) +} + +fun Cursor.getDoubleOrNull(columnName: String): Double? { + val columnIndex = getColumnIndex(columnName) + return if (columnIndex == -1) null else getDouble(columnIndex) +} + +fun Cursor.getFloatOrNull(columnName: String): Float? { + val columnIndex = getColumnIndex(columnName) + return if (columnIndex == -1) null else getFloat(columnIndex) +} \ No newline at end of file From 581b2df37ee4c89ca1b89d5dc9d27ddcddf506a2 Mon Sep 17 00:00:00 2001 From: rhunk <101876869+rhunk@users.noreply.github.com> Date: Sat, 5 Aug 2023 09:44:26 +0200 Subject: [PATCH 24/81] fix: shared context weak reference --- .../me/rhunk/snapenhance/RemoteSideContext.kt | 19 +++---------------- .../rhunk/snapenhance/SharedContextHolder.kt | 13 +++++-------- 2 files changed, 8 insertions(+), 24 deletions(-) diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/RemoteSideContext.kt b/app/src/main/kotlin/me/rhunk/snapenhance/RemoteSideContext.kt index 667f5534c..36b256c37 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/RemoteSideContext.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/RemoteSideContext.kt @@ -18,19 +18,10 @@ import java.lang.ref.WeakReference import kotlin.system.exitProcess class RemoteSideContext( - ctx: Context + val androidContext: Context ) { - private var _context: WeakReference = WeakReference(ctx) private var _activity: WeakReference? = null - var androidContext: Context - get() = synchronized(this) { - _context.get() ?: error("Context is null") - } - set(value) { synchronized(this) { - _context.clear(); _context = WeakReference(value) - } } - var activity: Activity? get() = _activity?.get() set(value) { _activity?.clear(); _activity = WeakReference(value) } @@ -99,13 +90,9 @@ class RemoteSideContext( if (currentContext !is Activity) { addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) currentContext.startActivity(this) - return@apply + exitProcess(0) } - currentContext.startActivityForResult(this, 22) - } - - if (currentContext !is Activity) { - exitProcess(0) + currentContext.startActivity(this) } } } \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/SharedContextHolder.kt b/app/src/main/kotlin/me/rhunk/snapenhance/SharedContextHolder.kt index 738639f75..31aeb5d87 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/SharedContextHolder.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/SharedContextHolder.kt @@ -1,19 +1,16 @@ package me.rhunk.snapenhance import android.content.Context +import java.lang.ref.WeakReference object SharedContextHolder { - private lateinit var _remoteSideContext: RemoteSideContext + private lateinit var _remoteSideContext: WeakReference fun remote(context: Context): RemoteSideContext { - if (!::_remoteSideContext.isInitialized) { - _remoteSideContext = RemoteSideContext(context) + if (!::_remoteSideContext.isInitialized || _remoteSideContext.get() == null) { + _remoteSideContext = WeakReference(RemoteSideContext(context)) } - if (_remoteSideContext.androidContext != context) { - _remoteSideContext.androidContext = context - } - - return _remoteSideContext + return _remoteSideContext.get()!! } } \ No newline at end of file From cd4f3c29c4a5a39a3e52de61307fe863a6f098c7 Mon Sep 17 00:00:00 2001 From: rhunk <101876869+rhunk@users.noreply.github.com> Date: Sat, 5 Aug 2023 09:55:43 +0200 Subject: [PATCH 25/81] fix: pick language screen save config --- .../snapenhance/ui/setup/SetupActivity.kt | 1 + .../ui/setup/screens/SetupScreen.kt | 1 + .../setup/screens/impl/PickLanguageScreen.kt | 63 ++++++++++++------- .../bridge/wrapper/LocaleWrapper.kt | 9 +-- 4 files changed, 44 insertions(+), 30 deletions(-) diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/SetupActivity.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/SetupActivity.kt index 7e4656ba3..5e0645d77 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/SetupActivity.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/SetupActivity.kt @@ -83,6 +83,7 @@ class SetupActivity : ComponentActivity() { fun nextScreen() { if (!canGoNext.value) return + requiredScreens.firstOrNull()?.onLeave() if (requiredScreens.size > 1) { canGoNext.value = false requiredScreens.removeFirst() diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/SetupScreen.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/SetupScreen.kt index 5b0375a4d..700196c40 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/SetupScreen.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/SetupScreen.kt @@ -25,6 +25,7 @@ abstract class SetupScreen { } open fun init() {} + open fun onLeave() {} @Composable abstract fun Content() diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/impl/PickLanguageScreen.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/impl/PickLanguageScreen.kt index b9422b0b1..433088a1b 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/impl/PickLanguageScreen.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/impl/PickLanguageScreen.kt @@ -12,53 +12,69 @@ import androidx.compose.foundation.lazy.items import androidx.compose.foundation.rememberScrollState import androidx.compose.material.Surface import androidx.compose.material.Text +import androidx.compose.material3.Button import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.OutlinedButton import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.window.Dialog import me.rhunk.snapenhance.bridge.wrapper.LocaleWrapper -import me.rhunk.snapenhance.ui.util.ObservableMutableState import me.rhunk.snapenhance.ui.setup.screens.SetupScreen +import me.rhunk.snapenhance.ui.util.ObservableMutableState import java.util.Locale class PickLanguageScreen : SetupScreen(){ - @Composable - override fun Content() { - val androidContext = LocalContext.current - val availableLocales = remember { LocaleWrapper.fetchAvailableLocales(androidContext) } + private val availableLocales by lazy { + LocaleWrapper.fetchAvailableLocales(context.androidContext) + } - allowNext(true) + private lateinit var selectedLocale: ObservableMutableState - fun getLocaleDisplayName(locale: String): String { - locale.split("_").let { - return Locale(it[0], it[1]).getDisplayName(Locale.getDefault()) - } + private fun getLocaleDisplayName(locale: String): String { + locale.split("_").let { + return Locale(it[0], it[1]).getDisplayName(Locale.getDefault()) } + } - val selectedLocale = remember { - val deviceLocale = Locale.getDefault().toString() - fun reloadTranslation(selectedLocale: String) { - context.translation.reloadFromContext(androidContext, selectedLocale) - } + private fun reloadTranslation(selectedLocale: String) { + context.translation.reloadFromContext(context.androidContext, selectedLocale) + } + + private fun setLocale(locale: String) { + with(context) { + config.locale = locale + config.writeConfig() + translation.reloadFromContext(androidContext, locale) + reloadTranslation(locale) + } + } + + override fun onLeave() { + context.config.locale = selectedLocale.value + context.config.writeConfig() + } + + override fun init() { + val deviceLocale = Locale.getDefault().toString() + selectedLocale = ObservableMutableState( defaultValue = availableLocales.firstOrNull { locale -> locale == deviceLocale } ?: LocaleWrapper.DEFAULT_LOCALE ) { _, newValue -> - context.config.locale = newValue - context.config.writeConfig() - reloadTranslation(newValue) + setLocale(newValue) }.also { reloadTranslation(it.value) } - } + } + + @Composable + override fun Content() { + allowNext(true) DialogText(text = context.translation["setup.dialogs.select_language"]) @@ -105,10 +121,11 @@ class PickLanguageScreen : SetupScreen(){ .fillMaxWidth(), contentAlignment = Alignment.Center ) { - OutlinedButton(onClick = { + Button(onClick = { isDialog.value = true }) { - Text(text = getLocaleDisplayName(selectedLocale.value), fontSize = 16.sp, fontWeight = FontWeight.Light) + Text(text = getLocaleDisplayName(selectedLocale.value), fontSize = 16.sp, + fontWeight = FontWeight.Normal) } } } diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/LocaleWrapper.kt b/core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/LocaleWrapper.kt index 289247602..2577a20ca 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/LocaleWrapper.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/LocaleWrapper.kt @@ -37,16 +37,11 @@ class LocaleWrapper { var userLocale = DEFAULT_LOCALE private val translationMap = linkedMapOf() - private lateinit var _loadedLocaleString: String - val loadedLocale by lazy { - Locale(_loadedLocaleString.substring(0, 2), _loadedLocaleString.substring(3, 5)) - } + lateinit var loadedLocale: Locale private fun load(localePair: LocalePair) { - if (!::_loadedLocaleString.isInitialized) { - _loadedLocaleString = localePair.locale - } + loadedLocale = localePair.locale.let { Locale(it.substring(0, 2), it.substring(3, 5)) } val translations = JsonParser.parseString(localePair.content).asJsonObject if (translations == null || translations.isJsonNull) { From c227dff7348337356d1d479d8df07d9963a258bf Mon Sep 17 00:00:00 2001 From: rhunk <101876869+rhunk@users.noreply.github.com> Date: Sat, 5 Aug 2023 09:58:47 +0200 Subject: [PATCH 26/81] feat: navbar localization --- .../snapenhance/ui/manager/MainActivity.kt | 2 +- .../snapenhance/ui/manager/Navigation.kt | 15 +++++-- .../rhunk/snapenhance/ui/manager/Section.kt | 6 --- .../ui/manager/sections/HomeSection.kt | 39 +++++++++++++++++-- .../sections/features/FeaturesSection.kt | 7 ++-- .../ui/setup/screens/impl/MappingsScreen.kt | 5 +-- core/src/main/assets/lang/en_US.json | 1 + core/src/main/assets/lang/fr_FR.json | 13 +++++++ 8 files changed, 67 insertions(+), 21 deletions(-) diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/MainActivity.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/MainActivity.kt index 1cb159f40..4e43becc6 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/MainActivity.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/MainActivity.kt @@ -39,7 +39,7 @@ class MainActivity : ComponentActivity() { setContent { val navController = rememberNavController() - val navigation = remember { Navigation(sections, navController) } + val navigation = remember { Navigation(managerContext, sections, navController) } AppMaterialTheme { Scaffold( containerColor = MaterialTheme.colorScheme.background, diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/Navigation.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/Navigation.kt index 3364a280d..0d23aad63 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/Navigation.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/Navigation.kt @@ -5,7 +5,7 @@ import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.requiredWidth +import androidx.compose.foundation.layout.wrapContentWidth import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material3.ExperimentalMaterial3Api @@ -18,16 +18,20 @@ import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import androidx.navigation.NavDestination import androidx.navigation.NavDestination.Companion.hierarchy import androidx.navigation.NavGraph.Companion.findStartDestination import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import androidx.navigation.compose.currentBackStackEntryAsState +import me.rhunk.snapenhance.RemoteSideContext class Navigation( + private val context: RemoteSideContext, private val sections: Map, private val navHostController: NavHostController ){ @@ -80,7 +84,6 @@ class Navigation( NavigationBarItem( modifier = Modifier - .requiredWidth(75.dp) .fillMaxHeight(), icon = { val iconOffset by animateDpAsState( @@ -99,7 +102,13 @@ class Navigation( if (selected()) 0.dp else (-5).dp, label = "" ) - Text(text = if (selected()) section.title else "", modifier = Modifier.offset(y = labelOffset)) + Text( + textAlign = TextAlign.Center, + softWrap = false, + fontSize = 12.sp, + modifier = Modifier.wrapContentWidth(unbounded = true).offset(y = labelOffset), + text = if (selected()) context.translation["manager.routes.${section.route}"] else "", + ) }, selected = selected(), onClick = { diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/Section.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/Section.kt index 9d270bfb9..cc5fa1f1a 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/Section.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/Section.kt @@ -20,36 +20,30 @@ import kotlin.reflect.KClass enum class EnumSection( val route: String, - val title: String, val icon: ImageVector, val section: KClass = NotImplemented::class ) { DOWNLOADS( route = "downloads", - title = "Downloads", icon = Icons.Filled.Download, section = DownloadSection::class ), FEATURES( route = "features", - title = "Features", icon = Icons.Filled.Stars, section = FeaturesSection::class ), HOME( route = "home", - title = "Home", icon = Icons.Filled.Home, section = HomeSection::class ), FRIENDS( route = "friends", - title = "Friends", icon = Icons.Filled.Group ), DEBUG( route = "debug", - title = "Debug", icon = Icons.Filled.BugReport ); diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/HomeSection.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/HomeSection.kt index b7df0d30f..0f6673c66 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/HomeSection.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/HomeSection.kt @@ -3,15 +3,16 @@ package me.rhunk.snapenhance.ui.manager.sections import androidx.compose.foundation.ScrollState import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.ExperimentalLayoutApi -import androidx.compose.foundation.layout.FlowRow +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Language import androidx.compose.material.icons.filled.Map +import androidx.compose.material.icons.filled.OpenInNew import androidx.compose.material.icons.filled.Refresh import androidx.compose.material3.Button import androidx.compose.material3.Icon @@ -26,14 +27,15 @@ import androidx.compose.ui.unit.dp import me.rhunk.snapenhance.ui.manager.Section import me.rhunk.snapenhance.ui.manager.data.InstallationSummary import me.rhunk.snapenhance.ui.setup.Requirements +import java.util.Locale class HomeSection : Section() { companion object { val cardMargin = 10.dp } private val installationSummary = mutableStateOf(null as InstallationSummary?) + private val userLocale = mutableStateOf(null as String?) - @OptIn(ExperimentalLayoutApi::class) @Composable private fun SummaryCards(installationSummary: InstallationSummary) { //installation summary @@ -57,7 +59,7 @@ class HomeSection : Section() { .padding(all = cardMargin) .fillMaxWidth() ) { - FlowRow( + Row( modifier = Modifier.padding(all = 16.dp), horizontalArrangement = Arrangement.SpaceBetween ) { @@ -86,6 +88,34 @@ class HomeSection : Section() { } } } + OutlinedCard( + modifier = Modifier + .padding(all = cardMargin) + .fillMaxWidth() + ) { + Row( + modifier = Modifier.padding(all = 16.dp), + ) { + Icon( + Icons.Filled.Language, + contentDescription = "Language", + modifier = Modifier + .padding(end = 10.dp) + .align(Alignment.CenterVertically) + ) + Text(text = userLocale.value ?: "Unknown", modifier = Modifier + .weight(1f) + .align(Alignment.CenterVertically) + ) + + //inline button + Button(onClick = { + context.checkForRequirements(Requirements.LANGUAGE) + }, modifier = Modifier.height(40.dp)) { + Icon(Icons.Filled.OpenInNew, contentDescription = null) + } + } + } } override fun onResumed() { @@ -93,6 +123,7 @@ class HomeSection : Section() { context.mappings.init() } installationSummary.value = context.getInstallationSummary() + userLocale.value = context.translation.loadedLocale.getDisplayName(Locale.getDefault()) } override fun sectionTopBarName() = "SnapEnhance" diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/features/FeaturesSection.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/features/FeaturesSection.kt index cb27b0eb2..88e540b3d 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/features/FeaturesSection.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/features/FeaturesSection.kt @@ -115,7 +115,7 @@ class FeaturesSection : Section() { } override fun build(navGraphBuilder: NavGraphBuilder) { - navGraphBuilder.navigation(route = "features", startDestination = MAIN_ROUTE) { + navGraphBuilder.navigation(route = enumSection.route, startDestination = MAIN_ROUTE) { composable(MAIN_ROUTE) { Container(context.config.root) } @@ -141,8 +141,7 @@ class FeaturesSection : Section() { if (showDialog.value) { Dialog( - onDismissRequest = { showDialog.value = false }, - properties = DialogProperties() + onDismissRequest = { showDialog.value = false } ) { dialogComposable.value() } @@ -329,7 +328,7 @@ class FeaturesSection : Section() { scaffoldState.snackbarHostState.showSnackbar("Saved") } }, - modifier = Modifier.padding(25.dp), + modifier = Modifier.padding(10.dp), containerColor = MaterialTheme.colors.primary, contentColor = MaterialTheme.colors.onPrimary, shape = RoundedCornerShape(16.dp), diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/impl/MappingsScreen.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/impl/MappingsScreen.kt index b7e4cddcb..3d660bcc2 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/impl/MappingsScreen.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/impl/MappingsScreen.kt @@ -35,7 +35,6 @@ class MappingsScreen : SetupScreen() { }) { Surface( modifier = Modifier.padding(16.dp).fillMaxWidth(), - color = MaterialTheme.colors.surface, shape = RoundedCornerShape(16.dp), ) { Column( @@ -86,8 +85,8 @@ class MappingsScreen : SetupScreen() { }) { if (isGenerating.value) { CircularProgressIndicator( - modifier = Modifier.padding(end = 5.dp).size(25.dp), - strokeWidth = 2.dp, + modifier = Modifier.padding().size(30.dp), + strokeWidth = 3.dp, color = MaterialTheme.colors.onPrimary ) } else { diff --git a/core/src/main/assets/lang/en_US.json b/core/src/main/assets/lang/en_US.json index 4eb9c5a44..b9c3997d2 100644 --- a/core/src/main/assets/lang/en_US.json +++ b/core/src/main/assets/lang/en_US.json @@ -20,6 +20,7 @@ "routes": { "downloads": "Downloads", "features": "Features", + "home": "Home", "friends": "Friends", "debug": "Debug" }, diff --git a/core/src/main/assets/lang/fr_FR.json b/core/src/main/assets/lang/fr_FR.json index a68882ebf..bcd1ff371 100644 --- a/core/src/main/assets/lang/fr_FR.json +++ b/core/src/main/assets/lang/fr_FR.json @@ -7,6 +7,19 @@ } }, + "manager": { + "routes": { + "downloads": "Téléchargements", + "features": "Fonctionnalités", + "home": "Accueil", + "friends": "Amis", + "debug": "Débogage" + }, + "features": { + "disabled": "Désactivé" + } + }, + "category": { "spying_privacy": "Espionnage et vie privée", "media_manager": "Gestionnaire de média", From 55f6627d72cd6797f2737f7db5de6dfa165f4346 Mon Sep 17 00:00:00 2001 From: rhunk <101876869+rhunk@users.noreply.github.com> Date: Sat, 5 Aug 2023 10:33:51 +0200 Subject: [PATCH 27/81] wrapper enum delegate --- .../me/rhunk/snapenhance/data/wrapper/AbstractWrapper.kt | 9 +++++++++ .../me/rhunk/snapenhance/data/wrapper/impl/Message.kt | 3 +-- .../snapenhance/data/wrapper/impl/MessageContent.kt | 4 +--- .../snapenhance/data/wrapper/impl/MessageMetadata.kt | 6 +----- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/AbstractWrapper.kt b/core/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/AbstractWrapper.kt index 541a0cb04..5ffd77a23 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/AbstractWrapper.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/AbstractWrapper.kt @@ -2,10 +2,17 @@ package me.rhunk.snapenhance.data.wrapper import de.robv.android.xposed.XposedHelpers import me.rhunk.snapenhance.util.CallbackBuilder +import kotlin.reflect.KProperty abstract class AbstractWrapper( protected var instance: Any? ) { + @Suppress("UNCHECKED_CAST") + inner class EnumAccessor(private val fieldName: String, private val defaultValue: T) { + operator fun getValue(obj: Any, property: KProperty<*>): T = getEnumValue(fieldName, defaultValue as Enum<*>) as T + operator fun setValue(obj: Any, property: KProperty<*>, value: Any) = setEnumValue(fieldName, value as Enum<*>) + } + companion object { fun newEmptyInstance(clazz: Class<*>): Any { return CallbackBuilder.createEmptyObject(clazz.constructors[0]) ?: throw NullPointerException() @@ -23,6 +30,8 @@ abstract class AbstractWrapper( return instance.toString() } + protected fun enum(fieldName: String, defaultValue: T) = EnumAccessor(fieldName, defaultValue) + fun > getEnumValue(fieldName: String, defaultValue: T): T { val mContentType = XposedHelpers.getObjectField(instance, fieldName) as Enum<*> return java.lang.Enum.valueOf(defaultValue::class.java, mContentType.name) as T diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/Message.kt b/core/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/Message.kt index 87be84ed2..a1ffe437f 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/Message.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/Message.kt @@ -10,6 +10,5 @@ class Message(obj: Any?) : AbstractWrapper(obj) { val messageContent get() = MessageContent(instanceNonNull().getObjectField("mMessageContent")) val messageDescriptor get() = MessageDescriptor(instanceNonNull().getObjectField("mDescriptor")) val messageMetadata get() = MessageMetadata(instanceNonNull().getObjectField("mMetadata")) - var messageState get() = getEnumValue("mState", MessageState.COMMITTED) - set(value) = setEnumValue("mState", value) + var messageState by enum("mState", MessageState.COMMITTED) } \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/MessageContent.kt b/core/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/MessageContent.kt index e0bf5a61c..fee3b043c 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/MessageContent.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/MessageContent.kt @@ -9,7 +9,5 @@ class MessageContent(obj: Any?) : AbstractWrapper(obj) { var content get() = instanceNonNull().getObjectField("mContent") as ByteArray set(value) = instanceNonNull().setObjectField("mContent", value) - var contentType - get() = getEnumValue("mContentType", ContentType.UNKNOWN) - set(value) = setEnumValue("mContentType", value) + var contentType by enum("mContentType", ContentType.UNKNOWN) } \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/MessageMetadata.kt b/core/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/MessageMetadata.kt index 98d954b1e..084e036d3 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/MessageMetadata.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/data/wrapper/impl/MessageMetadata.kt @@ -7,11 +7,7 @@ import me.rhunk.snapenhance.util.getObjectField class MessageMetadata(obj: Any?) : AbstractWrapper(obj){ val createdAt: Long get() = instanceNonNull().getObjectField("mCreatedAt") as Long val readAt: Long get() = instanceNonNull().getObjectField("mReadAt") as Long - var playableSnapState: PlayableSnapState - get() = getEnumValue("mPlayableSnapState", PlayableSnapState.PLAYABLE) - set(value) { - setEnumValue("mPlayableSnapState", value) - } + var playableSnapState by enum("mPlayableSnapState", PlayableSnapState.PLAYABLE) private fun getUUIDList(name: String): List { return (instanceNonNull().getObjectField(name) as List<*>).map { SnapUUID(it!!) } From ddfa89f4b4e3c5271f6dc218470b93090d781604 Mon Sep 17 00:00:00 2001 From: rhunk <101876869+rhunk@users.noreply.github.com> Date: Sat, 5 Aug 2023 10:34:21 +0200 Subject: [PATCH 28/81] fix: location spoofer - float to double conversion --- .../rhunk/snapenhance/features/impl/tweaks/LocationSpoofer.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/LocationSpoofer.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/LocationSpoofer.kt index 8ed9d07eb..55ae93a73 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/LocationSpoofer.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/tweaks/LocationSpoofer.kt @@ -30,8 +30,8 @@ class LocationSpoofer: Feature("LocationSpoof", loadParams = FeatureLoadParams.A val locationClass = android.location.Location::class.java val locationManagerClass = android.location.LocationManager::class.java - locationClass.hook("getLatitude", HookStage.BEFORE) { it.setResult(latitude) } - locationClass.hook("getLongitude", HookStage.BEFORE) { it.setResult(longitude) } + locationClass.hook("getLatitude", HookStage.BEFORE) { it.setResult(latitude.toDouble()) } + locationClass.hook("getLongitude", HookStage.BEFORE) { it.setResult(longitude.toDouble()) } locationClass.hook("getAccuracy", HookStage.BEFORE) { it.setResult(0.0F) } //Might be redundant because it calls isProviderEnabledForUser which we also hook, meaning if isProviderEnabledForUser returns true this will also return true From e6e75123f85f21e4405769ff97c7712505be533e Mon Sep 17 00:00:00 2001 From: rhunk <101876869+rhunk@users.noreply.github.com> Date: Sat, 5 Aug 2023 11:13:30 +0200 Subject: [PATCH 29/81] fix: download server timeout --- .../util/download/DownloadServer.kt | 39 +++++++++++++------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/util/download/DownloadServer.kt b/core/src/main/kotlin/me/rhunk/snapenhance/util/download/DownloadServer.kt index 26c30e735..509d0074e 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/util/download/DownloadServer.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/util/download/DownloadServer.kt @@ -2,6 +2,8 @@ package me.rhunk.snapenhance.util.download import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay import kotlinx.coroutines.launch import me.rhunk.snapenhance.Logger import java.io.BufferedReader @@ -10,16 +12,20 @@ import java.io.InputStreamReader import java.io.PrintWriter import java.net.ServerSocket import java.net.Socket -import java.net.SocketTimeoutException +import java.net.SocketException import java.util.Locale import java.util.StringTokenizer import java.util.concurrent.ConcurrentHashMap -import java.util.concurrent.ThreadLocalRandom +import kotlin.random.Random class DownloadServer( private val timeout: Int = 10000 ) { - private val port = ThreadLocalRandom.current().nextInt(10000, 65535) + private val port = Random.nextInt(10000, 65535) + + private val coroutineScope = CoroutineScope(Dispatchers.IO) + private var timeoutJob: Job? = null + private var socketJob: Job? = null private val cachedData = ConcurrentHashMap>() private var serverSocket: ServerSocket? = null @@ -30,27 +36,36 @@ class DownloadServer( return } - CoroutineScope(Dispatchers.IO).launch { + coroutineScope.launch(Dispatchers.IO) { Logger.debug("starting download server on port $port") serverSocket = ServerSocket(port) - serverSocket!!.soTimeout = timeout callback(this@DownloadServer) while (!serverSocket!!.isClosed) { try { val socket = serverSocket!!.accept() - launch(Dispatchers.IO) { + timeoutJob?.cancel() + launch { handleRequest(socket) + timeoutJob = launch { + delay(timeout.toLong()) + Logger.debug("download server closed due to timeout") + runCatching { + socketJob?.cancel() + socket.close() + serverSocket?.close() + }.onFailure { + Logger.error(it) + } + } } - } catch (e: SocketTimeoutException) { - serverSocket?.close() - serverSocket = null - Logger.debug("download server closed") + } catch (e: SocketException) { + Logger.debug("download server timed out") break; - } catch (e: Exception) { + } catch (e: Throwable) { Logger.error("failed to handle request", e) } } - } + }.also { socketJob = it } } fun close() { From 7b6b0bfd700f928870eb719c531fc1ced4f12c48 Mon Sep 17 00:00:00 2001 From: rhunk <101876869+rhunk@users.noreply.github.com> Date: Sat, 5 Aug 2023 19:34:31 +0200 Subject: [PATCH 30/81] feat: download section --- app/build.gradle.kts | 5 +- .../snapenhance/download/DownloadProcessor.kt | 39 ++- .../snapenhance/ui/manager/Navigation.kt | 2 + .../rhunk/snapenhance/ui/manager/Section.kt | 8 +- .../sections/download/DownloadSection.kt | 12 - .../sections/downloads/DownloadsSection.kt | 296 ++++++++++++++++++ .../sections/features/FeaturesSection.kt | 9 +- .../ui/setup/screens/impl/MappingsScreen.kt | 4 +- .../setup/screens/impl/PickLanguageScreen.kt | 5 +- .../src/main/res/drawable/bitmoji_blank.xml | 0 .../download/DownloadTaskManager.kt | 42 +-- .../{PendingDownload.kt => DownloadObject.kt} | 3 +- .../impl/downloader/MediaDownloader.kt | 8 +- .../ui/download/DownloadListAdapter.kt | 10 +- .../ui/download/DownloadManagerActivity.kt | 6 +- .../snapenhance/ui/download/MediaFilter.kt | 16 +- .../snapenhance/util/snap/PreviewUtils.kt | 4 +- gradle/libs.versions.toml | 10 +- 18 files changed, 386 insertions(+), 93 deletions(-) delete mode 100644 app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/download/DownloadSection.kt create mode 100644 app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/downloads/DownloadsSection.kt rename {core => app}/src/main/res/drawable/bitmoji_blank.xml (100%) rename core/src/main/kotlin/me/rhunk/snapenhance/download/data/{PendingDownload.kt => DownloadObject.kt} (92%) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 2b5b93a35..4376077f7 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -91,13 +91,16 @@ android { dependencies { implementation(project(":core")) + implementation(libs.androidx.material.icons.core) + implementation(libs.androidx.material.ripple) implementation(libs.androidx.material.icons.extended) implementation(libs.androidx.material3) - implementation(libs.androidx.material) implementation(libs.androidx.activity.ktx) implementation(libs.androidx.navigation.compose) implementation(libs.androidx.documentfile) implementation(libs.gson) + implementation(libs.coil.compose) + implementation(libs.coil.video) debugImplementation("androidx.compose.ui:ui-tooling:1.4.3") implementation("androidx.compose.ui:ui-tooling-preview:1.4.3") diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/download/DownloadProcessor.kt b/app/src/main/kotlin/me/rhunk/snapenhance/download/DownloadProcessor.kt index 410e58baa..561a83b73 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/download/DownloadProcessor.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/download/DownloadProcessor.kt @@ -16,16 +16,15 @@ import kotlinx.coroutines.runBlocking import me.rhunk.snapenhance.Constants import me.rhunk.snapenhance.Logger import me.rhunk.snapenhance.RemoteSideContext -import me.rhunk.snapenhance.SharedContext import me.rhunk.snapenhance.bridge.DownloadCallback import me.rhunk.snapenhance.data.FileType import me.rhunk.snapenhance.download.data.DownloadMediaType import me.rhunk.snapenhance.download.data.DownloadMetadata +import me.rhunk.snapenhance.download.data.DownloadObject import me.rhunk.snapenhance.download.data.DownloadRequest import me.rhunk.snapenhance.download.data.DownloadStage import me.rhunk.snapenhance.download.data.InputMedia import me.rhunk.snapenhance.download.data.MediaEncryptionKeyPair -import me.rhunk.snapenhance.download.data.PendingDownload import me.rhunk.snapenhance.util.download.RemoteMediaResolver import me.rhunk.snapenhance.util.snap.MediaDownloaderHelper import java.io.File @@ -60,7 +59,7 @@ class DownloadProcessor ( ) { private val translation by lazy { - SharedContext.translation.getCategory("download_processor") + remoteSideContext.translation.getCategory("download_processor") } private val gson by lazy { @@ -118,7 +117,7 @@ class DownloadProcessor ( } @SuppressLint("UnspecifiedRegisterReceiverFlag") - private suspend fun saveMediaToGallery(inputFile: File, pendingDownload: PendingDownload) { + private suspend fun saveMediaToGallery(inputFile: File, downloadObject: DownloadObject) { if (coroutineContext.job.isCancelled) return runCatching { @@ -128,12 +127,12 @@ class DownloadProcessor ( return } - val fileName = pendingDownload.metadata.outputPath.substringAfterLast("/") + "." + fileType.fileExtension + val fileName = downloadObject.metadata.outputPath.substringAfterLast("/") + "." + fileType.fileExtension val outputFolder = DocumentFile.fromTreeUri(remoteSideContext.androidContext, Uri.parse(remoteSideContext.config.root.downloader.saveFolder.get())) ?: throw Exception("Failed to open output folder") - val outputFileFolder = pendingDownload.metadata.outputPath.let { + val outputFileFolder = downloadObject.metadata.outputPath.let { if (it.contains("/")) { it.substringBeforeLast("/").split("/").fold(outputFolder) { folder, name -> folder.findFile(name) ?: folder.createDirectory(name)!! @@ -150,8 +149,8 @@ class DownloadProcessor ( inputStream.copyTo(outputStream) } - pendingDownload.outputFile = outputFile.uri.toString() - pendingDownload.downloadStage = DownloadStage.SAVED + downloadObject.outputFile = outputFile.uri.toString() + downloadObject.downloadStage = DownloadStage.SAVED runCatching { val mediaScanIntent = Intent("android.intent.action.MEDIA_SCANNER_SCAN_FILE") @@ -167,7 +166,7 @@ class DownloadProcessor ( }.onFailure { exception -> Logger.error(exception) callbackOnFailure(translation.format("failed_gallery_toast", "error" to exception.toString()), exception.message) - pendingDownload.downloadStage = DownloadStage.FAILED + downloadObject.downloadStage = DownloadStage.FAILED } } @@ -226,13 +225,13 @@ class DownloadProcessor ( downloadedMedias } - private suspend fun downloadRemoteMedia(pendingDownloadObject: PendingDownload, downloadedMedias: Map, downloadRequest: DownloadRequest) { + private suspend fun downloadRemoteMedia(downloadObjectObject: DownloadObject, downloadedMedias: Map, downloadRequest: DownloadRequest) { downloadRequest.inputMedias.first().let { inputMedia -> val mediaType = inputMedia.type val media = downloadedMedias[inputMedia]!! if (!downloadRequest.isDashPlaylist) { - saveMediaToGallery(media.file, pendingDownloadObject) + saveMediaToGallery(media.file, downloadObjectObject) media.file.delete() return } @@ -261,12 +260,12 @@ class DownloadProcessor ( output = outputFile, startTime = dashOptions.offsetTime, duration = dashOptions.duration) - saveMediaToGallery(outputFile, pendingDownloadObject) + saveMediaToGallery(outputFile, downloadObjectObject) }.onFailure { exception -> if (coroutineContext.job.isCancelled) return@onFailure Logger.error(exception) callbackOnFailure(translation.format("failed_processing_toast", "error" to exception.toString()), exception.message) - pendingDownloadObject.downloadStage = DownloadStage.FAILED + downloadObjectObject.downloadStage = DownloadStage.FAILED } dashPlaylistFile.delete() @@ -297,11 +296,11 @@ class DownloadProcessor ( return@launch } - val pendingDownloadObject = PendingDownload( + val downloadObjectObject = DownloadObject( metadata = downloadMetadata ).apply { downloadTaskManager = remoteSideContext.downloadTaskManager } - pendingDownloadObject.also { + downloadObjectObject.also { remoteSideContext.downloadTaskManager.addTask(it) }.apply { job = coroutineContext.job @@ -345,7 +344,7 @@ class DownloadProcessor ( val mergedOverlay: File = File.createTempFile("merged", "." + media.fileType.fileExtension) runCatching { callbackOnProgress(translation.format("download_toast", "path" to media.file.nameWithoutExtension)) - pendingDownloadObject.downloadStage = DownloadStage.MERGING + downloadObjectObject.downloadStage = DownloadStage.MERGING MediaDownloaderHelper.mergeOverlayFile( media = renamedMedia, @@ -353,12 +352,12 @@ class DownloadProcessor ( output = mergedOverlay ) - saveMediaToGallery(mergedOverlay, pendingDownloadObject) + saveMediaToGallery(mergedOverlay, downloadObjectObject) }.onFailure { exception -> if (coroutineContext.job.isCancelled) return@onFailure Logger.error(exception) callbackOnFailure(translation.format("failed_processing_toast", "error" to exception.toString()), exception.message) - pendingDownloadObject.downloadStage = DownloadStage.MERGE_FAILED + downloadObjectObject.downloadStage = DownloadStage.MERGE_FAILED } mergedOverlay.delete() @@ -367,9 +366,9 @@ class DownloadProcessor ( return@launch } - downloadRemoteMedia(pendingDownloadObject, downloadedMedias, downloadRequest) + downloadRemoteMedia(downloadObjectObject, downloadedMedias, downloadRequest) }.onFailure { exception -> - pendingDownloadObject.downloadStage = DownloadStage.FAILED + downloadObjectObject.downloadStage = DownloadStage.FAILED Logger.error(exception) callbackOnFailure(translation["failed_generic_toast"], exception.message) } diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/Navigation.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/Navigation.kt index 0d23aad63..5b2e47907 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/Navigation.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/Navigation.kt @@ -71,6 +71,8 @@ class Navigation( Icon(Icons.Filled.ArrowBack, contentDescription = null) } } + }, actions = { + currentSection.TopBarActions(this) }) } diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/Section.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/Section.kt index cc5fa1f1a..041ed52e6 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/Section.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/Section.kt @@ -1,5 +1,6 @@ package me.rhunk.snapenhance.ui.manager +import androidx.compose.foundation.layout.RowScope import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.BugReport import androidx.compose.material.icons.filled.Download @@ -14,7 +15,7 @@ import androidx.navigation.compose.composable import me.rhunk.snapenhance.RemoteSideContext import me.rhunk.snapenhance.ui.manager.sections.HomeSection import me.rhunk.snapenhance.ui.manager.sections.NotImplemented -import me.rhunk.snapenhance.ui.manager.sections.download.DownloadSection +import me.rhunk.snapenhance.ui.manager.sections.downloads.DownloadsSection import me.rhunk.snapenhance.ui.manager.sections.features.FeaturesSection import kotlin.reflect.KClass @@ -26,7 +27,7 @@ enum class EnumSection( DOWNLOADS( route = "downloads", icon = Icons.Filled.Download, - section = DownloadSection::class + section = DownloadsSection::class ), FEATURES( route = "features", @@ -70,6 +71,9 @@ open class Section { @Composable open fun Content() { NotImplemented() } + @Composable + open fun TopBarActions(rowScope: RowScope) {} + open fun build(navGraphBuilder: NavGraphBuilder) { navGraphBuilder.composable(enumSection.route) { Content() diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/download/DownloadSection.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/download/DownloadSection.kt deleted file mode 100644 index 89ca1313c..000000000 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/download/DownloadSection.kt +++ /dev/null @@ -1,12 +0,0 @@ -package me.rhunk.snapenhance.ui.manager.sections.download - -import androidx.compose.runtime.Composable -import me.rhunk.snapenhance.ui.manager.Section -import me.rhunk.snapenhance.ui.manager.sections.NotImplemented - -class DownloadSection : Section() { - @Composable - override fun Content() { - NotImplemented() - } -} \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/downloads/DownloadsSection.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/downloads/DownloadsSection.kt new file mode 100644 index 000000000..6d4bdfa80 --- /dev/null +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/downloads/DownloadsSection.kt @@ -0,0 +1,296 @@ +package me.rhunk.snapenhance.ui.manager.sections.downloads + +import android.content.Intent +import android.net.Uri +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.requiredWidthIn +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Delete +import androidx.compose.material.icons.filled.FilterList +import androidx.compose.material.icons.filled.OpenInNew +import androidx.compose.material3.Card +import androidx.compose.material3.DropdownMenu +import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material3.FilledIconButton +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.IconButtonDefaults +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.RadioButton +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.blur +import androidx.compose.ui.draw.clip +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import coil.ImageLoader +import coil.compose.rememberAsyncImagePainter +import coil.decode.VideoFrameDecoder +import coil.memory.MemoryCache +import coil.request.ImageRequest +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import me.rhunk.snapenhance.R +import me.rhunk.snapenhance.data.FileType +import me.rhunk.snapenhance.download.data.DownloadObject +import me.rhunk.snapenhance.ui.download.MediaFilter +import me.rhunk.snapenhance.ui.manager.Section + +class DownloadsSection : Section() { + private val loadedDownloads = mutableStateOf(mapOf()) + private var currentFilter = mutableStateOf(MediaFilter.NONE) + + private val imageLoader by lazy { + ImageLoader.Builder(context.androidContext) + .dispatcher(Dispatchers.IO) + .memoryCache { + MemoryCache.Builder(context.androidContext) + .maxSizePercent(0.25) + .build() + }.components { add(VideoFrameDecoder.Factory()) }.build() + } + + override fun onResumed() { + super.onResumed() + loadByFilter(currentFilter.value) + } + + private fun loadByFilter(filter: MediaFilter) { + this.currentFilter.value = filter + synchronized(loadedDownloads) { + loadedDownloads.value = context.downloadTaskManager.queryFirstTasks(filter) + } + } + + private fun lazyLoadFromIndex(lastIndex: Int) { + synchronized(loadedDownloads) { + loadedDownloads.value = loadedDownloads.value.toMutableMap().also { + val lastVisible = loadedDownloads.value.values.elementAt(lastIndex) + it += context.downloadTaskManager.queryTasks( + from = lastVisible.downloadId, + filter = currentFilter.value + ) + } + } + } + + @Composable + private fun FilterList() { + val coroutineScope = rememberCoroutineScope() + val showMenu = remember { mutableStateOf(false) } + IconButton(onClick = { showMenu.value = !showMenu.value}) { + Icon( + imageVector = Icons.Default.FilterList, + contentDescription = null + ) + } + + DropdownMenu(expanded = showMenu.value, onDismissRequest = { showMenu.value = false }) { + MediaFilter.values().toList().forEach { filter -> + DropdownMenuItem( + text = { + Row( + modifier = Modifier + .fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically + ) { + RadioButton( + modifier = Modifier.padding(end = 16.dp), + selected = (currentFilter.value == filter), + onClick = null + ) + Text(filter.name, modifier = Modifier.weight(1f)) + } + }, + onClick = { + coroutineScope.launch { + loadByFilter(filter) + showMenu.value = false + } + } + ) + } + } + } + + @Composable + override fun TopBarActions(rowScope: RowScope) { + FilterList() + } + + @Composable + private fun DownloadItem(download: DownloadObject) { + Card( + modifier = Modifier + .padding(6.dp) + .fillMaxWidth() + .clip(MaterialTheme.shapes.medium) + ) { + Box(modifier = Modifier.height(120.dp)) { + Image( + painter = rememberAsyncImagePainter( + model = ImageRequest.Builder(context.androidContext) + .data(download.outputFile) + .memoryCacheKey(download.outputFile) + .build(), + imageLoader = imageLoader + ), + modifier = Modifier + .matchParentSize() + .blur(5.dp), + contentDescription = null, + contentScale = ContentScale.FillWidth + ) + + Row( + modifier = Modifier + .padding(start = 16.dp, end = 16.dp) + .fillMaxWidth() + .fillMaxHeight(), + verticalAlignment = Alignment.CenterVertically + ){ + //info card + Row( + modifier = Modifier + .background( + color = MaterialTheme.colorScheme.background, + shape = MaterialTheme.shapes.medium + ) + .padding(15.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Image( + painter = rememberAsyncImagePainter( + model = ImageRequest.Builder(context.androidContext) + .data(download.metadata.iconUrl) + .fallback(R.drawable.bitmoji_blank) + .memoryCacheKey(download.metadata.iconUrl) + .build(), + imageLoader = imageLoader + ), + contentDescription = null, + contentScale = ContentScale.Crop, + modifier = Modifier + .requiredWidthIn(min = 0.dp, max = 48.dp) + .height(48.dp) + .clip(MaterialTheme.shapes.medium) + ) + + Column( + modifier = Modifier + .padding(start = 10.dp), + verticalArrangement = Arrangement.SpaceBetween + ) { + Text( + text = download.metadata.mediaDisplayType ?: "", + overflow = TextOverflow.Ellipsis, + fontSize = 16.sp, + fontWeight = FontWeight.Bold + ) + Text( + text = download.metadata.mediaDisplaySource ?: "", + overflow = TextOverflow.Ellipsis, + fontSize = 12.sp, + fontWeight = FontWeight.Light + ) + } + } + + Spacer(modifier = Modifier.weight(1f)) + + //action buttons + Row( + modifier = Modifier + .padding(5.dp), + verticalAlignment = Alignment.CenterVertically + ) { + FilledIconButton( + onClick = { + }, + colors = IconButtonDefaults.iconButtonColors( + containerColor = MaterialTheme.colorScheme.error, + contentColor = MaterialTheme.colorScheme.onError + ) + ) { + Icon( + imageVector = Icons.Default.Delete, + contentDescription = null + ) + } + //open + FilledIconButton(onClick = { + val fileType = runCatching { + context.androidContext.contentResolver.openInputStream(Uri.parse(download.outputFile))?.use { input -> + FileType.fromInputStream(input) + } + }.getOrNull() ?: FileType.UNKNOWN + + val intent = Intent(Intent.ACTION_VIEW).apply { + setDataAndType(Uri.parse(download.outputFile), fileType.mimeType) + flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_GRANT_READ_URI_PERMISSION + } + context.androidContext.startActivity(intent) + }) { + Icon( + imageVector = Icons.Default.OpenInNew, + contentDescription = null + ) + } + } + } + } + } + } + + @Composable + override fun Content() { + val scrollState = rememberLazyListState() + + LazyColumn( + state = scrollState, + modifier = Modifier.fillMaxSize() + ) { + items(loadedDownloads.value.size) { index -> + DownloadItem(loadedDownloads.value.values.elementAt(index)) + } + + item { + Spacer(Modifier.height(20.dp)) + if (loadedDownloads.value.isEmpty()) { + Text(text = "No downloads", fontSize = 20.sp, modifier = Modifier + .fillMaxWidth() + .padding(10.dp), textAlign = TextAlign.Center) + } + LaunchedEffect(true) { + val lastItemIndex = (loadedDownloads.value.size - 1).takeIf { it >= 0 } ?: return@LaunchedEffect + lazyLoadFromIndex(lastItemIndex) + scrollState.animateScrollToItem(lastItemIndex) + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/features/FeaturesSection.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/features/FeaturesSection.kt index 88e540b3d..4230397bb 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/features/FeaturesSection.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/features/FeaturesSection.kt @@ -19,7 +19,6 @@ import androidx.compose.foundation.layout.wrapContentWidth import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.MaterialTheme import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.FolderOpen import androidx.compose.material.icons.filled.OpenInNew @@ -30,6 +29,7 @@ import androidx.compose.material3.FilledIconButton import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.Icon import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.SnackbarHost import androidx.compose.material3.Switch @@ -46,7 +46,6 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.window.Dialog -import androidx.compose.ui.window.DialogProperties import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable import androidx.navigation.navigation @@ -238,7 +237,7 @@ class FeaturesSection : Section() { .height(50.dp) .width(1.dp) .background( - color = MaterialTheme.colors.onBackground.copy(alpha = 0.12f), + color = MaterialTheme.colorScheme.primary.copy(alpha = 0.12f), shape = RoundedCornerShape(5.dp) )) } @@ -329,8 +328,8 @@ class FeaturesSection : Section() { } }, modifier = Modifier.padding(10.dp), - containerColor = MaterialTheme.colors.primary, - contentColor = MaterialTheme.colors.onPrimary, + containerColor = MaterialTheme.colorScheme.primary, + contentColor = MaterialTheme.colorScheme.onPrimary, shape = RoundedCornerShape(16.dp), ) { Icon( diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/impl/MappingsScreen.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/impl/MappingsScreen.kt index 3d660bcc2..483fea2b3 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/impl/MappingsScreen.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/impl/MappingsScreen.kt @@ -5,9 +5,9 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.MaterialTheme import androidx.compose.material3.Button import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -87,7 +87,7 @@ class MappingsScreen : SetupScreen() { CircularProgressIndicator( modifier = Modifier.padding().size(30.dp), strokeWidth = 3.dp, - color = MaterialTheme.colors.onPrimary + color = MaterialTheme.colorScheme.onPrimary ) } else { Text(text = context.translation["setup.mappings.generate_button"]) diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/impl/PickLanguageScreen.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/impl/PickLanguageScreen.kt index 433088a1b..fc0d4cb6f 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/impl/PickLanguageScreen.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/setup/screens/impl/PickLanguageScreen.kt @@ -10,10 +10,10 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.rememberScrollState -import androidx.compose.material.Surface -import androidx.compose.material.Text import androidx.compose.material3.Button import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -86,7 +86,6 @@ class PickLanguageScreen : SetupScreen(){ modifier = Modifier .padding(10.dp) .fillMaxWidth(), - elevation = 8.dp, shape = MaterialTheme.shapes.medium ) { LazyColumn( diff --git a/core/src/main/res/drawable/bitmoji_blank.xml b/app/src/main/res/drawable/bitmoji_blank.xml similarity index 100% rename from core/src/main/res/drawable/bitmoji_blank.xml rename to app/src/main/res/drawable/bitmoji_blank.xml diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/download/DownloadTaskManager.kt b/core/src/main/kotlin/me/rhunk/snapenhance/download/DownloadTaskManager.kt index 80e331687..9fbdf0981 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/download/DownloadTaskManager.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/download/DownloadTaskManager.kt @@ -4,15 +4,17 @@ import android.annotation.SuppressLint import android.content.Context import android.database.sqlite.SQLiteDatabase import me.rhunk.snapenhance.download.data.DownloadMetadata -import me.rhunk.snapenhance.download.data.PendingDownload +import me.rhunk.snapenhance.download.data.DownloadObject import me.rhunk.snapenhance.download.data.DownloadStage import me.rhunk.snapenhance.ui.download.MediaFilter import me.rhunk.snapenhance.util.SQLiteDatabaseHelper +import me.rhunk.snapenhance.util.getIntOrNull +import me.rhunk.snapenhance.util.getStringOrNull class DownloadTaskManager { private lateinit var taskDatabase: SQLiteDatabase - private val pendingTasks = mutableMapOf() - private val cachedTasks = mutableMapOf() + private val pendingTasks = mutableMapOf() + private val cachedTasks = mutableMapOf() @SuppressLint("Range") fun init(context: Context) { @@ -33,7 +35,7 @@ class DownloadTaskManager { } } - fun addTask(task: PendingDownload): Int { + fun addTask(task: DownloadObject): Int { taskDatabase.execSQL("INSERT INTO tasks (hash, outputPath, outputFile, mediaDisplayType, mediaDisplaySource, iconUrl, downloadStage) VALUES (?, ?, ?, ?, ?, ?, ?)", arrayOf( task.metadata.mediaIdentifier, @@ -53,7 +55,7 @@ class DownloadTaskManager { return task.downloadId } - fun updateTask(task: PendingDownload) { + fun updateTask(task: DownloadObject) { taskDatabase.execSQL("UPDATE tasks SET hash = ?, outputPath = ?, outputFile = ?, mediaDisplayType = ?, mediaDisplaySource = ?, iconUrl = ?, downloadStage = ? WHERE id = ?", arrayOf( task.metadata.mediaIdentifier, @@ -107,13 +109,13 @@ class DownloadTaskManager { pendingTasks.remove(id) } - fun removeTask(task: PendingDownload) { + fun removeTask(task: DownloadObject) { removeTask(task.downloadId) } - fun queryAllTasks(filter: MediaFilter): Map { + fun queryFirstTasks(filter: MediaFilter): Map { val isPendingFilter = filter == MediaFilter.PENDING - val tasks = mutableMapOf() + val tasks = mutableMapOf() tasks.putAll(pendingTasks.filter { isPendingFilter || filter.matches(it.value.metadata.mediaDisplayType) }) if (isPendingFilter) { @@ -130,7 +132,7 @@ class DownloadTaskManager { } @SuppressLint("Range") - fun queryTasks(from: Int, amount: Int = 20, filter: MediaFilter = MediaFilter.NONE): Map { + fun queryTasks(from: Int, amount: Int = 30, filter: MediaFilter = MediaFilter.NONE): Map { if (filter == MediaFilter.PENDING) { return emptyMap() } @@ -139,27 +141,27 @@ class DownloadTaskManager { "SELECT * FROM tasks WHERE id < ? AND mediaDisplayType LIKE ? ORDER BY id DESC LIMIT ?", arrayOf( from.toString(), - filter.mediaDisplayType.let { if (it == null) "%" else "%$it" }, + if (filter.shouldIgnoreFilter) "%" else "%${filter.key}", amount.toString() ) ) - val result = sortedMapOf() + val result = sortedMapOf() while (cursor.moveToNext()) { - val task = PendingDownload( - downloadId = cursor.getInt(cursor.getColumnIndex("id")), - outputFile = cursor.getString(cursor.getColumnIndex("outputFile")), + val task = DownloadObject( + downloadId = cursor.getIntOrNull("id")!!, + outputFile = cursor.getStringOrNull("outputFile"), metadata = DownloadMetadata( - outputPath = cursor.getString(cursor.getColumnIndex("outputPath")), - mediaIdentifier = cursor.getString(cursor.getColumnIndex("hash")), - mediaDisplayType = cursor.getString(cursor.getColumnIndex("mediaDisplayType")), - mediaDisplaySource = cursor.getString(cursor.getColumnIndex("mediaDisplaySource")), - iconUrl = cursor.getString(cursor.getColumnIndex("iconUrl")) + outputPath = cursor.getStringOrNull("outputPath")!!, + mediaIdentifier = cursor.getStringOrNull("hash"), + mediaDisplayType = cursor.getStringOrNull("mediaDisplayType"), + mediaDisplaySource = cursor.getStringOrNull("mediaDisplaySource"), + iconUrl = cursor.getStringOrNull("iconUrl") ) ).apply { downloadTaskManager = this@DownloadTaskManager - downloadStage = DownloadStage.valueOf(cursor.getString(cursor.getColumnIndex("downloadStage"))) + downloadStage = DownloadStage.valueOf(cursor.getStringOrNull("downloadStage")!!) //if downloadStage is not saved, it means the app was killed before the download was finished if (downloadStage != DownloadStage.SAVED) { downloadStage = DownloadStage.FAILED diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/download/data/PendingDownload.kt b/core/src/main/kotlin/me/rhunk/snapenhance/download/data/DownloadObject.kt similarity index 92% rename from core/src/main/kotlin/me/rhunk/snapenhance/download/data/PendingDownload.kt rename to core/src/main/kotlin/me/rhunk/snapenhance/download/data/DownloadObject.kt index 09dc4a263..e3eec134a 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/download/data/PendingDownload.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/download/data/DownloadObject.kt @@ -1,10 +1,9 @@ package me.rhunk.snapenhance.download.data import kotlinx.coroutines.Job -import me.rhunk.snapenhance.SharedContext import me.rhunk.snapenhance.download.DownloadTaskManager -data class PendingDownload( +data class DownloadObject( var downloadId: Int = 0, var outputFile: String? = null, val metadata : DownloadMetadata diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/downloader/MediaDownloader.kt b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/downloader/MediaDownloader.kt index 2f6f836c5..65c20191e 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/downloader/MediaDownloader.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/features/impl/downloader/MediaDownloader.kt @@ -242,7 +242,7 @@ class MediaDownloader : Feature("MediaDownloader", loadParams = FeatureLoadParam pathSuffix = authorUsername, mediaIdentifier = "$conversationId$senderId${conversationMessage.server_message_id}", mediaDisplaySource = authorUsername, - mediaDisplayType = MediaFilter.CHAT_MEDIA.mediaDisplayType, + mediaDisplayType = MediaFilter.CHAT_MEDIA.key, friendInfo = author ), mediaInfoMap) @@ -282,7 +282,7 @@ class MediaDownloader : Feature("MediaDownloader", loadParams = FeatureLoadParam pathSuffix = authorName, mediaIdentifier = paramMap["MEDIA_ID"].toString(), mediaDisplaySource = authorName, - mediaDisplayType = MediaFilter.STORY.mediaDisplayType, + mediaDisplayType = MediaFilter.STORY.key, friendInfo = author ), mediaInfoMap) return @@ -311,7 +311,7 @@ class MediaDownloader : Feature("MediaDownloader", loadParams = FeatureLoadParam downloadOperaMedia(provideDownloadManagerClient( pathSuffix = "Spotlight", mediaIdentifier = paramMap["SNAP_ID"].toString(), - mediaDisplayType = MediaFilter.SPOTLIGHT.mediaDisplayType, + mediaDisplayType = MediaFilter.SPOTLIGHT.key, mediaDisplaySource = paramMap["TIME_STAMP"].toString() ), mediaInfoMap) return @@ -476,7 +476,7 @@ class MediaDownloader : Feature("MediaDownloader", loadParams = FeatureLoadParam pathSuffix = authorName, mediaIdentifier = "${message.client_conversation_id}${message.sender_id}${message.server_message_id}", mediaDisplaySource = authorName, - mediaDisplayType = MediaFilter.CHAT_MEDIA.mediaDisplayType, + mediaDisplayType = MediaFilter.CHAT_MEDIA.key, friendInfo = friendInfo ).downloadSingleMedia( Base64.UrlSafe.encode(urlProto), diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadListAdapter.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadListAdapter.kt index 20acf5b64..6b11d54cf 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadListAdapter.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadListAdapter.kt @@ -26,7 +26,7 @@ import me.rhunk.snapenhance.Logger import me.rhunk.snapenhance.SharedContext import me.rhunk.snapenhance.core.R import me.rhunk.snapenhance.data.FileType -import me.rhunk.snapenhance.download.data.PendingDownload +import me.rhunk.snapenhance.download.data.DownloadObject import me.rhunk.snapenhance.download.data.DownloadStage import me.rhunk.snapenhance.util.snap.PreviewUtils import java.io.File @@ -37,7 +37,7 @@ import kotlin.coroutines.coroutineContext class DownloadListAdapter( private val activity: DownloadManagerActivity, - private val downloadList: MutableList + private val downloadList: MutableList ): Adapter() { private val coroutineScope = CoroutineScope(Dispatchers.IO) private val previewJobs = mutableMapOf() @@ -68,7 +68,7 @@ class DownloadListAdapter( } @SuppressLint("Recycle") - private suspend fun handlePreview(download: PendingDownload, holder: ViewHolder) { + private suspend fun handlePreview(download: DownloadObject, holder: ViewHolder) { download.outputFile?.let { val uri = Uri.parse(it) runCatching { @@ -124,7 +124,7 @@ class DownloadListAdapter( } } - private fun updateViewHolder(download: PendingDownload, holder: ViewHolder) { + private fun updateViewHolder(download: DownloadObject, holder: ViewHolder) { holder.status.text = download.downloadStage.toString() holder.view.background = holder.view.context.getDrawable(R.drawable.download_manager_item_background) @@ -163,7 +163,7 @@ class DownloadListAdapter( } } - holder.bitmojiIcon.setImageResource(R.drawable.bitmoji_blank) + // holder.bitmojiIcon.setImageResource(R.drawable.bitmoji_blank) pendingDownload.metadata.iconUrl?.let { url -> thread(start = true) { diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadManagerActivity.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadManagerActivity.kt index f456e96d4..c929b4f00 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadManagerActivity.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadManagerActivity.kt @@ -19,13 +19,13 @@ import me.rhunk.snapenhance.SharedContext import me.rhunk.snapenhance.bridge.wrapper.LocaleWrapper import me.rhunk.snapenhance.core.BuildConfig import me.rhunk.snapenhance.core.R -import me.rhunk.snapenhance.download.data.PendingDownload +import me.rhunk.snapenhance.download.data.DownloadObject class DownloadManagerActivity : Activity() { lateinit var translation: LocaleWrapper private val backCallbacks = mutableListOf<() -> Unit>() - private val fetchedDownloadTasks = mutableListOf() + private val fetchedDownloadTasks = mutableListOf() private var listFilter = MediaFilter.NONE private val preferences by lazy { @@ -42,7 +42,7 @@ class DownloadManagerActivity : Activity() { @SuppressLint("NotifyDataSetChanged") private fun updateListContent() { fetchedDownloadTasks.clear() - fetchedDownloadTasks.addAll(SharedContext.downloadTaskManager.queryAllTasks(filter = listFilter).values) + fetchedDownloadTasks.addAll(SharedContext.downloadTaskManager.queryFirstTasks(filter = listFilter).values) with(findViewById(R.id.download_list)) { adapter?.notifyDataSetChanged() diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/MediaFilter.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/MediaFilter.kt index b0b8cb490..1a68dce20 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/MediaFilter.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/MediaFilter.kt @@ -1,17 +1,17 @@ package me.rhunk.snapenhance.ui.download enum class MediaFilter( - val mediaDisplayType: String? = null + val key: String, + val shouldIgnoreFilter: Boolean = false ) { - NONE, - PENDING, - CHAT_MEDIA("Chat Media"), - STORY("Story"), - SPOTLIGHT("Spotlight"); + NONE("none", true), + PENDING("pending", true), + CHAT_MEDIA("chat_media"), + STORY("story"), + SPOTLIGHT("spotlight"); fun matches(source: String?): Boolean { - if (mediaDisplayType == null) return true if (source == null) return false - return source.contains(mediaDisplayType, ignoreCase = true) + return source.contains(key, ignoreCase = true) } } \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/util/snap/PreviewUtils.kt b/core/src/main/kotlin/me/rhunk/snapenhance/util/snap/PreviewUtils.kt index 13d7ff549..acef7a62f 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/util/snap/PreviewUtils.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/util/snap/PreviewUtils.kt @@ -48,9 +48,7 @@ object PreviewUtils { setDataSource(file.absolutePath) }.getFrameAtTime(0, MediaMetadataRetriever.OPTION_CLOSEST_SYNC) } else { - BitmapFactory.decodeFile(file.absolutePath, BitmapFactory.Options().apply { - inSampleSize = 1 - }) + BitmapFactory.decodeFile(file.absolutePath, BitmapFactory.Options()) } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 81639f1cb..a6514b130 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,11 +1,12 @@ [versions] agp = "8.2.0-alpha14" -androidx-material = "1.6.0-alpha02" +coil-compose = "2.4.0" junit = "4.13.2" kotlin = "1.8.22" kotlinx-coroutines-android = "1.7.2" kotlin-reflect = "1.8.22" -material-icons-extended = "1.6.0-alpha03" +material-icons-core = "1.4.3" +material-icons-extended = "1.6.0-alpha02" navigation-compose = "2.7.0-rc01" recyclerview = "1.3.1" gson = "2.10.1" @@ -19,9 +20,12 @@ material3 = "1.1.1" [libraries] -androidx-material = { module = "androidx.compose.material:material", version.ref = "androidx-material" } +androidx-material-icons-core = { module = "androidx.compose.material:material-icons-core", version.ref = "material-icons-core" } androidx-material-icons-extended = { module = "androidx.compose.material:material-icons-extended", version.ref = "material-icons-extended" } +androidx-material-ripple = { module = "androidx.compose.material:material-ripple", version.ref = "material-icons-core" } androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigation-compose" } +coil-compose = { module = "io.coil-kt:coil-compose", version.ref = "coil-compose" } +coil-video = { module = "io.coil-kt:coil-video", version.ref = "coil-compose" } coroutines = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "kotlinx-coroutines-android" } junit = { module = "junit:junit", version.ref = "junit" } kotlin-reflect = { group = "org.jetbrains.kotlin", name = "kotlin-reflect", version.ref = "kotlin-reflect" } From 2b11dec050221ce6527f96f5cc96560294155462 Mon Sep 17 00:00:00 2001 From: rhunk <101876869+rhunk@users.noreply.github.com> Date: Sun, 6 Aug 2023 15:55:06 +0200 Subject: [PATCH 31/81] fix: navigation bar label --- .../rhunk/snapenhance/ui/manager/Navigation.kt | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/Navigation.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/Navigation.kt index 5b2e47907..20d0a4fbf 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/Navigation.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/Navigation.kt @@ -1,9 +1,7 @@ package me.rhunk.snapenhance.ui.manager -import androidx.compose.animation.core.animateDpAsState import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxHeight -import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.wrapContentWidth import androidx.compose.material.icons.Icons @@ -19,7 +17,6 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.navigation.NavDestination import androidx.navigation.NavDestination.Companion.hierarchy @@ -85,30 +82,22 @@ class Navigation( fun selected() = currentDestination?.hierarchy?.any { it.route == section.route } == true NavigationBarItem( + alwaysShowLabel = false, modifier = Modifier .fillMaxHeight(), icon = { - val iconOffset by animateDpAsState( - if (selected()) 0.dp else 10.dp, - label = "" - ) Icon( imageVector = section.icon, - contentDescription = null, - modifier = Modifier.offset(y = iconOffset) + contentDescription = null ) }, label = { - val labelOffset by animateDpAsState( - if (selected()) 0.dp else (-5).dp, - label = "" - ) Text( textAlign = TextAlign.Center, softWrap = false, fontSize = 12.sp, - modifier = Modifier.wrapContentWidth(unbounded = true).offset(y = labelOffset), + modifier = Modifier.wrapContentWidth(unbounded = true), text = if (selected()) context.translation["manager.routes.${section.route}"] else "", ) }, From 6eb5ead35d322eacde869a02a964dd7842056088 Mon Sep 17 00:00:00 2001 From: rhunk <101876869+rhunk@users.noreply.github.com> Date: Sun, 6 Aug 2023 16:41:15 +0200 Subject: [PATCH 32/81] feat(ui/features): dialog translation --- .../ui/manager/sections/features/Dialogs.kt | 26 +- .../sections/features/FeaturesSection.kt | 6 +- core/src/main/assets/lang/en_US.json | 379 +++++++++--------- .../bridge/wrapper/LocaleWrapper.kt | 4 +- .../snapenhance/core/config/ConfigObjects.kt | 4 +- 5 files changed, 214 insertions(+), 205 deletions(-) diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/features/Dialogs.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/features/Dialogs.kt index c18a968e1..02889c57a 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/features/Dialogs.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/features/Dialogs.kt @@ -29,11 +29,14 @@ import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.unit.dp +import me.rhunk.snapenhance.bridge.wrapper.LocaleWrapper import me.rhunk.snapenhance.core.config.DataProcessors import me.rhunk.snapenhance.core.config.PropertyPair -class Dialogs { +class Dialogs( + private val translation: LocaleWrapper, +){ @Composable fun DefaultDialogCard(content: @Composable ColumnScope.() -> Unit) { Card( @@ -50,9 +53,12 @@ class Dialogs { } @Composable - fun DefaultEntryText(text: String, modifier: Modifier = Modifier) { + fun TranslatedText(property: PropertyPair<*>, key: String, modifier: Modifier = Modifier) { Text( - text = text, + text = when (key) { + "null" -> translation["manager.features.disabled"] + else -> if (property.key.params.shouldTranslate) translation["features.options.${property.key.name}.$key"] else key + }, modifier = Modifier .padding(10.dp, 10.dp, 10.dp, 10.dp) .then(modifier) @@ -63,11 +69,11 @@ class Dialogs { @Suppress("UNCHECKED_CAST") fun UniqueSelectionDialog(property: PropertyPair<*>) { val keys = (property.value.defaultValues as List).toMutableList().apply { - add(0, "disabled") + add(0, "null") } val selectedValue = remember { - mutableStateOf(property.value.getNullable()?.toString() ?: "disabled") + mutableStateOf(property.value.getNullable()?.toString() ?: "null") } DefaultDialogCard { @@ -85,8 +91,9 @@ class Dialogs { modifier = Modifier.clickable { select() }, verticalAlignment = Alignment.CenterVertically ) { - DefaultEntryText( - text = item, + TranslatedText( + property = property, + key = item, modifier = Modifier.weight(1f) ) RadioButton( @@ -189,8 +196,9 @@ class Dialogs { modifier = Modifier.clickable { toggle() }, verticalAlignment = Alignment.CenterVertically ) { - DefaultEntryText( - text = key, + TranslatedText( + property = property, + key = key, modifier = Modifier .weight(1f) ) diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/features/FeaturesSection.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/features/FeaturesSection.kt index 4230397bb..3fc7758f8 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/features/FeaturesSection.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/features/FeaturesSection.kt @@ -57,7 +57,7 @@ import me.rhunk.snapenhance.ui.manager.Section import me.rhunk.snapenhance.ui.util.ChooseFolderHelper class FeaturesSection : Section() { - private val dialogs by lazy { Dialogs() } + private val dialogs by lazy { Dialogs(context.translation) } companion object { const val MAIN_ROUTE = "feature_root" @@ -183,7 +183,9 @@ class FeaturesSection : Section() { overflow = TextOverflow.Ellipsis, maxLines = 1, modifier = Modifier.widthIn(0.dp, 120.dp), - text = (propertyValue.getNullable() as? String) ?: context.translation["manager.features.disabled"], + text = (propertyValue.getNullable() as? String)?.let{ + context.translation["features.options.${property.name}.$it"] + } ?: context.translation["manager.features.disabled"], ) } diff --git a/core/src/main/assets/lang/en_US.json b/core/src/main/assets/lang/en_US.json index b9c3997d2..c4ba7b6a6 100644 --- a/core/src/main/assets/lang/en_US.json +++ b/core/src/main/assets/lang/en_US.json @@ -39,106 +39,203 @@ }, "features": { - "spoof": { - "name": "Spoof", - "description": "Spoof your information", - "properties": { - "location": { - "name": "Location", - "description": "Spoof your location" - }, - "device": { - "name": "Device", - "description": "Spoof your device" + "properties": { + "spoof": { + "name": "Spoof", + "description": "Spoof your information", + "properties": { + "location": { + "name": "Location", + "description": "Spoof your location" + }, + "device": { + "name": "Device", + "description": "Spoof your device" + } } - } - }, - "downloader": { - "name": "Downloader", - "description": "Download Snaps and Stories", - "properties": { - "save_folder": { - "name": "Save Folder", - "description": "The directory where all media is saved" - }, - "auto_download_options": { - "name": "Auto Download Options", - "description": "Select which medias to auto download" + }, + "downloader": { + "name": "Downloader", + "description": "Download Snaps and Stories", + "properties": { + "save_folder": { + "name": "Save Folder", + "description": "The directory where all media is saved" + }, + "auto_download_options": { + "name": "Auto Download Options", + "description": "Select which medias to auto download" + } } - } - }, - "user_interface": { - "name": "User Interface", - "description": "Change the look and feel of Snapchat", - "properties": { - "enable_app_appearance": { - "name": "Enable App Appearance Settings", - "description": "Enables the hidden app appearance settings" - }, - "amoled_dark_mode": { - "name": "AMOLED Dark Mode", - "description": "Enables AMOLED dark mode\nMake sure Snapchat's dark mode is enabled" - }, - "map_friend_nametags": { - "name": "Enhanced Friend Map Nametags", - "description": "Enhances the nametags of friends on the map" - }, - "streak_expiration_info": { - "name": "Show Streak Expiration Info", - "description": "Shows Streak expiration info next to streaks" - }, - "hide_story_section": { - "name": "Hide Story Section", - "description": "Hide certain UI Elements shown in the story section" - }, - "hide_ui_components": { - "name": "Hide UI Components", - "description": "Select which UI components to hide" - }, - "disable_spotlight": { - "name": "Disable Spotlight", - "description": "Disables the Spotlight page" - }, - "startup_tab": { - "name": "Startup Tab", - "description": "Change the tab that opens on startup" - }, - "story_viewer_override": { - "name": "Story Viewer Override", - "description": "Turns on certain features which Snapchat hid" - }, - "friend_feed_menu_buttons": { - "name": "Friend Feed Menu Buttons", - "description": "Select which buttons to show in the Friend Feed Menu Bar" - }, - "friend_feed_menu_position": { - "name": "Friend Feed Position Index", - "description": "The position of the Friend Feed Menu component" - }, - "enable_friend_feed_menu_bar": { - "name": "Friend Feed Menu Bar", - "description": "Enables the new Friend Feed Menu Bar" + }, + "user_interface": { + "name": "User Interface", + "description": "Change the look and feel of Snapchat", + "properties": { + "enable_app_appearance": { + "name": "Enable App Appearance Settings", + "description": "Enables the hidden app appearance settings" + }, + "amoled_dark_mode": { + "name": "AMOLED Dark Mode", + "description": "Enables AMOLED dark mode\nMake sure Snapchat's dark mode is enabled" + }, + "map_friend_nametags": { + "name": "Enhanced Friend Map Nametags", + "description": "Enhances the nametags of friends on the map" + }, + "streak_expiration_info": { + "name": "Show Streak Expiration Info", + "description": "Shows Streak expiration info next to streaks" + }, + "hide_story_sections": { + "name": "Hide Story Section", + "description": "Hide certain UI Elements shown in the story section" + }, + "hide_ui_components": { + "name": "Hide UI Components", + "description": "Select which UI components to hide" + }, + "disable_spotlight": { + "name": "Disable Spotlight", + "description": "Disables the Spotlight page" + }, + "startup_tab": { + "name": "Startup Tab", + "description": "Change the tab that opens on startup" + }, + "story_viewer_override": { + "name": "Story Viewer Override", + "description": "Turns on certain features which Snapchat hid" + }, + "friend_feed_menu_buttons": { + "name": "Friend Feed Menu Buttons", + "description": "Select which buttons to show in the Friend Feed Menu Bar" + }, + "friend_feed_menu_position": { + "name": "Friend Feed Position Index", + "description": "The position of the Friend Feed Menu component" + }, + "enable_friend_feed_menu_bar": { + "name": "Friend Feed Menu Bar", + "description": "Enables the new Friend Feed Menu Bar" + } } + }, + "messaging": { + "name": "Messaging", + "description": "Change how you interact with friends" + }, + "global": { + "name": "Global", + "description": "Tweak Snapchat globally" + }, + "camera": { + "name": "Camera", + "description": "Adjust the right settings for the perfect snap" + }, + "experimental": { + "name": "Experimental", + "description": "Experimental features" } }, - "messaging": { - "name": "Messaging", - "description": "Change how you interact with friends" - }, - "global": { - "name": "Global", - "description": "Tweak Snapchat globally" - }, - "camera": { - "name": "Camera", - "description": "Adjust the right settings for the perfect snap" - }, - "experimental": { - "name": "Experimental", - "description": "Experimental features" + "options": { + "better_notifications": { + "chat": "Show chat messages", + "snap": "Show medias", + "reply_button": "Add reply button", + "download_button": "Add download button" + }, + "friend_feed_menu_buttons": { + "auto_download_blacklist": "\u2B07\uFE0F Auto Download Blacklist", + "anti_auto_save": "\uD83D\uDCAC Anti Auto Save Messages", + "stealth_mode": "\uD83D\uDC7B Stealth Mode", + "conversation_info": "\uD83D\uDC64 Conversation Info" + }, + "download_options": { + "allow_duplicate": "Allow duplicate downloads", + "create_user_folder": "Create folder for each user", + "append_hash": "Add a unique hash to the file name", + "append_username": "Add the username to the file name", + "append_date_time": "Add the date and time to the file name", + "append_type": "Add the media type to the file name", + "merge_overlay": "Merge Snap Image Overlays" + }, + "auto_download_options": { + "friend_snaps": "Friend Snaps", + "friend_stories": "Friend Stories", + "public_stories": "Public Stories", + "spotlight": "Spotlight" + }, + "download_logging": { + "started": "Started", + "success": "Success", + "progress": "Progress", + "failure": "Failure" + }, + "auto_save_messages": { + "NOTE": "Audio Note", + "CHAT": "Chat", + "EXTERNAL_MEDIA": "External Media", + "SNAP": "Snap", + "STICKER": "Sticker" + }, + "notifications": { + "chat_screenshot": "Screenshot", + "chat_screen_record": "Screen Record", + "camera_roll_save": "Camera Roll Save", + "chat": "Chat", + "chat_reply": "Chat Reply", + "snap": "Snap", + "typing": "Typing", + "stories": "Stories", + "initiate_audio": "Incoming Audio Call", + "abandon_audio": "Missed Audio Call", + "initiate_video": "Incoming Video Call", + "abandon_video": "Missed Video Call" + }, + "gallery_media_send_override": { + "ORIGINAL": "Original", + "NOTE": "Audio Note", + "SNAP": "Snap", + "LIVE_SNAP": "Snap with audio" + }, + "hide_ui_components": { + "hide_call_buttons": "Remove Call Buttons", + "hide_cognac_button": "Remove Cognac Button", + "hide_live_location_share_button": "Remove Live Location Share Button", + "hide_stickers_button": "Remove Stickers Button", + "hide_voice_record_button": "Remove Voice Record Button" + }, + "auto_updater": { + "DISABLED": "Disabled", + "EVERY_LAUNCH": "Every Launch", + "DAILY": "Daily", + "WEEKLY": "Weekly" + }, + "story_viewer_override": { + "OFF": "Off", + "DISCOVER_PLAYBACK_SEEKBAR": "Enable Discover Playback Seekbar", + "VERTICAL_STORY_VIEWER": "Enable Vertical Story Viewer" + }, + "hide_story_sections": { + "hide_friend_suggestions": "Hide friend suggestions", + "hide_friends": "Hide friends section", + "hide_following": "Hide following section", + "hide_for_you": "Hide For You section" + }, + "startup_tab": { + "OFF": "Off", + "ngs_map_icon_container": "Map", + "ngs_chat_icon_container": "Chat", + "ngs_camera_icon_container": "Camera", + "ngs_community_icon_container": "Community / Stories", + "ngs_spotlight_icon_container": "Spotlight", + "ngs_search_icon_container": "Search" + } }, - "properties": { + "temp": { "message_logger": { "name": "Message Logger", "description": "Prevents messages from being deleted" @@ -300,102 +397,6 @@ "name": "Android ID", "description": "Spoofs the devices Android ID" } - }, - "option": { - "better_notifications": { - "chat": "Show chat messages", - "snap": "Show medias", - "reply_button": "Add reply button", - "download_button": "Add download button" - }, - "friend_feed_menu_buttons": { - "auto_download_blacklist": "\u2B07\uFE0F Auto Download Blacklist", - "anti_auto_save": "\uD83D\uDCAC Anti Auto Save Messages", - "stealth_mode": "\uD83D\uDC7B Stealth Mode", - "conversation_info": "\uD83D\uDC64 Conversation Info" - }, - "download_options": { - "allow_duplicate": "Allow duplicate downloads", - "create_user_folder": "Create folder for each user", - "append_hash": "Add a unique hash to the file name", - "append_username": "Add the username to the file name", - "append_date_time": "Add the date and time to the file name", - "append_type": "Add the media type to the file name", - "merge_overlay": "Merge Snap Image Overlays" - }, - "auto_download_options": { - "friend_snaps": "Friend Snaps", - "friend_stories": "Friend Stories", - "public_stories": "Public Stories", - "spotlight": "Spotlight" - }, - "download_logging": { - "started": "Started", - "success": "Success", - "progress": "Progress", - "failure": "Failure" - }, - "auto_save_messages": { - "NOTE": "Audio Note", - "CHAT": "Chat", - "EXTERNAL_MEDIA": "External Media", - "SNAP": "Snap", - "STICKER": "Sticker" - }, - "notifications": { - "chat_screenshot": "Screenshot", - "chat_screen_record": "Screen Record", - "camera_roll_save": "Camera Roll Save", - "chat": "Chat", - "chat_reply": "Chat Reply", - "snap": "Snap", - "typing": "Typing", - "stories": "Stories", - "initiate_audio": "Incoming Audio Call", - "abandon_audio": "Missed Audio Call", - "initiate_video": "Incoming Video Call", - "abandon_video": "Missed Video Call" - }, - "gallery_media_send_override": { - "ORIGINAL": "Original", - "NOTE": "Audio Note", - "SNAP": "Snap", - "LIVE_SNAP": "Snap with audio" - }, - "hide_ui_elements": { - "remove_call_buttons": "Remove Call Buttons", - "remove_cognac_button": "Remove Cognac Button", - "remove_live_location_share_button": "Remove Live Location Share Button", - "remove_stickers_button": "Remove Stickers Button", - "remove_voice_record_button": "Remove Voice Record Button", - "remove_camera_borders": "Remove Camera Borders" - }, - "auto_updater": { - "DISABLED": "Disabled", - "EVERY_LAUNCH": "Every Launch", - "DAILY": "Daily", - "WEEKLY": "Weekly" - }, - "story_viewer_override": { - "OFF": "Off", - "DISCOVER_PLAYBACK_SEEKBAR": "Enable Discover Playback Seekbar", - "VERTICAL_STORY_VIEWER": "Enable Vertical Story Viewer" - }, - "hide_story_section": { - "hide_friend_suggestions": "Hide friend suggestions", - "hide_friends": "Hide friends section", - "hide_following": "Hide following section", - "hide_for_you": "Hide For You section" - }, - "startup_page_override": { - "OFF": "Off", - "ngs_map_icon_container": "Map", - "ngs_chat_icon_container": "Chat", - "ngs_camera_icon_container": "Camera", - "ngs_community_icon_container": "Community / Stories", - "ngs_spotlight_icon_container": "Spotlight", - "ngs_search_icon_container": "Search" - } } }, diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/LocaleWrapper.kt b/core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/LocaleWrapper.kt index 2577a20ca..186d87c5d 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/LocaleWrapper.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/bridge/wrapper/LocaleWrapper.kt @@ -80,9 +80,7 @@ class LocaleWrapper { loadFromContext(context) } - operator fun get(key: String): String { - return translationMap[key] ?: key.also { Logger.debug("Missing translation for $key") } - } + operator fun get(key: String) = translationMap[key] ?: key.also { Logger.debug("Missing translation for $key") } fun format(key: String, vararg args: Pair): String { return args.fold(get(key)) { acc, pair -> diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/config/ConfigObjects.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/ConfigObjects.kt index 052bd5f80..ba284bc2a 100644 --- a/core/src/main/kotlin/me/rhunk/snapenhance/core/config/ConfigObjects.kt +++ b/core/src/main/kotlin/me/rhunk/snapenhance/core/config/ConfigObjects.kt @@ -11,7 +11,7 @@ data class PropertyPair( } class ConfigParams( - var shouldTranslate: Boolean = false, + var shouldTranslate: Boolean = true, var isHidden: Boolean = false, var isFolder: Boolean = false, val disabledKey: String? = null @@ -52,7 +52,7 @@ data class PropertyKey( return if (parentKey != null) { "${parentKey!!.propertyTranslationPath()}.properties.$name" } else { - "features.$name" + "features.properties.$name" } } } From 00b6f25933373c5a4a2d093641763a8caa61ea5f Mon Sep 17 00:00:00 2001 From: rhunk <101876869+rhunk@users.noreply.github.com> Date: Sun, 6 Aug 2023 16:42:37 +0200 Subject: [PATCH 33/81] fix(ui/downloads): image preview - fix blur - add crossfade --- .../ui/manager/sections/downloads/DownloadsSection.kt | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/downloads/DownloadsSection.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/downloads/DownloadsSection.kt index 6d4bdfa80..417b19f5f 100644 --- a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/downloads/DownloadsSection.kt +++ b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/downloads/DownloadsSection.kt @@ -52,6 +52,7 @@ import coil.compose.rememberAsyncImagePainter import coil.decode.VideoFrameDecoder import coil.memory.MemoryCache import coil.request.ImageRequest +import coil.size.Precision import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import me.rhunk.snapenhance.R @@ -150,25 +151,26 @@ class DownloadsSection : Section() { .fillMaxWidth() .clip(MaterialTheme.shapes.medium) ) { - Box(modifier = Modifier.height(120.dp)) { + Box(modifier = Modifier.height(100.dp)) { Image( painter = rememberAsyncImagePainter( model = ImageRequest.Builder(context.androidContext) .data(download.outputFile) .memoryCacheKey(download.outputFile) + .crossfade(true) .build(), imageLoader = imageLoader ), modifier = Modifier .matchParentSize() - .blur(5.dp), + .blur(12.dp), contentDescription = null, contentScale = ContentScale.FillWidth ) Row( modifier = Modifier - .padding(start = 16.dp, end = 16.dp) + .padding(start = 10.dp, end = 10.dp) .fillMaxWidth() .fillMaxHeight(), verticalAlignment = Alignment.CenterVertically @@ -188,6 +190,8 @@ class DownloadsSection : Section() { model = ImageRequest.Builder(context.androidContext) .data(download.metadata.iconUrl) .fallback(R.drawable.bitmoji_blank) + .precision(Precision.INEXACT) + .crossfade(true) .memoryCacheKey(download.metadata.iconUrl) .build(), imageLoader = imageLoader From 289afce4a5cd27b294fdc3577ce249f4a043e621 Mon Sep 17 00:00:00 2001 From: rhunk <101876869+rhunk@users.noreply.github.com> Date: Sun, 6 Aug 2023 22:07:53 +0200 Subject: [PATCH 34/81] package refactor --- app/build.gradle.kts | 1 + app/src/main/AndroidManifest.xml | 2 +- .../me/rhunk/snapenhance/ui}/MapActivity.kt | 2 +- .../sections/downloads/DownloadsSection.kt | 2 +- .../manager/sections/features/PickLocation.kt | 4 + core/build.gradle.kts | 1 - .../rhunk/snapenhance/action/impl/OpenMap.kt | 3 +- .../download/DownloadTaskManager.kt | 2 +- .../download => download/data}/MediaFilter.kt | 2 +- .../impl/downloader/MediaDownloader.kt | 2 +- .../download/DebugSettingsLayoutInflater.kt | 104 -------- .../ui/download/DownloadListAdapter.kt | 242 ------------------ .../ui/download/DownloadManagerActivity.kt | 214 ---------------- 13 files changed, 12 insertions(+), 569 deletions(-) rename {core/src/main/kotlin/me/rhunk/snapenhance/ui/map => app/src/main/kotlin/me/rhunk/snapenhance/ui}/MapActivity.kt (99%) create mode 100644 app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/features/PickLocation.kt rename core/src/main/kotlin/me/rhunk/snapenhance/{ui/download => download/data}/MediaFilter.kt (89%) delete mode 100644 core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DebugSettingsLayoutInflater.kt delete mode 100644 core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadListAdapter.kt delete mode 100644 core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadManagerActivity.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 4376077f7..f7c2b28a4 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -101,6 +101,7 @@ dependencies { implementation(libs.gson) implementation(libs.coil.compose) implementation(libs.coil.video) + implementation(libs.osmdroid.android) debugImplementation("androidx.compose.ui:ui-tooling:1.4.3") implementation("androidx.compose.ui:ui-tooling-preview:1.4.3") diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e7f622484..68c2b2e86 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -52,7 +52,7 @@ android:theme="@style/AppTheme" android:excludeFromRecents="true" /> Unit>> -) : ArrayAdapter Unit>>(activity, layoutId, actions) { - override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { - val view = convertView ?: activity.layoutInflater.inflate(layoutId, parent, false) - val action = actions[position] - view.isClickable = true - - view.findViewById(R.id.feature_text).text = action.first - view.setOnClickListener { - action.second() - } - - return view - } -} - -class DebugSettingsLayoutInflater( - private val activity: DownloadManagerActivity -) { - private fun confirmAction(title: String, message: String, action: () -> Unit) { - activity.runOnUiThread { - AlertDialog.Builder(activity) - .setTitle(title) - .setMessage(message) - .setPositiveButton(SharedContext.translation["button.positive"]) { _, _ -> - action() - } - .setNegativeButton(SharedContext.translation["button.negative"]) { _, _ -> } - .show() - } - } - - private fun showSuccessToast() { - Toast.makeText(activity, "Success", Toast.LENGTH_SHORT).show() - } - - fun inflate(parent: ViewGroup) { - val debugSettingsLayout = activity.layoutInflater.inflate(R.layout.debug_settings_page, parent, false) - - val debugSettingsTranslation = activity.translation.getCategory("debug_settings_page") - - debugSettingsLayout.findViewById(R.id.back_button).setOnClickListener { - parent.removeView(debugSettingsLayout) - } - - debugSettingsLayout.findViewById(R.id.title).text = activity.translation["debug_settings"] - - debugSettingsLayout.findViewById(R.id.setting_page_list).apply { - adapter = ActionListAdapter(activity, R.layout.debug_setting_item, mutableListOf Unit>>().apply { - add(debugSettingsTranslation["clear_cache_title"] to { - context.cacheDir.listFiles()?.forEach { - it.deleteRecursively() - } - showSuccessToast() - }) - - BridgeFileType.values().forEach { fileType -> - val actionName = debugSettingsTranslation.format("clear_file_title", "file_name" to fileType.displayName) - add(actionName to { - confirmAction(actionName, debugSettingsTranslation.format("clear_file_confirmation", "file_name" to fileType.displayName)) { - fileType.resolve(context).deleteRecursively() - showSuccessToast() - } - }) - } - - add(debugSettingsTranslation["reset_all_title"] to { - confirmAction(debugSettingsTranslation["reset_all_title"], debugSettingsTranslation["reset_all_confirmation"]) { - arrayOf(context.cacheDir, context.filesDir, File(context.dataDir, "databases"), File(context.dataDir, "shared_prefs")).forEach { - it.listFiles()?.forEach { file -> - file.deleteRecursively() - } - } - showSuccessToast() - } - }) - }.toTypedArray()) - } - - activity.registerBackCallback { - parent.removeView(debugSettingsLayout) - } - - parent.addView(debugSettingsLayout) - } -} \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadListAdapter.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadListAdapter.kt deleted file mode 100644 index 6b11d54cf..000000000 --- a/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadListAdapter.kt +++ /dev/null @@ -1,242 +0,0 @@ -package me.rhunk.snapenhance.ui.download - -import android.annotation.SuppressLint -import android.content.Intent -import android.graphics.Bitmap -import android.graphics.BitmapFactory -import android.net.Uri -import android.os.Handler -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.Button -import android.widget.ImageView -import android.widget.TextView -import android.widget.Toast -import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory -import androidx.recyclerview.widget.RecyclerView -import androidx.recyclerview.widget.RecyclerView.Adapter -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job -import kotlinx.coroutines.job -import kotlinx.coroutines.launch -import kotlinx.coroutines.withTimeout -import me.rhunk.snapenhance.Logger -import me.rhunk.snapenhance.SharedContext -import me.rhunk.snapenhance.core.R -import me.rhunk.snapenhance.data.FileType -import me.rhunk.snapenhance.download.data.DownloadObject -import me.rhunk.snapenhance.download.data.DownloadStage -import me.rhunk.snapenhance.util.snap.PreviewUtils -import java.io.File -import java.io.FileInputStream -import java.net.URL -import kotlin.concurrent.thread -import kotlin.coroutines.coroutineContext - -class DownloadListAdapter( - private val activity: DownloadManagerActivity, - private val downloadList: MutableList -): Adapter() { - private val coroutineScope = CoroutineScope(Dispatchers.IO) - private val previewJobs = mutableMapOf() - - inner class ViewHolder(val view: View) : RecyclerView.ViewHolder(view) { - val bitmojiIcon: ImageView = view.findViewById(R.id.bitmoji_icon) - val title: TextView = view.findViewById(R.id.item_title) - val subtitle: TextView = view.findViewById(R.id.item_subtitle) - val status: TextView = view.findViewById(R.id.item_status) - val actionButton: Button = view.findViewById(R.id.item_action_button) - val radius by lazy { - view.context.resources.getDimensionPixelSize(R.dimen.download_manager_item_preview_radius) - } - val viewWidth by lazy { - view.resources.displayMetrics.widthPixels - } - val viewHeight by lazy { - view.layoutParams.height - } - } - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { - return ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.download_manager_item, parent, false)) - } - - override fun getItemCount(): Int { - return downloadList.size - } - - @SuppressLint("Recycle") - private suspend fun handlePreview(download: DownloadObject, holder: ViewHolder) { - download.outputFile?.let { - val uri = Uri.parse(it) - runCatching { - if (uri.scheme == "content") { - val fileType = activity.contentResolver.openInputStream(uri)!!.use { stream -> - FileType.fromInputStream(stream) - } - fileType to activity.contentResolver.openInputStream(uri) - } else { - FileType.fromFile(File(it)) to FileInputStream(it) - } - }.getOrNull() - }?.also { (fileType, assetStream) -> - val previewBitmap = assetStream?.use { stream -> - //don't preview files larger than 30MB - if (stream.available() > 30 * 1024 * 1024) return@also - - val tempFile = File.createTempFile("preview", ".${fileType.fileExtension}") - tempFile.outputStream().use { output -> - stream.copyTo(output) - } - runCatching { - PreviewUtils.createPreviewFromFile(tempFile)?.let { preview -> - val offsetY = (preview.height / 2 - holder.viewHeight / 2).coerceAtLeast(0) - - Bitmap.createScaledBitmap( - Bitmap.createBitmap( - preview, 0, offsetY, - preview.width.coerceAtMost(holder.viewWidth), - preview.height.coerceAtMost(holder.viewHeight) - ), - holder.viewWidth, - holder.viewHeight, - false - ) - } - }.onFailure { - Logger.error("failed to create preview $fileType", it) - }.also { - tempFile.delete() - }.getOrNull() - } ?: return@also - - if (coroutineContext.job.isCancelled) return@also - Handler(holder.view.context.mainLooper).post { - holder.view.background = RoundedBitmapDrawableFactory.create( - holder.view.context.resources, - previewBitmap - ).also { - it.cornerRadius = holder.radius.toFloat() - } - } - } - } - - private fun updateViewHolder(download: DownloadObject, holder: ViewHolder) { - holder.status.text = download.downloadStage.toString() - holder.view.background = holder.view.context.getDrawable(R.drawable.download_manager_item_background) - - coroutineScope.launch { - withTimeout(2000) { - handlePreview(download, holder) - } - } - - val isSaved = download.downloadStage == DownloadStage.SAVED - //if the download is in progress, the user can cancel it - val canInteract = if (download.job != null) !download.downloadStage.isFinalStage || isSaved - else isSaved - - holder.status.visibility = if (isSaved) View.GONE else View.VISIBLE - - with(holder.actionButton) { - isEnabled = canInteract - alpha = if (canInteract) 1f else 0.5f - background = context.getDrawable(if (isSaved) R.drawable.action_button_success else R.drawable.action_button_cancel) - setTextColor(context.getColor(if (isSaved) R.color.successColor else R.color.actionBarColor)) - text = if (isSaved) - SharedContext.translation["button.open"] - else - SharedContext.translation["button.cancel"] - } - } - - override fun onBindViewHolder(holder: ViewHolder, position: Int) { - val pendingDownload = downloadList[position] - - pendingDownload.changeListener = { _, _ -> - Handler(holder.view.context.mainLooper).post { - updateViewHolder(pendingDownload, holder) - notifyItemChanged(position) - } - } - - // holder.bitmojiIcon.setImageResource(R.drawable.bitmoji_blank) - - pendingDownload.metadata.iconUrl?.let { url -> - thread(start = true) { - runCatching { - val iconBitmap = URL(url).openStream().use { - BitmapFactory.decodeStream(it) - } - Handler(holder.view.context.mainLooper).post { - holder.bitmojiIcon.setImageBitmap(iconBitmap) - } - } - } - } - - holder.title.visibility = View.GONE - holder.subtitle.visibility = View.GONE - - pendingDownload.metadata.mediaDisplayType?.let { - holder.title.text = it - holder.title.visibility = View.VISIBLE - } - - pendingDownload.metadata.mediaDisplaySource?.let { - holder.subtitle.text = it - holder.subtitle.visibility = View.VISIBLE - } - - holder.actionButton.setOnClickListener { - if (pendingDownload.downloadStage != DownloadStage.SAVED) { - pendingDownload.cancel() - pendingDownload.downloadStage = DownloadStage.CANCELLED - updateViewHolder(pendingDownload, holder) - notifyItemChanged(position); - return@setOnClickListener - } - - pendingDownload.outputFile?.let { - fun showFileNotFound() { - Toast.makeText(holder.view.context, SharedContext.translation["download_manager_activity.file_not_found_toast"], Toast.LENGTH_SHORT).show() - } - - val uri = Uri.parse(it) - val fileType = runCatching { - if (uri.scheme == "content") { - activity.contentResolver.openInputStream(uri)?.use { input -> - FileType.fromInputStream(input) - } ?: run { - showFileNotFound() - return@setOnClickListener - } - } else { - val file = File(it) - if (!file.exists()) { - showFileNotFound() - return@setOnClickListener - } - FileType.fromFile(file) - } - }.onFailure { exception -> - Logger.error("Failed to open file", exception) - }.getOrDefault(FileType.UNKNOWN) - if (fileType == FileType.UNKNOWN) { - showFileNotFound() - return@setOnClickListener - } - - val intent = Intent(Intent.ACTION_VIEW) - intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_GRANT_READ_URI_PERMISSION - intent.setDataAndType(uri, fileType.mimeType) - holder.view.context.startActivity(intent) - } - } - - updateViewHolder(pendingDownload, holder) - } -} \ No newline at end of file diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadManagerActivity.kt b/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadManagerActivity.kt deleted file mode 100644 index c929b4f00..000000000 --- a/core/src/main/kotlin/me/rhunk/snapenhance/ui/download/DownloadManagerActivity.kt +++ /dev/null @@ -1,214 +0,0 @@ -package me.rhunk.snapenhance.ui.download - -import android.annotation.SuppressLint -import android.app.Activity -import android.app.AlertDialog -import android.content.Context -import android.content.Intent -import android.net.Uri -import android.os.Bundle -import android.os.PowerManager -import android.provider.Settings -import android.view.View -import android.widget.Button -import android.widget.ImageButton -import android.widget.TextView -import androidx.recyclerview.widget.ItemTouchHelper -import androidx.recyclerview.widget.RecyclerView -import me.rhunk.snapenhance.SharedContext -import me.rhunk.snapenhance.bridge.wrapper.LocaleWrapper -import me.rhunk.snapenhance.core.BuildConfig -import me.rhunk.snapenhance.core.R -import me.rhunk.snapenhance.download.data.DownloadObject - -class DownloadManagerActivity : Activity() { - lateinit var translation: LocaleWrapper - - private val backCallbacks = mutableListOf<() -> Unit>() - private val fetchedDownloadTasks = mutableListOf() - private var listFilter = MediaFilter.NONE - - private val preferences by lazy { - getSharedPreferences("settings", Context.MODE_PRIVATE) - } - - private fun updateNoDownloadText() { - findViewById(R.id.no_download_title).let { - it.text = translation["no_downloads"] - it.visibility = if (fetchedDownloadTasks.isEmpty()) View.VISIBLE else View.GONE - } - } - - @SuppressLint("NotifyDataSetChanged") - private fun updateListContent() { - fetchedDownloadTasks.clear() - fetchedDownloadTasks.addAll(SharedContext.downloadTaskManager.queryFirstTasks(filter = listFilter).values) - - with(findViewById(R.id.download_list)) { - adapter?.notifyDataSetChanged() - scrollToPosition(0) - } - updateNoDownloadText() - } - - @Deprecated("Deprecated in Java") - @Suppress("DEPRECATION") - override fun onBackPressed() { - backCallbacks.lastOrNull()?.let { - it() - backCallbacks.removeLast() - } ?: super.onBackPressed() - } - - fun registerBackCallback(callback: () -> Unit) { - backCallbacks.add(callback) - } - - @SuppressLint("BatteryLife", "NotifyDataSetChanged", "SetTextI18n") - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - SharedContext.ensureInitialized(this) - translation = SharedContext.translation.getCategory("download_manager_activity") - - setContentView(R.layout.download_manager_activity) - - findViewById(R.id.title).text = resources.getString(R.string.app_name) + " " + BuildConfig.VERSION_NAME - - findViewById(R.id.debug_settings_button).setOnClickListener { - DebugSettingsLayoutInflater(this).inflate(findViewById(android.R.id.content)) - } - - with(findViewById(R.id.download_list)) { - layoutManager = androidx.recyclerview.widget.LinearLayoutManager(this@DownloadManagerActivity) - - adapter = DownloadListAdapter(this@DownloadManagerActivity, fetchedDownloadTasks).apply { - registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() { - override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) { - updateNoDownloadText() - } - }) - } - - ItemTouchHelper(object : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT) { - override fun getMovementFlags( - recyclerView: RecyclerView, - viewHolder: RecyclerView.ViewHolder - ): Int { - val download = fetchedDownloadTasks[viewHolder.absoluteAdapterPosition] - return if (download.isJobActive()) { - 0 - } else { - super.getMovementFlags(recyclerView, viewHolder) - } - } - - override fun onMove( - recyclerView: RecyclerView, - viewHolder: RecyclerView.ViewHolder, - target: RecyclerView.ViewHolder - ): Boolean { - return false - } - - @SuppressLint("NotifyDataSetChanged") - override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) { - fetchedDownloadTasks.removeAt(viewHolder.absoluteAdapterPosition).let { - SharedContext.downloadTaskManager.removeTask(it) - } - adapter?.notifyItemRemoved(viewHolder.absoluteAdapterPosition) - } - }).attachToRecyclerView(this) - - var isLoading = false - - addOnScrollListener(object : RecyclerView.OnScrollListener() { - override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { - val layoutManager = recyclerView.layoutManager as androidx.recyclerview.widget.LinearLayoutManager - val lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition() - - if (lastVisibleItemPosition == RecyclerView.NO_POSITION) { - return - } - - if (lastVisibleItemPosition == fetchedDownloadTasks.size - 1 && !isLoading) { - isLoading = true - - SharedContext.downloadTaskManager.queryTasks(fetchedDownloadTasks.last().downloadId, filter = listFilter).forEach { - fetchedDownloadTasks.add(it.value) - adapter?.notifyItemInserted(fetchedDownloadTasks.size - 1) - } - - isLoading = false - } - } - }) - - arrayOf( - Pair(R.id.all_category, MediaFilter.NONE), - Pair(R.id.pending_category, MediaFilter.PENDING), - Pair(R.id.snap_category, MediaFilter.CHAT_MEDIA), - Pair(R.id.story_category, MediaFilter.STORY), - Pair(R.id.spotlight_category, MediaFilter.SPOTLIGHT) - ).let { categoryPairs -> - categoryPairs.forEach { pair -> - this@DownloadManagerActivity.findViewById(pair.first).apply { - text = translation["category.${resources.getResourceEntryName(pair.first)}"] - }.setOnClickListener { view -> - listFilter = pair.second - updateListContent() - categoryPairs.map { this@DownloadManagerActivity.findViewById(it.first) }.forEach { - it.setTextColor(getColor(R.color.primaryText)) - } - (view as TextView).setTextColor(getColor(R.color.focusedCategoryColor)) - } - } - } - - this@DownloadManagerActivity.findViewById