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
+
+
()
+
+ 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