From 3e84389b4697ad9afbe2682ded7ff1566a8fa120 Mon Sep 17 00:00:00 2001 From: darken Date: Sun, 6 Feb 2022 21:41:39 +0100 Subject: [PATCH] POC v0.0.1 --- .github/FUNDING.yml | 4 + .github/ISSUE_TEMPLATE/bug_report.md | 29 ++ .github/workflows/android.yml | 28 ++ .../workflows/gradle-wrapper-validation.yml | 17 + .gitignore | 13 + .idea/.gitignore | 3 + app/.gitignore | 1 + app/build.gradle | 252 +++++++++++++ app/proguard-rules-debug.pro | 1 + app/proguard/proguard-rules.pro | 21 ++ .../darken/adsbc/ExampleInstrumentedTest.kt | 22 ++ .../java/eu/darken/adsbc/HiltTestRunner.kt | 13 + .../eu/darken/adsbc/main/MainActivityTest.kt | 43 +++ .../main/fragment/ExampleFragmentTest.kt | 44 +++ .../androidTest/java/testhelper/BaseUITest.kt | 5 + .../java/testhelper/EmptyFragmentActivity.kt | 41 ++ .../java/testhelper/HiltExtensions.kt | 39 ++ app/src/debug/AndroidManifest.xml | 10 + .../java/eu/darken/adsbc/HiltTestActivity.kt | 7 + app/src/main/AndroidManifest.xml | 45 +++ app/src/main/ic_launcher-playstore.png | Bin 0 -> 204975 bytes .../eu/darken/adsbc/aircraft/core/Aircraft.kt | 21 ++ .../darken/adsbc/aircraft/core/AircraftId.kt | 11 + .../adsbc/aircraft/core/AircraftStorage.kt | 46 +++ .../adsbc/aircraft/core/BaseAircraft.kt | 16 + .../adsbc/aircraft/core/FeederAircraft.kt | 8 + .../aircraft/core/api/ADSBxAircraftApi.kt | 76 ++++ .../core/api/ADSBxAircraftEndpoint.kt | 77 ++++ .../adsbc/aircraft/ui/AircraftAdapter.kt | 78 ++++ .../adsbc/aircraft/ui/AircraftListFragment.kt | 36 ++ .../aircraft/ui/AircraftListFragmentVM.kt | 47 +++ .../aircraft/ui/actions/ActionsAdapter.kt | 33 ++ .../aircraft/ui/actions/AircraftAction.kt | 13 + .../ui/actions/AircraftActionDialog.kt | 41 ++ .../ui/actions/AircraftActionDialogVM.kt | 76 ++++ .../eu/darken/adsbc/common/BuildConfigWrap.kt | 18 + .../java/eu/darken/adsbc/common/BuildWrap.kt | 16 + .../eu/darken/adsbc/common/ClipboardHelper.kt | 51 +++ .../eu/darken/adsbc/common/ColorExtensions.kt | 13 + .../darken/adsbc/common/ContextExtensions.kt | 39 ++ .../darken/adsbc/common/DialogActionEnum.kt | 8 + .../java/eu/darken/adsbc/common/EmailTool.kt | 33 ++ .../java/eu/darken/adsbc/common/InstallId.kt | 42 +++ .../darken/adsbc/common/LiveDataExtensions.kt | 23 ++ .../eu/darken/adsbc/common/UnitConverter.kt | 14 + .../eu/darken/adsbc/common/WebpageTool.kt | 28 ++ .../adsbc/common/collections/MapExtensions.kt | 5 + .../darken/adsbc/common/compression/Zipper.kt | 40 ++ .../common/coroutine/AppCoroutineScope.kt | 19 + .../adsbc/common/coroutine/CoroutineModule.kt | 19 + .../coroutine/DefaultDispatcherProvider.kt | 7 + .../common/coroutine/DispatcherProvider.kt | 21 ++ .../adsbc/common/dagger/AndroidModule.kt | 24 ++ .../java/eu/darken/adsbc/common/debug/Bugs.kt | 21 ++ .../common/debug/autoreport/AutoReporting.kt | 58 +++ .../common/debug/autoreport/DebugSettings.kt | 35 ++ .../autoreport/bugsnag/BugsnagErrorHandler.kt | 58 +++ .../debug/autoreport/bugsnag/BugsnagLogger.kt | 47 +++ .../bugsnag/NOPBugsnagErrorHandler.kt | 19 + .../adsbc/common/debug/logging/FileLogger.kt | 79 ++++ .../common/debug/logging/LogCatLogger.kt | 49 +++ .../common/debug/logging/LogExtensions.kt | 10 + .../adsbc/common/debug/logging/Logging.kt | 132 +++++++ .../common/debug/recording/core/Recorder.kt | 48 +++ .../debug/recording/core/RecorderModule.kt | 123 ++++++ .../debug/recording/core/RecorderService.kt | 111 ++++++ .../debug/recording/ui/RecorderActivity.kt | 59 +++ .../debug/recording/ui/RecorderActivityVM.kt | 116 ++++++ .../darken/adsbc/common/error/ErrorDialog.kt | 18 + .../adsbc/common/error/ErrorEventSource.kt | 7 + .../adsbc/common/error/LocalizedError.kt | 36 ++ .../adsbc/common/error/ThrowableExtensions.kt | 42 +++ .../adsbc/common/flow/DynamicStateFlow.kt | 149 ++++++++ .../common/flow/DynamicStateFlowExtensions.kt | 3 + .../common/flow/FlowCombineExtensions.kt | 213 +++++++++++ .../adsbc/common/flow/FlowExtensions.kt | 74 ++++ .../eu/darken/adsbc/common/http/HttpModule.kt | 48 +++ .../darken/adsbc/common/lists/BaseAdapter.kt | 58 +++ .../darken/adsbc/common/lists/BindableVH.kt | 26 ++ .../darken/adsbc/common/lists/DataAdapter.kt | 13 + .../adsbc/common/lists/ItemSwipeTool.kt | 190 ++++++++++ .../eu/darken/adsbc/common/lists/ListItem.kt | 3 + .../common/lists/RecyclerViewExtensions.kt | 13 + .../adsbc/common/lists/differ/AsyncDiffer.kt | 44 +++ .../lists/differ/AsyncDifferExtensions.kt | 17 + .../adsbc/common/lists/differ/DifferItem.kt | 10 + .../common/lists/differ/HasAsyncDiffer.kt | 12 + .../common/lists/modular/ModularAdapter.kt | 95 +++++ .../common/lists/modular/mods/ClickMod.kt | 12 + .../lists/modular/mods/DataBinderMod.kt | 17 + .../lists/modular/mods/SimpleVHCreatorMod.kt | 15 + .../common/lists/modular/mods/StableIdMod.kt | 21 ++ .../lists/modular/mods/TypedVHCreatorMod.kt | 29 ++ .../adsbc/common/livedata/SingleLiveEvent.kt | 76 ++++ .../common/navigation/FragmentExtensions.kt | 38 ++ .../common/navigation/NavArgsExtensions.kt | 20 + .../navigation/NavControllerExtensions.kt | 22 ++ .../navigation/NavDestinationExtensions.kt | 9 + .../navigation/NavDirectionsExtensions.kt | 13 + .../notifications/PendingIntentCompat.kt | 12 + .../common/preferences/FlowPreference.kt | 67 ++++ .../preferences/FlowPreferenceExtension.kt | 43 +++ .../FlowPreferenceMoshiExtension.kt | 35 ++ .../preferences/PreferenceStoreMapper.kt | 76 ++++ .../adsbc/common/preferences/Settings.kt | 12 + .../preferences/SharedPreferenceExtensions.kt | 16 + .../adsbc/common/room/CommonConverters.kt | 35 ++ .../serialiation/SerializationModule.kt | 19 + .../common/ui/ConfirmableActionAdapterVH.kt | 78 ++++ .../eu/darken/adsbc/common/uix/Activity2.kt | 44 +++ .../common/uix/BottomSheetDialogFragment2.kt | 95 +++++ .../eu/darken/adsbc/common/uix/Fragment2.kt | 80 ++++ .../eu/darken/adsbc/common/uix/Fragment3.kt | 53 +++ .../adsbc/common/uix/PreferenceFragment2.kt | 70 ++++ .../eu/darken/adsbc/common/uix/Service2.kt | 52 +++ .../eu/darken/adsbc/common/uix/ViewModel1.kt | 20 + .../eu/darken/adsbc/common/uix/ViewModel2.kt | 63 ++++ .../eu/darken/adsbc/common/uix/ViewModel3.kt | 33 ++ .../adsbc/common/uix/ViewModelLazyKeyed.kt | 174 +++++++++ .../viewbinding/ViewBindingExtensions.kt | 94 +++++ .../adsbc/common/worker/WorkerExtensions.kt | 31 ++ .../enterprise/core/EnterpriseSettings.kt | 31 ++ .../eu/darken/adsbc/feeder/core/ADSBxId.kt | 18 + .../eu/darken/adsbc/feeder/core/Feeder.kt | 25 ++ .../eu/darken/adsbc/feeder/core/FeederId.kt | 10 + .../eu/darken/adsbc/feeder/core/FeederRepo.kt | 138 +++++++ .../adsbc/feeder/core/FeederRepoExtensions.kt | 8 + .../adsbc/feeder/core/FeederSettings.kt | 27 ++ .../adsbc/feeder/core/FeederSetupRequest.kt | 6 + .../adsbc/feeder/core/FeederUpdateService.kt | 83 +++++ .../adsbc/feeder/core/SpottedAircraft.kt | 8 + .../adsbc/feeder/core/storage/FeederDao.kt | 31 ++ .../adsbc/feeder/core/storage/FeederData.kt | 20 + .../feeder/core/storage/FeederDatabase.kt | 27 ++ .../feeder/core/storage/FeederStorage.kt | 57 +++ .../adsbc/feeder/ui/list/FeederAdapter.kt | 75 ++++ .../feeder/ui/list/FeederListFragment.kt | 64 ++++ .../feeder/ui/list/FeederListFragmentVM.kt | 64 ++++ .../feeder/ui/list/actions/ActionsAdapter.kt | 32 ++ .../feeder/ui/list/actions/FeederAction.kt | 15 + .../ui/list/actions/FeederActionDialog.kt | 61 +++ .../ui/list/actions/FeederActionDialogVM.kt | 104 ++++++ app/src/main/java/eu/darken/adsbc/main/App.kt | 41 ++ .../darken/adsbc/main/core/GeneralSettings.kt | 37 ++ .../eu/darken/adsbc/main/core/SomeRepo.kt | 53 +++ .../main/core/receiver/ExampleReceiver.kt | 24 ++ .../adsbc/main/core/service/ExampleBinder.kt | 9 + .../adsbc/main/core/service/ExampleService.kt | 17 + .../eu/darken/adsbc/main/ui/MainActivity.kt | 45 +++ .../eu/darken/adsbc/main/ui/MainActivityVM.kt | 45 +++ .../darken/adsbc/main/ui/main/MainFragment.kt | 90 +++++ .../adsbc/main/ui/main/MainFragmentVM.kt | 55 +++ .../adsbc/main/ui/main/MainPagerAdapter.kt | 39 ++ .../main/ui/settings/SettingsFragment.kt | 120 ++++++ .../main/ui/settings/SettingsFragmentVM.kt | 13 + .../main/ui/settings/SettingsIndexFragment.kt | 41 ++ .../settings/acks/AcknowledgementsFragment.kt | 22 ++ .../acks/AcknowledgementsFragmentVM.kt | 17 + .../general/GeneralSettingsFragment.kt | 62 ++++ .../general/GeneralSettingsFragmentVM.kt | 20 + .../advanced/AdvancedSettingsFragment.kt | 22 ++ .../advanced/AdvancedSettingsFragmentVM.kt | 19 + .../ui/settings/support/SupportFragment.kt | 59 +++ .../ui/settings/support/SupportFragmentVM.kt | 49 +++ .../adsbc/stats/core/api/ADSBxStatsApi.kt | 18 + .../stats/core/api/ADSBxStatsEndpoint.kt | 92 +++++ .../stats/core/storage/FeederStatsDao.kt | 27 ++ .../stats/core/storage/FeederStatsData.kt | 15 + .../adsbc/stats/core/storage/StatsDatabase.kt | 26 ++ .../adsbc/stats/core/storage/StatsStorage.kt | 59 +++ .../core/storage/StatsStorageExtensions.kt | 9 + .../drawable-v24/ic_launcher_foreground.xml | 30 ++ .../ic_baseline_add_circle_outline_24.xml | 10 + .../drawable/ic_baseline_arrow_back_24.xml | 11 + .../res/drawable/ic_baseline_av_timer_24.xml | 10 + .../drawable/ic_baseline_bug_report_24.xml | 10 + .../ic_baseline_business_center_24.xml | 10 + .../drawable/ic_baseline_delete_sweep_24.xml | 10 + ..._baseline_drive_file_rename_outline_24.xml | 10 + .../main/res/drawable/ic_baseline_info_24.xml | 10 + .../res/drawable/ic_baseline_radio_24.xml | 10 + .../res/drawable/ic_baseline_refresh_24.xml | 10 + .../res/drawable/ic_baseline_science_24.xml | 10 + .../res/drawable/ic_baseline_settings_24.xml | 10 + .../res/drawable/ic_card_text_onsurface.xml | 10 + .../res/drawable/ic_changelog_onsurface.xml | 10 + .../main/res/drawable/ic_email_onsurface.xml | 10 + app/src/main/res/drawable/ic_heart.xml | 10 + app/src/main/res/drawable/ic_id_onsurface.xml | 10 + .../drawable/ic_spider_thread_onsurface.xml | 10 + app/src/main/res/drawable/ic_twitter.xml | 10 + app/src/main/res/drawable/launch_screen.xml | 10 + .../layout/aircraft_list_action_dialog.xml | 58 +++ .../res/layout/aircraft_list_fragment.xml | 16 + .../main/res/layout/aircraft_list_line.xml | 56 +++ .../res/layout/debug_recording_activity.xml | 119 ++++++ .../res/layout/feeder_list_action_dialog.xml | 58 +++ .../main/res/layout/feeder_list_fragment.xml | 25 ++ .../res/layout/feeder_list_input_dialog.xml | 48 +++ app/src/main/res/layout/feeder_list_line.xml | 44 +++ .../res/layout/feeder_list_rename_dialog.xml | 25 ++ app/src/main/res/layout/main_activity.xml | 11 + app/src/main/res/layout/main_fragment.xml | 33 ++ app/src/main/res/layout/settings_fragment.xml | 26 ++ .../layout/settings_number_input_dialog.xml | 26 ++ .../res/layout/view_action_adapter_line.xml | 50 +++ app/src/main/res/menu/menu_main.xml | 18 + app/src/main/res/menu/menu_settings_index.xml | 12 + .../res/mipmap-anydpi-v26/ic_launcher.xml | 5 + .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 + .../mipmap-hdpi/ic_launcher_foreground.png | Bin 0 -> 17338 bytes .../mipmap-mdpi/ic_launcher_foreground.png | Bin 0 -> 7357 bytes .../mipmap-xhdpi/ic_launcher_foreground.png | Bin 0 -> 31630 bytes .../mipmap-xxhdpi/ic_launcher_foreground.png | Bin 0 -> 73429 bytes .../mipmap-xxxhdpi/ic_launcher_foreground.png | Bin 0 -> 129454 bytes app/src/main/res/navigation/main.xml | 46 +++ app/src/main/res/values-night/themes.xml | 42 +++ app/src/main/res/values/colors.xml | 58 +++ .../res/values/ic_launcher_background.xml | 4 + app/src/main/res/values/strings.xml | 56 +++ app/src/main/res/values/styles.xml | 73 ++++ app/src/main/res/values/themes.xml | 47 +++ .../res/xml/preferences_acknowledgements.xml | 82 ++++ app/src/main/res/xml/preferences_advanced.xml | 9 + app/src/main/res/xml/preferences_general.xml | 28 ++ app/src/main/res/xml/preferences_index.xml | 29 ++ app/src/main/res/xml/preferences_support.xml | 24 ++ .../adsbc/common/flow/DynamicStateFlowTest.kt | 351 ++++++++++++++++++ .../preferences/FlowPreferenceMoshiTest.kt | 131 +++++++ .../common/preferences/FlowPreferenceTest.kt | 160 ++++++++ app/src/test/java/testhelper/BaseTest.kt | 29 ++ .../coroutine/CoroutinesTestExtension.kt | 26 ++ .../coroutine/TestDispatcherProvider.kt | 23 ++ .../testhelper/coroutine/TestExtensions.kt | 49 +++ app/src/test/java/testhelper/flow/FlowTest.kt | 101 +++++ .../java/testhelper/json/JsonExtensions.kt | 29 ++ .../livedata/InstantExecutorExtension.kt | 26 ++ .../preferences/MockSharedPreferencesTest.kt | 22 ++ .../testhelpers/BaseTestInstrumentation.kt | 29 ++ .../java/testhelpers/IsAUnitTest.kt | 3 + .../java/testhelpers/logging/JUnitLogger.kt | 13 + .../preferences/MockFlowPreference.kt | 21 ++ .../preferences/MockSharedPreferences.kt | 99 +++++ build.gradle | 54 +++ gradle.properties | 19 + gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 54329 bytes gradle/wrapper/gradle-wrapper.properties | 6 + gradlew | 172 +++++++++ gradlew.bat | 84 +++++ settings.gradle | 2 + 250 files changed, 10009 insertions(+) create mode 100644 .github/FUNDING.yml create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/workflows/android.yml create mode 100644 .github/workflows/gradle-wrapper-validation.yml create mode 100644 .gitignore create mode 100644 .idea/.gitignore create mode 100644 app/.gitignore create mode 100644 app/build.gradle create mode 100644 app/proguard-rules-debug.pro create mode 100644 app/proguard/proguard-rules.pro create mode 100644 app/src/androidTest/java/eu/darken/adsbc/ExampleInstrumentedTest.kt create mode 100644 app/src/androidTest/java/eu/darken/adsbc/HiltTestRunner.kt create mode 100644 app/src/androidTest/java/eu/darken/adsbc/main/MainActivityTest.kt create mode 100644 app/src/androidTest/java/eu/darken/adsbc/main/fragment/ExampleFragmentTest.kt create mode 100644 app/src/androidTest/java/testhelper/BaseUITest.kt create mode 100644 app/src/androidTest/java/testhelper/EmptyFragmentActivity.kt create mode 100644 app/src/androidTest/java/testhelper/HiltExtensions.kt create mode 100644 app/src/debug/AndroidManifest.xml create mode 100644 app/src/debug/java/eu/darken/adsbc/HiltTestActivity.kt create mode 100644 app/src/main/AndroidManifest.xml create mode 100644 app/src/main/ic_launcher-playstore.png create mode 100644 app/src/main/java/eu/darken/adsbc/aircraft/core/Aircraft.kt create mode 100644 app/src/main/java/eu/darken/adsbc/aircraft/core/AircraftId.kt create mode 100644 app/src/main/java/eu/darken/adsbc/aircraft/core/AircraftStorage.kt create mode 100644 app/src/main/java/eu/darken/adsbc/aircraft/core/BaseAircraft.kt create mode 100644 app/src/main/java/eu/darken/adsbc/aircraft/core/FeederAircraft.kt create mode 100644 app/src/main/java/eu/darken/adsbc/aircraft/core/api/ADSBxAircraftApi.kt create mode 100644 app/src/main/java/eu/darken/adsbc/aircraft/core/api/ADSBxAircraftEndpoint.kt create mode 100644 app/src/main/java/eu/darken/adsbc/aircraft/ui/AircraftAdapter.kt create mode 100644 app/src/main/java/eu/darken/adsbc/aircraft/ui/AircraftListFragment.kt create mode 100644 app/src/main/java/eu/darken/adsbc/aircraft/ui/AircraftListFragmentVM.kt create mode 100644 app/src/main/java/eu/darken/adsbc/aircraft/ui/actions/ActionsAdapter.kt create mode 100644 app/src/main/java/eu/darken/adsbc/aircraft/ui/actions/AircraftAction.kt create mode 100644 app/src/main/java/eu/darken/adsbc/aircraft/ui/actions/AircraftActionDialog.kt create mode 100644 app/src/main/java/eu/darken/adsbc/aircraft/ui/actions/AircraftActionDialogVM.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/BuildConfigWrap.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/BuildWrap.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/ClipboardHelper.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/ColorExtensions.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/ContextExtensions.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/DialogActionEnum.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/EmailTool.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/InstallId.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/LiveDataExtensions.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/UnitConverter.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/WebpageTool.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/collections/MapExtensions.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/compression/Zipper.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/coroutine/AppCoroutineScope.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/coroutine/CoroutineModule.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/coroutine/DefaultDispatcherProvider.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/coroutine/DispatcherProvider.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/dagger/AndroidModule.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/debug/Bugs.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/debug/autoreport/AutoReporting.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/debug/autoreport/DebugSettings.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/debug/autoreport/bugsnag/BugsnagErrorHandler.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/debug/autoreport/bugsnag/BugsnagLogger.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/debug/autoreport/bugsnag/NOPBugsnagErrorHandler.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/debug/logging/FileLogger.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/debug/logging/LogCatLogger.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/debug/logging/LogExtensions.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/debug/logging/Logging.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/debug/recording/core/Recorder.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/debug/recording/core/RecorderModule.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/debug/recording/core/RecorderService.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/debug/recording/ui/RecorderActivity.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/debug/recording/ui/RecorderActivityVM.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/error/ErrorDialog.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/error/ErrorEventSource.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/error/LocalizedError.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/error/ThrowableExtensions.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/flow/DynamicStateFlow.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/flow/DynamicStateFlowExtensions.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/flow/FlowCombineExtensions.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/flow/FlowExtensions.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/http/HttpModule.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/lists/BaseAdapter.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/lists/BindableVH.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/lists/DataAdapter.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/lists/ItemSwipeTool.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/lists/ListItem.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/lists/RecyclerViewExtensions.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/lists/differ/AsyncDiffer.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/lists/differ/AsyncDifferExtensions.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/lists/differ/DifferItem.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/lists/differ/HasAsyncDiffer.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/lists/modular/ModularAdapter.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/lists/modular/mods/ClickMod.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/lists/modular/mods/DataBinderMod.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/lists/modular/mods/SimpleVHCreatorMod.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/lists/modular/mods/StableIdMod.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/lists/modular/mods/TypedVHCreatorMod.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/livedata/SingleLiveEvent.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/navigation/FragmentExtensions.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/navigation/NavArgsExtensions.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/navigation/NavControllerExtensions.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/navigation/NavDestinationExtensions.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/navigation/NavDirectionsExtensions.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/notifications/PendingIntentCompat.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/preferences/FlowPreference.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/preferences/FlowPreferenceExtension.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/preferences/FlowPreferenceMoshiExtension.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/preferences/PreferenceStoreMapper.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/preferences/Settings.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/preferences/SharedPreferenceExtensions.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/room/CommonConverters.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/serialiation/SerializationModule.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/ui/ConfirmableActionAdapterVH.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/uix/Activity2.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/uix/BottomSheetDialogFragment2.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/uix/Fragment2.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/uix/Fragment3.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/uix/PreferenceFragment2.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/uix/Service2.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/uix/ViewModel1.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/uix/ViewModel2.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/uix/ViewModel3.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/uix/ViewModelLazyKeyed.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/viewbinding/ViewBindingExtensions.kt create mode 100644 app/src/main/java/eu/darken/adsbc/common/worker/WorkerExtensions.kt create mode 100644 app/src/main/java/eu/darken/adsbc/enterprise/core/EnterpriseSettings.kt create mode 100644 app/src/main/java/eu/darken/adsbc/feeder/core/ADSBxId.kt create mode 100644 app/src/main/java/eu/darken/adsbc/feeder/core/Feeder.kt create mode 100644 app/src/main/java/eu/darken/adsbc/feeder/core/FeederId.kt create mode 100644 app/src/main/java/eu/darken/adsbc/feeder/core/FeederRepo.kt create mode 100644 app/src/main/java/eu/darken/adsbc/feeder/core/FeederRepoExtensions.kt create mode 100644 app/src/main/java/eu/darken/adsbc/feeder/core/FeederSettings.kt create mode 100644 app/src/main/java/eu/darken/adsbc/feeder/core/FeederSetupRequest.kt create mode 100644 app/src/main/java/eu/darken/adsbc/feeder/core/FeederUpdateService.kt create mode 100644 app/src/main/java/eu/darken/adsbc/feeder/core/SpottedAircraft.kt create mode 100644 app/src/main/java/eu/darken/adsbc/feeder/core/storage/FeederDao.kt create mode 100644 app/src/main/java/eu/darken/adsbc/feeder/core/storage/FeederData.kt create mode 100644 app/src/main/java/eu/darken/adsbc/feeder/core/storage/FeederDatabase.kt create mode 100644 app/src/main/java/eu/darken/adsbc/feeder/core/storage/FeederStorage.kt create mode 100644 app/src/main/java/eu/darken/adsbc/feeder/ui/list/FeederAdapter.kt create mode 100644 app/src/main/java/eu/darken/adsbc/feeder/ui/list/FeederListFragment.kt create mode 100644 app/src/main/java/eu/darken/adsbc/feeder/ui/list/FeederListFragmentVM.kt create mode 100644 app/src/main/java/eu/darken/adsbc/feeder/ui/list/actions/ActionsAdapter.kt create mode 100644 app/src/main/java/eu/darken/adsbc/feeder/ui/list/actions/FeederAction.kt create mode 100644 app/src/main/java/eu/darken/adsbc/feeder/ui/list/actions/FeederActionDialog.kt create mode 100644 app/src/main/java/eu/darken/adsbc/feeder/ui/list/actions/FeederActionDialogVM.kt create mode 100644 app/src/main/java/eu/darken/adsbc/main/App.kt create mode 100644 app/src/main/java/eu/darken/adsbc/main/core/GeneralSettings.kt create mode 100644 app/src/main/java/eu/darken/adsbc/main/core/SomeRepo.kt create mode 100644 app/src/main/java/eu/darken/adsbc/main/core/receiver/ExampleReceiver.kt create mode 100644 app/src/main/java/eu/darken/adsbc/main/core/service/ExampleBinder.kt create mode 100644 app/src/main/java/eu/darken/adsbc/main/core/service/ExampleService.kt create mode 100644 app/src/main/java/eu/darken/adsbc/main/ui/MainActivity.kt create mode 100644 app/src/main/java/eu/darken/adsbc/main/ui/MainActivityVM.kt create mode 100644 app/src/main/java/eu/darken/adsbc/main/ui/main/MainFragment.kt create mode 100644 app/src/main/java/eu/darken/adsbc/main/ui/main/MainFragmentVM.kt create mode 100644 app/src/main/java/eu/darken/adsbc/main/ui/main/MainPagerAdapter.kt create mode 100644 app/src/main/java/eu/darken/adsbc/main/ui/settings/SettingsFragment.kt create mode 100644 app/src/main/java/eu/darken/adsbc/main/ui/settings/SettingsFragmentVM.kt create mode 100644 app/src/main/java/eu/darken/adsbc/main/ui/settings/SettingsIndexFragment.kt create mode 100644 app/src/main/java/eu/darken/adsbc/main/ui/settings/acks/AcknowledgementsFragment.kt create mode 100644 app/src/main/java/eu/darken/adsbc/main/ui/settings/acks/AcknowledgementsFragmentVM.kt create mode 100644 app/src/main/java/eu/darken/adsbc/main/ui/settings/general/GeneralSettingsFragment.kt create mode 100644 app/src/main/java/eu/darken/adsbc/main/ui/settings/general/GeneralSettingsFragmentVM.kt create mode 100644 app/src/main/java/eu/darken/adsbc/main/ui/settings/general/advanced/AdvancedSettingsFragment.kt create mode 100644 app/src/main/java/eu/darken/adsbc/main/ui/settings/general/advanced/AdvancedSettingsFragmentVM.kt create mode 100644 app/src/main/java/eu/darken/adsbc/main/ui/settings/support/SupportFragment.kt create mode 100644 app/src/main/java/eu/darken/adsbc/main/ui/settings/support/SupportFragmentVM.kt create mode 100644 app/src/main/java/eu/darken/adsbc/stats/core/api/ADSBxStatsApi.kt create mode 100644 app/src/main/java/eu/darken/adsbc/stats/core/api/ADSBxStatsEndpoint.kt create mode 100644 app/src/main/java/eu/darken/adsbc/stats/core/storage/FeederStatsDao.kt create mode 100644 app/src/main/java/eu/darken/adsbc/stats/core/storage/FeederStatsData.kt create mode 100644 app/src/main/java/eu/darken/adsbc/stats/core/storage/StatsDatabase.kt create mode 100644 app/src/main/java/eu/darken/adsbc/stats/core/storage/StatsStorage.kt create mode 100644 app/src/main/java/eu/darken/adsbc/stats/core/storage/StatsStorageExtensions.kt create mode 100644 app/src/main/res/drawable-v24/ic_launcher_foreground.xml create mode 100644 app/src/main/res/drawable/ic_baseline_add_circle_outline_24.xml create mode 100644 app/src/main/res/drawable/ic_baseline_arrow_back_24.xml create mode 100644 app/src/main/res/drawable/ic_baseline_av_timer_24.xml create mode 100644 app/src/main/res/drawable/ic_baseline_bug_report_24.xml create mode 100644 app/src/main/res/drawable/ic_baseline_business_center_24.xml create mode 100644 app/src/main/res/drawable/ic_baseline_delete_sweep_24.xml create mode 100644 app/src/main/res/drawable/ic_baseline_drive_file_rename_outline_24.xml create mode 100644 app/src/main/res/drawable/ic_baseline_info_24.xml create mode 100644 app/src/main/res/drawable/ic_baseline_radio_24.xml create mode 100644 app/src/main/res/drawable/ic_baseline_refresh_24.xml create mode 100644 app/src/main/res/drawable/ic_baseline_science_24.xml create mode 100644 app/src/main/res/drawable/ic_baseline_settings_24.xml create mode 100644 app/src/main/res/drawable/ic_card_text_onsurface.xml create mode 100644 app/src/main/res/drawable/ic_changelog_onsurface.xml create mode 100644 app/src/main/res/drawable/ic_email_onsurface.xml create mode 100644 app/src/main/res/drawable/ic_heart.xml create mode 100644 app/src/main/res/drawable/ic_id_onsurface.xml create mode 100644 app/src/main/res/drawable/ic_spider_thread_onsurface.xml create mode 100644 app/src/main/res/drawable/ic_twitter.xml create mode 100644 app/src/main/res/drawable/launch_screen.xml create mode 100644 app/src/main/res/layout/aircraft_list_action_dialog.xml create mode 100644 app/src/main/res/layout/aircraft_list_fragment.xml create mode 100644 app/src/main/res/layout/aircraft_list_line.xml create mode 100644 app/src/main/res/layout/debug_recording_activity.xml create mode 100644 app/src/main/res/layout/feeder_list_action_dialog.xml create mode 100644 app/src/main/res/layout/feeder_list_fragment.xml create mode 100644 app/src/main/res/layout/feeder_list_input_dialog.xml create mode 100644 app/src/main/res/layout/feeder_list_line.xml create mode 100644 app/src/main/res/layout/feeder_list_rename_dialog.xml create mode 100644 app/src/main/res/layout/main_activity.xml create mode 100644 app/src/main/res/layout/main_fragment.xml create mode 100644 app/src/main/res/layout/settings_fragment.xml create mode 100644 app/src/main/res/layout/settings_number_input_dialog.xml create mode 100644 app/src/main/res/layout/view_action_adapter_line.xml create mode 100644 app/src/main/res/menu/menu_main.xml create mode 100644 app/src/main/res/menu/menu_settings_index.xml create mode 100644 app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml create mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png create mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png create mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png create mode 100644 app/src/main/res/navigation/main.xml create mode 100644 app/src/main/res/values-night/themes.xml create mode 100644 app/src/main/res/values/colors.xml create mode 100644 app/src/main/res/values/ic_launcher_background.xml create mode 100644 app/src/main/res/values/strings.xml create mode 100644 app/src/main/res/values/styles.xml create mode 100644 app/src/main/res/values/themes.xml create mode 100644 app/src/main/res/xml/preferences_acknowledgements.xml create mode 100644 app/src/main/res/xml/preferences_advanced.xml create mode 100644 app/src/main/res/xml/preferences_general.xml create mode 100644 app/src/main/res/xml/preferences_index.xml create mode 100644 app/src/main/res/xml/preferences_support.xml create mode 100644 app/src/test/java/eu/darken/adsbc/common/flow/DynamicStateFlowTest.kt create mode 100644 app/src/test/java/eu/darken/adsbc/common/preferences/FlowPreferenceMoshiTest.kt create mode 100644 app/src/test/java/eu/darken/adsbc/common/preferences/FlowPreferenceTest.kt create mode 100644 app/src/test/java/testhelper/BaseTest.kt create mode 100644 app/src/test/java/testhelper/coroutine/CoroutinesTestExtension.kt create mode 100644 app/src/test/java/testhelper/coroutine/TestDispatcherProvider.kt create mode 100644 app/src/test/java/testhelper/coroutine/TestExtensions.kt create mode 100644 app/src/test/java/testhelper/flow/FlowTest.kt create mode 100644 app/src/test/java/testhelper/json/JsonExtensions.kt create mode 100644 app/src/test/java/testhelper/livedata/InstantExecutorExtension.kt create mode 100644 app/src/test/java/testhelper/preferences/MockSharedPreferencesTest.kt create mode 100644 app/src/testShared/java/testhelpers/BaseTestInstrumentation.kt create mode 100644 app/src/testShared/java/testhelpers/IsAUnitTest.kt create mode 100644 app/src/testShared/java/testhelpers/logging/JUnitLogger.kt create mode 100644 app/src/testShared/java/testhelpers/preferences/MockFlowPreference.kt create mode 100644 app/src/testShared/java/testhelpers/preferences/MockSharedPreferences.kt create mode 100644 build.gradle create mode 100644 gradle.properties create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100644 gradlew.bat create mode 100644 settings.gradle diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..cf66c0b --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,4 @@ +# These are supported funding model platforms + +custom: + - "https://www.buymeacoffee.com/tydarken" diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..e9cbf53 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,29 @@ +--- +name: Bug report about: Create a report to help us improve title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: + +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Setup** + +- Android version: [e.g. Android 11] +- App version [e.g. v1.0.0] +- Gear type [e.g. FPV Goggles V1] diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml new file mode 100644 index 0000000..8d1dbfb --- /dev/null +++ b/.github/workflows/android.yml @@ -0,0 +1,28 @@ +name: Android CI + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: set up JDK 11 + uses: actions/setup-java@v2 + with: + java-version: '11' + distribution: 'adopt' + cache: gradle + + - name: Grant execute permission for gradlew + run: chmod +x gradlew + - name: Run tests + run: ./gradlew testRelease + - name: Build with Gradle + run: ./gradlew assembleDebug diff --git a/.github/workflows/gradle-wrapper-validation.yml b/.github/workflows/gradle-wrapper-validation.yml new file mode 100644 index 0000000..8115a4c --- /dev/null +++ b/.github/workflows/gradle-wrapper-validation.yml @@ -0,0 +1,17 @@ +name: "Validate Gradle Wrapper" + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + validation: + name: "Validation" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: gradle/wrapper-validation-action@v1 \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fbb2bb0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +*.iml +.gradle +/local.properties +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +/.idea/**/* +!/.idea/codeStyles/ +!/.idea/codeStyles/**/* +*.jks +.local/* diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..b8beb1c --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,252 @@ +plugins { + id 'com.android.application' + id 'kotlin-android' + id 'kotlin-kapt' + id 'kotlin-parcelize' +} + +apply plugin: 'dagger.hilt.android.plugin' +apply plugin: 'androidx.navigation.safeargs.kotlin' + +def gitSha = 'git rev-parse --short HEAD'.execute([], project.rootDir).text.trim() +def buildTime = new Date().format("yyyy-MM-dd'T'HH:mm:ss'Z'", TimeZone.getTimeZone("GMT+1")) + +android { + def packageName = "eu.darken.adsbc" + + compileSdkVersion buildConfig.compileSdk + + Properties bugsnagProps = new Properties() + def bugsnagPropsFile = new File(System.properties['user.home'], ".appconfig/${packageName}/bugsnag.properties") + if (bugsnagPropsFile.canRead()) bugsnagProps.load(new FileInputStream(bugsnagPropsFile)) + + defaultConfig { + applicationId "${packageName}" + + minSdkVersion buildConfig.minSdk + targetSdkVersion buildConfig.targetSdk + + versionCode buildConfig.version.code + versionName buildConfig.version.name + + testInstrumentationRunner "eu.darken.adsbc.HiltTestRunner" + + buildConfigField "String", "GITSHA", "\"${gitSha}\"" + buildConfigField "String", "BUILDTIME", "\"${buildTime}\"" + + manifestPlaceholders = [bugsnagApiKey: "fake"] + } + + signingConfigs { + release {} + } + def signingPropFile = new File(System.properties['user.home'], ".appconfig/${packageName}/signing.properties") + if (signingPropFile.canRead()) { + Properties signingProps = new Properties() + signingProps.load(new FileInputStream(signingPropFile)) + signingConfigs { + release { + storeFile new File(signingProps['release.storePath']) + keyAlias signingProps['release.keyAlias'] + storePassword signingProps['release.storePassword'] + keyPassword signingProps['release.keyPassword'] + } + } + } + + buildTypes { + def proguardRulesRelease = fileTree(dir: "../proguard", include: ["*.pro"]).asList().toArray() + debug { + minifyEnabled false + shrinkResources false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt') + proguardFiles proguardRulesRelease + proguardFiles 'proguard-rules-debug.pro' + } + release { + signingConfig signingConfigs.release + lintOptions { + abortOnError true + fatal 'StopShip' + } + minifyEnabled true + shrinkResources true + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt') + proguardFiles proguardRulesRelease + } + applicationVariants.all { variant -> + if (variant.buildType.name == "debug") { + variant.mergedFlavor.resourceConfigurations.clear() + variant.mergedFlavor.resourceConfigurations.add("en") + variant.mergedFlavor.resourceConfigurations.add("de") + } else if (variant.buildType.name != "debug") { + variant.outputs.each { output -> + output.outputFileName = "${packageName}-v" + defaultConfig.versionName + "(" + defaultConfig.versionCode + ")-" + variant.buildType.name.toUpperCase() + "-" + gitSha + ".apk" + } + } + } + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = '1.8' + } + + buildFeatures { + viewBinding true + } + + tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { + kotlinOptions { + jvmTarget = "1.8" + + freeCompilerArgs += [ + "-Xuse-experimental=kotlinx.coroutines.ExperimentalCoroutinesApi", + "-Xuse-experimental=kotlinx.coroutines.FlowPreview", + "-Xuse-experimental=kotlin.time.ExperimentalTime", + "-Xopt-in=kotlin.RequiresOptIn" + ] + } + } + + testOptions { + unitTests.all { + useJUnitPlatform() + } + unitTests { + includeAndroidResources = true + } + } + + sourceSets { + test { + java.srcDirs += "$projectDir/src/testShared/java" + } + androidTest { + java.srcDirs += "$projectDir/src/testShared/java" + androidTest.assets.srcDirs += files("$projectDir/schemas".toString()) + } + } + + gradle.projectsEvaluated { + tasks.withType(JavaCompile) { + options.compilerArgs << "-Xmaxerrs" << "1000" // or whatever number you want + } + } +} + +dependencies { + // Kotlin + implementation "org.jetbrains.kotlin:kotlin-stdlib:${versions.kotlin.core}" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:${versions.kotlin.coroutines}" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:${versions.kotlin.coroutines}" + + testImplementation "org.jetbrains.kotlin:kotlin-reflect:${versions.kotlin.core}" + testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:${versions.kotlin.coroutines}" + androidTestImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:${versions.kotlin.coroutines}") { + // conflicts with mockito due to direct inclusion of byte buddy + exclude group: "org.jetbrains.kotlinx", module: "kotlinx-coroutines-debug" + } + + implementation("org.jsoup:jsoup:1.14.3") + + // Debugging + implementation ('com.bugsnag:bugsnag-android:5.9.2') + implementation 'com.getkeepsafe.relinker:relinker:1.4.3' + + implementation("com.squareup.moshi:moshi:1.13.0") + kapt("com.squareup.moshi:moshi-kotlin-codegen:1.13.0") + + // DI + implementation "com.google.dagger:dagger:${versions.dagger.core}" + implementation "com.google.dagger:dagger-android:${versions.dagger.core}" + + kapt "com.google.dagger:dagger-compiler:${versions.dagger.core}" + kapt "com.google.dagger:dagger-android-processor:${versions.dagger.core}" + + implementation "com.google.dagger:hilt-android:${versions.dagger.core}" + kapt "com.google.dagger:hilt-android-compiler:${versions.dagger.core}" + + testImplementation "com.google.dagger:hilt-android-testing:${versions.dagger.core}" + kaptTest "com.google.dagger:hilt-android-compiler:${versions.dagger.core}" + + androidTestImplementation "com.google.dagger:hilt-android-testing:${versions.dagger.core}" + kaptAndroidTest "com.google.dagger:hilt-android-compiler:${versions.dagger.core}" + + // Room + implementation "androidx.room:room-runtime:2.4.1" + kapt "androidx.room:room-compiler:2.4.1" + implementation "androidx.room:room-ktx:2.4.1" + + implementation 'com.squareup.retrofit2:retrofit:2.9.0' + implementation 'com.squareup.retrofit2:converter-moshi:2.9.0' + implementation "com.squareup.okhttp3:logging-interceptor:4.9.1" + + // Support libs + implementation 'androidx.core:core-ktx:1.7.0' + implementation 'androidx.appcompat:appcompat:1.4.1' + implementation 'androidx.annotation:annotation:1.3.0' + + implementation 'androidx.activity:activity-ktx:1.4.0' + implementation 'androidx.fragment:fragment-ktx:1.4.1' + + implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' + implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0' + implementation 'androidx.lifecycle:lifecycle-viewmodel-savedstate:2.4.0' + implementation 'androidx.lifecycle:lifecycle-common-java8:2.4.0' + implementation 'androidx.lifecycle:lifecycle-process:2.4.0' + implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.4.0' + + implementation "androidx.navigation:navigation-fragment-ktx:${versions.androidx.navigation}" + implementation "androidx.navigation:navigation-ui-ktx:${versions.androidx.navigation}" + + implementation 'androidx.preference:preference-ktx:1.2.0' + + implementation 'androidx.core:core-splashscreen:1.0.0-beta01' + + def work_version = "2.7.1" + implementation "androidx.work:work-runtime:${work_version}" + testImplementation "androidx.work:work-testing:${work_version}" + + // UI + implementation 'androidx.constraintlayout:constraintlayout:2.1.3' + implementation 'com.google.android.material:material:1.6.0-alpha02' + + // Testing + testImplementation 'junit:junit:4.13.2' + testImplementation "org.junit.vintage:junit-vintage-engine:5.7.1" + testImplementation "androidx.test:core-ktx:1.4.0" + + testImplementation "io.mockk:mockk:1.12.1" + androidTestImplementation "io.mockk:mockk-android:1.11.0" + + testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.7.1" + testImplementation "org.junit.jupiter:junit-jupiter-api:5.7.1" + testImplementation "org.junit.jupiter:junit-jupiter-params:5.7.1" + + androidTestImplementation "androidx.navigation:navigation-testing:${versions.androidx.navigation}" + + testImplementation "io.kotest:kotest-runner-junit5:4.6.2" + testImplementation "io.kotest:kotest-assertions-core-jvm:4.6.2" + testImplementation "io.kotest:kotest-property-jvm:4.6.2" + androidTestImplementation "io.kotest:kotest-assertions-core-jvm:4.6.2" + androidTestImplementation "io.kotest:kotest-property-jvm:4.6.2" + + testImplementation 'android.arch.core:core-testing:1.1.1' + androidTestImplementation 'android.arch.core:core-testing:1.1.1' + debugImplementation 'androidx.test:core-ktx:1.4.0' + + androidTestImplementation 'androidx.test.ext:junit:1.1.3' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' + + androidTestImplementation 'androidx.test:runner:1.4.0' + androidTestImplementation 'androidx.test:rules:1.4.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' + androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.4.0' + androidTestImplementation 'androidx.test.espresso:espresso-intents:3.4.0' + androidTestImplementation 'androidx.test.espresso.idling:idling-concurrent:3.4.0' +} \ No newline at end of file diff --git a/app/proguard-rules-debug.pro b/app/proguard-rules-debug.pro new file mode 100644 index 0000000..0674e77 --- /dev/null +++ b/app/proguard-rules-debug.pro @@ -0,0 +1 @@ +-dontobfuscate \ No newline at end of file diff --git a/app/proguard/proguard-rules.pro b/app/proguard/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/app/proguard/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/app/src/androidTest/java/eu/darken/adsbc/ExampleInstrumentedTest.kt b/app/src/androidTest/java/eu/darken/adsbc/ExampleInstrumentedTest.kt new file mode 100644 index 0000000..6b9ede3 --- /dev/null +++ b/app/src/androidTest/java/eu/darken/adsbc/ExampleInstrumentedTest.kt @@ -0,0 +1,22 @@ +package eu.darken.adsbc + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("eu.darken.adsbc", appContext.packageName) + } +} \ No newline at end of file diff --git a/app/src/androidTest/java/eu/darken/adsbc/HiltTestRunner.kt b/app/src/androidTest/java/eu/darken/adsbc/HiltTestRunner.kt new file mode 100644 index 0000000..b313f08 --- /dev/null +++ b/app/src/androidTest/java/eu/darken/adsbc/HiltTestRunner.kt @@ -0,0 +1,13 @@ +package eu.darken.adsbc + +import android.app.Application +import android.content.Context +import androidx.test.runner.AndroidJUnitRunner +import dagger.hilt.android.testing.HiltTestApplication + +class HiltTestRunner : AndroidJUnitRunner() { + + override fun newApplication(cl: ClassLoader?, name: String?, context: Context?): Application { + return super.newApplication(cl, HiltTestApplication::class.java.name, context) + } +} \ No newline at end of file diff --git a/app/src/androidTest/java/eu/darken/adsbc/main/MainActivityTest.kt b/app/src/androidTest/java/eu/darken/adsbc/main/MainActivityTest.kt new file mode 100644 index 0000000..5c4f9f6 --- /dev/null +++ b/app/src/androidTest/java/eu/darken/adsbc/main/MainActivityTest.kt @@ -0,0 +1,43 @@ +package eu.darken.adsbc.main + +import android.arch.core.executor.testing.InstantTaskExecutorRule +import androidx.lifecycle.liveData +import androidx.test.core.app.launchActivity +import dagger.hilt.android.testing.BindValue +import dagger.hilt.android.testing.HiltAndroidRule +import dagger.hilt.android.testing.HiltAndroidTest +import eu.darken.adsbc.main.ui.MainActivity +import eu.darken.adsbc.main.ui.MainActivityVM +import io.mockk.* +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import testhelper.BaseUITest + +@HiltAndroidTest +class MainActivityTest : BaseUITest() { + + @get:Rule(order = 0) + var hiltRule = HiltAndroidRule(this) + + @get:Rule(order = 1) + var instantTaskExecutorRule = InstantTaskExecutorRule() + + @BindValue + val mockViewModel = mockk(relaxed = true) + + @Before fun init() { + hiltRule.inject() + + mockViewModel.apply { + every { state } returns liveData { } + every { onGo() } just Runs + } + } + + @Test fun happyPath() { + launchActivity() + + verify { mockViewModel.onGo() } + } +} \ No newline at end of file diff --git a/app/src/androidTest/java/eu/darken/adsbc/main/fragment/ExampleFragmentTest.kt b/app/src/androidTest/java/eu/darken/adsbc/main/fragment/ExampleFragmentTest.kt new file mode 100644 index 0000000..90a1f1d --- /dev/null +++ b/app/src/androidTest/java/eu/darken/adsbc/main/fragment/ExampleFragmentTest.kt @@ -0,0 +1,44 @@ +package eu.darken.adsbc.main.fragment + +import android.arch.core.executor.testing.InstantTaskExecutorRule +import androidx.lifecycle.liveData +import dagger.hilt.android.testing.BindValue +import dagger.hilt.android.testing.HiltAndroidRule +import dagger.hilt.android.testing.HiltAndroidTest +import eu.darken.adsbc.main.ui.main.MainFragment +import eu.darken.adsbc.main.ui.main.MainFragmentVM +import io.mockk.every +import io.mockk.mockk +import io.mockk.verify +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import testhelper.BaseUITest +import testhelper.launchFragmentInHiltContainer + +@HiltAndroidTest +class ExampleFragmentTest : BaseUITest() { + + @get:Rule(order = 0) + var hiltRule = HiltAndroidRule(this) + + @get:Rule(order = 1) + var instantTaskExecutorRule = InstantTaskExecutorRule() + + @BindValue + val mockViewModel = mockk(relaxed = true) + + @Before fun init() { + hiltRule.inject() + + mockViewModel.apply { + every { state } returns liveData { } + } + } + + @Test fun happyPath() { + launchFragmentInHiltContainer() + + verify { mockViewModel.state } + } +} \ No newline at end of file diff --git a/app/src/androidTest/java/testhelper/BaseUITest.kt b/app/src/androidTest/java/testhelper/BaseUITest.kt new file mode 100644 index 0000000..3d4da5c --- /dev/null +++ b/app/src/androidTest/java/testhelper/BaseUITest.kt @@ -0,0 +1,5 @@ +package testhelper + +import testhelpers.BaseTestInstrumentation + +abstract class BaseUITest : BaseTestInstrumentation() diff --git a/app/src/androidTest/java/testhelper/EmptyFragmentActivity.kt b/app/src/androidTest/java/testhelper/EmptyFragmentActivity.kt new file mode 100644 index 0000000..de4f51f --- /dev/null +++ b/app/src/androidTest/java/testhelper/EmptyFragmentActivity.kt @@ -0,0 +1,41 @@ +package testhelper + +import android.annotation.SuppressLint +import android.os.Bundle +import androidx.annotation.Nullable +import androidx.fragment.app.FragmentActivity +import androidx.fragment.app.FragmentFactory +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider + + +class EmptyFragmentActivity : FragmentActivity() { + @SuppressLint("RestrictedApi") override fun onCreate(@Nullable savedInstanceState: Bundle?) { + FragmentFactoryHolderViewModel.getInstance(this).fragmentFactory?.let { + supportFragmentManager.fragmentFactory = it + } + + super.onCreate(savedInstanceState) + } +} + +class FragmentFactoryHolderViewModel : ViewModel() { + var fragmentFactory: FragmentFactory? = null + + override fun onCleared() { + super.onCleared() + fragmentFactory = null + } + + companion object { + fun getInstance(activity: FragmentActivity): FragmentFactoryHolderViewModel { + return ViewModelProvider(activity, FACTORY)[FragmentFactoryHolderViewModel::class.java] + } + + private val FACTORY: ViewModelProvider.Factory = object : ViewModelProvider.Factory { + override fun create(modelClass: Class): T { + return FragmentFactoryHolderViewModel() as T + } + } + } +} diff --git a/app/src/androidTest/java/testhelper/HiltExtensions.kt b/app/src/androidTest/java/testhelper/HiltExtensions.kt new file mode 100644 index 0000000..b39c3b7 --- /dev/null +++ b/app/src/androidTest/java/testhelper/HiltExtensions.kt @@ -0,0 +1,39 @@ +package testhelper + + +import android.content.ComponentName +import android.content.Intent +import android.os.Bundle +import androidx.fragment.app.Fragment +import androidx.test.core.app.ActivityScenario +import androidx.test.core.app.ApplicationProvider +import eu.darken.adsbc.HiltTestActivity + +/** + * https://developer.android.com/training/dependency-injection/hilt-testing#launchfragment + */ +inline fun launchFragmentInHiltContainer( + fragmentArgs: Bundle? = null, + crossinline action: Fragment.() -> Unit = {} +) { + val startActivityIntent = Intent.makeMainActivity( + ComponentName( + ApplicationProvider.getApplicationContext(), + HiltTestActivity::class.java + ) + ) + + ActivityScenario.launch(startActivityIntent).onActivity { activity -> + activity.supportFragmentManager.fragmentFactory.instantiate(T::class.java.classLoader!!, T::class.java.name) + .apply { + arguments = fragmentArgs + + activity.supportFragmentManager + .beginTransaction() + .add(android.R.id.content, this, "") + .commitNow() + + action() + } + } +} \ No newline at end of file diff --git a/app/src/debug/AndroidManifest.xml b/app/src/debug/AndroidManifest.xml new file mode 100644 index 0000000..fc13ae3 --- /dev/null +++ b/app/src/debug/AndroidManifest.xml @@ -0,0 +1,10 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/debug/java/eu/darken/adsbc/HiltTestActivity.kt b/app/src/debug/java/eu/darken/adsbc/HiltTestActivity.kt new file mode 100644 index 0000000..c9f976c --- /dev/null +++ b/app/src/debug/java/eu/darken/adsbc/HiltTestActivity.kt @@ -0,0 +1,7 @@ +package eu.darken.adsbc + +import androidx.appcompat.app.AppCompatActivity +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class HiltTestActivity : AppCompatActivity() \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..21a233a --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/ic_launcher-playstore.png b/app/src/main/ic_launcher-playstore.png new file mode 100644 index 0000000000000000000000000000000000000000..a64c0233bd4cf9191782054fa14b4830fe4cffef GIT binary patch literal 204975 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%0Ax{&V#0_;A|c8C~=XaQa=$oTzZ? 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%0D!AXupXQ#WTuLCNw5-ldL&uTqv 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ᐴcxQmVrf@)G76wv<2ugH4Mo$NkQ%ODAfB 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 literal 0 HcmV?d00001 diff --git a/app/src/main/java/eu/darken/adsbc/aircraft/core/Aircraft.kt b/app/src/main/java/eu/darken/adsbc/aircraft/core/Aircraft.kt new file mode 100644 index 0000000..c5503fc --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/aircraft/core/Aircraft.kt @@ -0,0 +1,21 @@ +package eu.darken.adsbc.aircraft.core + +import java.time.Instant + +interface Aircraft { + val hexCode: String + val lastSeenAt: Instant + val totalMessages: Int + val reception: Float + val messageRate: Float + + val callsign: String? + + val squawkCode: String? + + val altitudeMeter: Long? + + val airSpeedKmh: Int? + + val distanceKmh: Float? +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/aircraft/core/AircraftId.kt b/app/src/main/java/eu/darken/adsbc/aircraft/core/AircraftId.kt new file mode 100644 index 0000000..60eacf7 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/aircraft/core/AircraftId.kt @@ -0,0 +1,11 @@ +package eu.darken.adsbc.aircraft.core + +import android.os.Parcelable +import androidx.annotation.Keep +import com.squareup.moshi.JsonClass +import kotlinx.parcelize.Parcelize + +@Parcelize +@Keep +@JsonClass(generateAdapter = true) +data class AircraftId(val id: String) : Parcelable \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/aircraft/core/AircraftStorage.kt b/app/src/main/java/eu/darken/adsbc/aircraft/core/AircraftStorage.kt new file mode 100644 index 0000000..3ac18b1 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/aircraft/core/AircraftStorage.kt @@ -0,0 +1,46 @@ +package eu.darken.adsbc.aircraft.core + +import eu.darken.adsbc.common.collections.mutate +import eu.darken.adsbc.common.coroutine.AppScope +import eu.darken.adsbc.common.coroutine.DispatcherProvider +import eu.darken.adsbc.common.debug.logging.log +import eu.darken.adsbc.common.debug.logging.logTag +import eu.darken.adsbc.common.flow.DynamicStateFlow +import eu.darken.adsbc.feeder.core.FeederId +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.plus +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class AircraftStorage @Inject constructor( + @AppScope private val appScope: CoroutineScope, + private val dispatcherProvider: DispatcherProvider, +) { + private val data = DynamicStateFlow>>( + parentScope = appScope + dispatcherProvider.IO + ) { + emptyMap() + } + + val allAircraft: Flow>> = data.flow + + suspend fun updateData(feederId: FeederId, aircraft: Collection) { + log(TAG) { "Updating aircraft for $feederId: ${aircraft.size} items" } + data.updateBlocking { + mutate { this[feederId] = aircraft } + } + } + + suspend fun deleteByFeederId(feederId: FeederId) { + log(TAG) { "Deleting all aircraft for $feederId" } + data.updateBlocking { + mutate { remove(feederId) } + } + } + + companion object { + private val TAG = logTag("Aircraft", "Storage") + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/aircraft/core/BaseAircraft.kt b/app/src/main/java/eu/darken/adsbc/aircraft/core/BaseAircraft.kt new file mode 100644 index 0000000..29ff3e3 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/aircraft/core/BaseAircraft.kt @@ -0,0 +1,16 @@ +package eu.darken.adsbc.aircraft.core + +import java.time.Instant + +data class BaseAircraft( + override val hexCode: String, + override val lastSeenAt: Instant, + override val reception: Float, + override val totalMessages: Int, + override val messageRate: Float, + override val callsign: String? = null, + override val squawkCode: String? = null, + override val altitudeMeter: Long? = null, + override val airSpeedKmh: Int? = null, + override val distanceKmh: Float? = null +) : Aircraft \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/aircraft/core/FeederAircraft.kt b/app/src/main/java/eu/darken/adsbc/aircraft/core/FeederAircraft.kt new file mode 100644 index 0000000..5552c1c --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/aircraft/core/FeederAircraft.kt @@ -0,0 +1,8 @@ +package eu.darken.adsbc.aircraft.core + +import eu.darken.adsbc.feeder.core.FeederId + +data class FeederAircraft( + val feederId: FeederId, + val aircraft: Aircraft +) diff --git a/app/src/main/java/eu/darken/adsbc/aircraft/core/api/ADSBxAircraftApi.kt b/app/src/main/java/eu/darken/adsbc/aircraft/core/api/ADSBxAircraftApi.kt new file mode 100644 index 0000000..b37333b --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/aircraft/core/api/ADSBxAircraftApi.kt @@ -0,0 +1,76 @@ +package eu.darken.adsbc.aircraft.core.api + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import okhttp3.ResponseBody +import retrofit2.http.GET +import retrofit2.http.Query + +interface ADSBxAircraftApi { + + @JsonClass(generateAdapter = true) + data class Error( + @Json(name = "error") val error: String, // {"error": "Data Not Found - Anywhere ID [124124asd]"} + ) + + @GET("/uuid/?") + suspend fun getAircraft(@Query("feed") feedId: String): ResponseBody + + @JsonClass(generateAdapter = true) + data class AircraftPayload( + @Json(name = "now") val now: Double, + @Json(name = "messages") val messages: Int, + @Json(name = "aircraft") val aircraft: List, + ) { + + @JsonClass(generateAdapter = true) + data class TrackingData( + @Json(name = "hex") val hex: String, //"040170" + @Json(name = "type") val type: String, //"adsb_icao" + @Json(name = "flight") val flight: String?, //"ETH701 " + @Json(name = "alt_baro") val alt_baro: String?, //37000|ground + @Json(name = "alt_geom") val alt_geom: Long?, //37025 + @Json(name = "gs") val gs: Double?, //581.4 + @Json(name = "ias") val ias: Int?, //276 + @Json(name = "tas") val tas: Int?, //476 + @Json(name = "mach") val mach: Double?, //0.844 + @Json(name = "wd") val wd: Int?, //329 + @Json(name = "ws") val ws: Int?, //132 + @Json(name = "oat") val oat: Long?, //-64 + @Json(name = "tat") val tat: Long?, //-34 + @Json(name = "track") val track: Double?, //115.9 + @Json(name = "track_rate") val track_rate: Float?, //0.0 + @Json(name = "roll") val roll: Float?, //-0.18 + @Json(name = "mag_heading") val mag_heading: Float?, //104.59 + @Json(name = "true_heading") val true_heading: Float?, //107.22 + @Json(name = "baro_rate") val baro_rate: Int?, //0 + @Json(name = "geom_rate") val geom_rate: Int?, //0 + @Json(name = "squawk") val squawk: String?, //"2271" + @Json(name = "emergency") val emergency: String?, //"none" + @Json(name = "category") val category: String?, //"A5" + @Json(name = "nav_qnh") val nav_qnh: Float?, //1013.6 + @Json(name = "nav_altitude_mcp") val nav_altitude_mcp: Int?, //36992 + @Json(name = "nav_heading") val nav_heading: Float?, //104.77 + @Json(name = "lat") val lat: Float?, //49.717464 + @Json(name = "lon") val lon: Float?, //6.801576 + @Json(name = "nic") val nic: Int?, //8 + @Json(name = "rc") val rc: Int?, //186 + @Json(name = "seen_pos") val seen_pos: Float?, //0.6 + @Json(name = "version") val version: Int?, //2 + @Json(name = "nic_baro") val nic_baro: Int?, //1 + @Json(name = "nac_p") val nac_p: Int?, //9 + @Json(name = "nac_v") val nac_v: Int?, //2 + @Json(name = "sil") val sil: Int?, //3 + @Json(name = "sil_type") val sil_type: String?, //"perhour" + @Json(name = "gva") val gva: Int?, //2 + @Json(name = "sda") val sda: Int?, //2 + @Json(name = "alert") val alert: Int?, //0 + @Json(name = "spi") val spi: Int?, //0 + @Json(name = "mlat") val mlat: List, // ["lat","lon","nic","rc","nac_p","nac_v","sil","sil_type"] + @Json(name = "tisb") val tisb: List, + @Json(name = "messages") val messages: Int, //1536 + @Json(name = "seen") val seen: Float, //0.6 + @Json(name = "rssi") val rssi: Float, //-29.3 + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/aircraft/core/api/ADSBxAircraftEndpoint.kt b/app/src/main/java/eu/darken/adsbc/aircraft/core/api/ADSBxAircraftEndpoint.kt new file mode 100644 index 0000000..d56c8bf --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/aircraft/core/api/ADSBxAircraftEndpoint.kt @@ -0,0 +1,77 @@ +package eu.darken.adsbc.aircraft.core.api + +import com.squareup.moshi.Moshi +import dagger.Reusable +import eu.darken.adsbc.aircraft.core.Aircraft +import eu.darken.adsbc.aircraft.core.BaseAircraft +import eu.darken.adsbc.common.coroutine.DispatcherProvider +import eu.darken.adsbc.common.debug.logging.log +import eu.darken.adsbc.common.debug.logging.logTag +import eu.darken.adsbc.feeder.core.ADSBxId +import kotlinx.coroutines.withContext +import okhttp3.OkHttpClient +import retrofit2.Retrofit +import retrofit2.converter.moshi.MoshiConverterFactory +import java.time.Instant +import javax.inject.Inject +import kotlin.math.roundToLong + +@Reusable +class ADSBxAircraftEndpoint @Inject constructor( + private val baseClient: OkHttpClient, + private val moshiConverterFactory: MoshiConverterFactory, + private val moshi: Moshi, + private val dispatcherProvider: DispatcherProvider, +) { + + private val aircraftAdapter = moshi.adapter(ADSBxAircraftApi.AircraftPayload::class.java) + private val errorAdapter = moshi.adapter(ADSBxAircraftApi.Error::class.java) + + private val api by lazy { + val configHttpClient = baseClient.newBuilder().apply { + + }.build() + + Retrofit.Builder() + .client(configHttpClient) + .baseUrl("https://globe.adsbexchange.com") + .addConverterFactory(moshiConverterFactory) + .build() + .create(ADSBxAircraftApi::class.java) + } + + + suspend fun getAircraft(id: ADSBxId): Collection = withContext(dispatcherProvider.IO) { + log(TAG) { "getAircrafts(id=$id)" } + + val body = api.getAircraft(id.value) + val bodyRaw = body.string() + + val container = if (bodyRaw.contains("\"error\"")) { + val error = errorAdapter.fromJson(bodyRaw) + throw IllegalArgumentException(error?.error) + } else { + aircraftAdapter.fromJson(bodyRaw)!! + } + val containerNow = Instant.ofEpochMilli((container.now * 1000L).roundToLong()) + container.aircraft.map { + val nowSeenAt = Instant.ofEpochMilli(((container.now - it.seen) * 1000L).roundToLong()) + + BaseAircraft( + hexCode = it.hex, + callsign = it.flight, + lastSeenAt = nowSeenAt, + totalMessages = it.messages, + messageRate = 0f, + squawkCode = it.squawk, + altitudeMeter = it.alt_geom, + distanceKmh = 0f, + reception = it.rssi + ) + } + } + + companion object { + private val TAG = logTag("Feeder", "Aircraft", "Endpoint") + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/aircraft/ui/AircraftAdapter.kt b/app/src/main/java/eu/darken/adsbc/aircraft/ui/AircraftAdapter.kt new file mode 100644 index 0000000..1f5d4c1 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/aircraft/ui/AircraftAdapter.kt @@ -0,0 +1,78 @@ +package eu.darken.adsbc.aircraft.ui + +import android.annotation.SuppressLint +import android.icu.text.RelativeDateTimeFormatter +import android.view.ViewGroup +import eu.darken.adsbc.R +import eu.darken.adsbc.aircraft.core.Aircraft +import eu.darken.adsbc.common.lists.BindableVH +import eu.darken.adsbc.common.lists.differ.DifferItem +import eu.darken.adsbc.common.lists.differ.setupDiffer +import eu.darken.adsbc.common.lists.modular.mods.DataBinderMod +import eu.darken.adsbc.databinding.AircraftListLineBinding +import eu.darken.adsbc.feeder.core.SpottedAircraft +import eu.darken.bb.common.lists.differ.AsyncDiffer +import eu.darken.bb.common.lists.differ.HasAsyncDiffer +import eu.darken.bb.common.lists.modular.ModularAdapter +import eu.darken.bb.common.lists.modular.mods.SimpleVHCreatorMod +import java.time.Duration +import java.time.Instant +import javax.inject.Inject + +class AircraftAdapter @Inject constructor() : ModularAdapter(), + HasAsyncDiffer { + + override val asyncDiffer: AsyncDiffer<*, Item> = setupDiffer() + + override fun getItemCount(): Int = data.size + + init { + modules.add(DataBinderMod(data)) + modules.add(SimpleVHCreatorMod { AircraftVH(it) }) + } + + data class Item( + val spottedAircraft: SpottedAircraft, + val onClickAction: (Aircraft) -> Unit + ) : DifferItem { + override val stableId: Long = spottedAircraft.aircraft.hexCode.hashCode().toLong() + } + + class AircraftVH(parent: ViewGroup) : ModularAdapter.VH(R.layout.aircraft_list_line, parent), + BindableVH { + + override val viewBinding: Lazy = lazy { AircraftListLineBinding.bind(itemView) } + + private val lastSeenFormatter = RelativeDateTimeFormatter.getInstance() + + @SuppressLint("SetTextI18n") + override val onBindData: AircraftListLineBinding.( + item: Item, + payloads: List + ) -> Unit = { item, _ -> + val ac = item.spottedAircraft.aircraft + label.text = "${ac.callsign} (${ac.hexCode})" + + val timeAgo = Duration.between( + ac.lastSeenAt, + Instant.now() + ) + + lastSeen.text = lastSeenFormatter.format( + timeAgo.seconds.toDouble(), + RelativeDateTimeFormatter.Direction.LAST, + RelativeDateTimeFormatter.RelativeUnit.SECONDS + ) + + val seenBy = item.spottedAircraft.feeders + .sortedByDescending { it.second.reception } + .joinToString(", ") { + "${it.first.label} (${it.second.reception} dBm)" + } + receptionStats.text = seenBy + aircraftStats.text = "SQAWK ${ac.squawkCode}" + + itemView.setOnClickListener { item.onClickAction(ac) } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/aircraft/ui/AircraftListFragment.kt b/app/src/main/java/eu/darken/adsbc/aircraft/ui/AircraftListFragment.kt new file mode 100644 index 0000000..c223f46 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/aircraft/ui/AircraftListFragment.kt @@ -0,0 +1,36 @@ +package eu.darken.adsbc.aircraft.ui + +import android.os.Bundle +import android.view.View +import androidx.fragment.app.viewModels +import dagger.hilt.android.AndroidEntryPoint +import eu.darken.adsbc.R +import eu.darken.adsbc.common.WebpageTool +import eu.darken.adsbc.common.debug.logging.logTag +import eu.darken.adsbc.common.lists.differ.update +import eu.darken.adsbc.common.uix.Fragment3 +import eu.darken.adsbc.common.viewbinding.viewBinding +import eu.darken.adsbc.databinding.AircraftListFragmentBinding +import eu.darken.bb.common.lists.setupDefaults +import javax.inject.Inject + +@AndroidEntryPoint +class AircraftListFragment : Fragment3(R.layout.aircraft_list_fragment) { + + override val vm: AircraftListFragmentVM by viewModels() + override val ui: AircraftListFragmentBinding by viewBinding() + + @Inject lateinit var webpageTool: WebpageTool + @Inject lateinit var aircraftAdapter: AircraftAdapter + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + ui.list.setupDefaults(aircraftAdapter) + + vm.aircraft.observe2(ui) { aircraftAdapter.update(it) } + super.onViewCreated(view, savedInstanceState) + } + + companion object { + private val TAG = logTag("Aircraft", "List", "Fragment") + } +} diff --git a/app/src/main/java/eu/darken/adsbc/aircraft/ui/AircraftListFragmentVM.kt b/app/src/main/java/eu/darken/adsbc/aircraft/ui/AircraftListFragmentVM.kt new file mode 100644 index 0000000..10aff7a --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/aircraft/ui/AircraftListFragmentVM.kt @@ -0,0 +1,47 @@ +package eu.darken.adsbc.aircraft.ui + +import androidx.lifecycle.LiveData +import androidx.lifecycle.SavedStateHandle +import dagger.hilt.android.lifecycle.HiltViewModel +import eu.darken.adsbc.common.coroutine.DispatcherProvider +import eu.darken.adsbc.common.debug.logging.log +import eu.darken.adsbc.common.debug.logging.logTag +import eu.darken.adsbc.common.uix.ViewModel3 +import eu.darken.adsbc.feeder.core.FeederRepo +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.isActive +import java.time.Instant +import javax.inject.Inject +import kotlin.coroutines.coroutineContext + +@HiltViewModel +class AircraftListFragmentVM @Inject constructor( + handle: SavedStateHandle, + dispatcherProvider: DispatcherProvider, + private val feederRepo: FeederRepo, +) : ViewModel3(dispatcherProvider = dispatcherProvider) { + + private val ticker = flow { + while (coroutineContext.isActive) { + emit(Instant.now()) + delay(1000) + } + } + val aircraft: LiveData> = combine( + feederRepo.mergedAircrafts, + ticker + ) { aircraft, now -> + aircraft.map { spot -> + AircraftAdapter.Item( + spottedAircraft = spot, + ) { log(TAG) { "Aircraft clicked: $it" } } + } + .sortedByDescending { it.spottedAircraft.feeders.size } + }.asLiveData2() + + companion object { + private val TAG = logTag("Aircraft", "List", "VM") + } +} diff --git a/app/src/main/java/eu/darken/adsbc/aircraft/ui/actions/ActionsAdapter.kt b/app/src/main/java/eu/darken/adsbc/aircraft/ui/actions/ActionsAdapter.kt new file mode 100644 index 0000000..52a6aaf --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/aircraft/ui/actions/ActionsAdapter.kt @@ -0,0 +1,33 @@ +package eu.darken.adsbc.aircraft.ui.actions + +import android.view.ViewGroup +import eu.darken.adsbc.common.lists.differ.setupDiffer +import eu.darken.adsbc.common.lists.modular.mods.DataBinderMod +import eu.darken.adsbc.common.ui.Confirmable +import eu.darken.adsbc.common.ui.ConfirmableActionAdapterVH +import eu.darken.bb.common.lists.differ.AsyncDiffer +import eu.darken.bb.common.lists.differ.HasAsyncDiffer +import eu.darken.bb.common.lists.modular.ModularAdapter +import eu.darken.bb.common.lists.modular.mods.SimpleVHCreatorMod +import javax.inject.Inject + +class ActionsAdapter @Inject constructor() : + ModularAdapter(), + HasAsyncDiffer> { + + override val asyncDiffer: AsyncDiffer> = setupDiffer() + + override fun getItemCount(): Int = data.size + + init { + modules.add(DataBinderMod(data)) + modules.add(SimpleVHCreatorMod { VH(it) }) + } + + class VH(parent: ViewGroup) : ConfirmableActionAdapterVH(parent) { + override fun getIcon(item: AircraftAction): Int = item.iconRes + + override fun getLabel(item: AircraftAction): String = getString(item.labelRes) + + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/aircraft/ui/actions/AircraftAction.kt b/app/src/main/java/eu/darken/adsbc/aircraft/ui/actions/AircraftAction.kt new file mode 100644 index 0000000..01bec30 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/aircraft/ui/actions/AircraftAction.kt @@ -0,0 +1,13 @@ +package eu.darken.adsbc.aircraft.ui.actions + +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes +import eu.darken.adsbc.R +import eu.darken.adsbc.common.DialogActionEnum + +enum class AircraftAction constructor( + @DrawableRes val iconRes: Int, + @StringRes val labelRes: Int +) : DialogActionEnum { + DETAILS(R.drawable.ic_baseline_drive_file_rename_outline_24, R.string.aircraft_details_action), +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/aircraft/ui/actions/AircraftActionDialog.kt b/app/src/main/java/eu/darken/adsbc/aircraft/ui/actions/AircraftActionDialog.kt new file mode 100644 index 0000000..f535991 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/aircraft/ui/actions/AircraftActionDialog.kt @@ -0,0 +1,41 @@ +package eu.darken.adsbc.aircraft.ui.actions + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.viewModels +import dagger.hilt.android.AndroidEntryPoint +import eu.darken.adsbc.common.lists.differ.update +import eu.darken.adsbc.common.uix.BottomSheetDialogFragment2 +import eu.darken.adsbc.databinding.AircraftListActionDialogBinding +import eu.darken.bb.common.lists.setupDefaults +import javax.inject.Inject + +@AndroidEntryPoint +class AircraftActionDialog : BottomSheetDialogFragment2() { + override val vm: AircraftActionDialogVM by viewModels() + override lateinit var ui: AircraftListActionDialogBinding + @Inject lateinit var actionsAdapter: ActionsAdapter + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + ui = AircraftListActionDialogBinding.inflate(inflater, container, false) + return ui.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + ui.recyclerview.setupDefaults(actionsAdapter) + + vm.state.observe2(ui) { state -> + feederName.text = state.label + + actionsAdapter.update(state.allowedActions) + + ui.recyclerview.visibility = if (state.loading) View.INVISIBLE else View.VISIBLE + ui.progressCircular.visibility = if (state.loading) View.VISIBLE else View.INVISIBLE + if (state.finished) dismissAllowingStateLoss() + } + + super.onViewCreated(view, savedInstanceState) + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/aircraft/ui/actions/AircraftActionDialogVM.kt b/app/src/main/java/eu/darken/adsbc/aircraft/ui/actions/AircraftActionDialogVM.kt new file mode 100644 index 0000000..9f6a5d6 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/aircraft/ui/actions/AircraftActionDialogVM.kt @@ -0,0 +1,76 @@ +package eu.darken.adsbc.aircraft.ui.actions + +import androidx.lifecycle.SavedStateHandle +import dagger.hilt.android.lifecycle.HiltViewModel +import eu.darken.adsbc.aircraft.ui.actions.AircraftAction.DETAILS +import eu.darken.adsbc.common.coroutine.DispatcherProvider +import eu.darken.adsbc.common.debug.logging.logTag +import eu.darken.adsbc.common.flow.DynamicStateFlow +import eu.darken.adsbc.common.navigation.navArgs +import eu.darken.adsbc.common.ui.Confirmable +import eu.darken.adsbc.common.uix.ViewModel3 +import eu.darken.adsbc.stats.core.storage.StatsStorage +import javax.inject.Inject + +@HiltViewModel +class AircraftActionDialogVM @Inject constructor( + handle: SavedStateHandle, + dispatcherProvider: DispatcherProvider, + private val feederStorage: StatsStorage, +) : ViewModel3(dispatcherProvider) { + + private val navArgs by handle.navArgs() + private val aircraftId = navArgs.aircraftId + private val stateUpdater = DynamicStateFlow(TAG, vmScope) { State(loading = true) } + + val state = stateUpdater.asLiveData2() + + init { + launch { +// val feeder = feederRepo.feeder(feederId).first() +// +// val actions = listOf( +// Confirmable(DETAILS) { aircraftAction(it) }, +// ) +// +// stateUpdater.updateBlocking { +// if (feeder == null) { +// copy(loading = true, finished = true) +// } else { +// copy( +// label = feeder.labelOrId, +// loading = false, +// allowedActions = actions +// ) +// } +// } + } + } + + private fun aircraftAction(action: AircraftAction) = launch { + stateUpdater.updateBlocking { copy(loading = true) } + launch { + try { + when (action) { + DETAILS -> { + + } + } + } finally { + stateUpdater.updateBlocking { copy(loading = false, finished = true) } + } + } + } + + data class State( + val loading: Boolean = false, + val finished: Boolean = false, + val label: String = "", + val allowedActions: List> = listOf() + ) + + companion object { + private val TAG = logTag("Aircraft", "ActionDialog", "VM") + } + +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/common/BuildConfigWrap.kt b/app/src/main/java/eu/darken/adsbc/common/BuildConfigWrap.kt new file mode 100644 index 0000000..1a4dcda --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/BuildConfigWrap.kt @@ -0,0 +1,18 @@ +package eu.darken.adsbc.common + +import eu.darken.adsbc.BuildConfig + + +// Can't be const because that prevents them from being mocked in tests +@Suppress("MayBeConstant") +object BuildConfigWrap { + val APPLICATION_ID = BuildConfig.APPLICATION_ID + val DEBUG: Boolean = BuildConfig.DEBUG + val BUILD_TYPE: String = BuildConfig.BUILD_TYPE + + val VERSION_CODE: Long = BuildConfig.VERSION_CODE.toLong() + val VERSION_NAME: String = BuildConfig.VERSION_NAME + val GIT_SHA: String = BuildConfig.GITSHA + + val VERSION_DESCRIPTION: String = "v$VERSION_NAME ($VERSION_CODE) [$GIT_SHA]" +} diff --git a/app/src/main/java/eu/darken/adsbc/common/BuildWrap.kt b/app/src/main/java/eu/darken/adsbc/common/BuildWrap.kt new file mode 100644 index 0000000..dfb1178 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/BuildWrap.kt @@ -0,0 +1,16 @@ +package eu.darken.adsbc.common + +import android.os.Build + +// Can't be const because that prevents them from being mocked in tests +@Suppress("MayBeConstant") +object BuildWrap { + + val VERSION = VersionWrap + + object VersionWrap { + val SDK_INT = Build.VERSION.SDK_INT + } +} + +fun hasApiLevel(level: Int): Boolean = BuildWrap.VERSION.SDK_INT >= level diff --git a/app/src/main/java/eu/darken/adsbc/common/ClipboardHelper.kt b/app/src/main/java/eu/darken/adsbc/common/ClipboardHelper.kt new file mode 100644 index 0000000..674d095 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/ClipboardHelper.kt @@ -0,0 +1,51 @@ +package eu.darken.adsbc.common + +import android.content.ClipData +import android.content.ClipboardManager +import android.content.Context +import android.os.Handler +import android.os.Looper +import dagger.hilt.android.qualifiers.ApplicationContext +import eu.darken.adsbc.R +import eu.darken.adsbc.common.debug.logging.log +import eu.darken.adsbc.common.debug.logging.logTag +import java.util.concurrent.locks.ReentrantLock +import javax.inject.Inject +import javax.inject.Singleton +import kotlin.concurrent.withLock + +@Singleton +class ClipboardHelper @Inject constructor( + @ApplicationContext private val context: Context +) { + private val clipboard: ClipboardManager by lazy { + return@lazy if (Looper.getMainLooper() == Looper.myLooper()) { + context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + } else { + // java.lang.RuntimeException · Can't create handler inside thread that has not called Looper.prepare() + log(TAG) { "Clipboard is not initialized on the main thread, applying workaround" } + val lock = ReentrantLock() + val lockCondition = lock.newCondition() + + var clipboardManager: ClipboardManager? = null + + Handler(Looper.getMainLooper()).postAtFrontOfQueue { + clipboardManager = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + lock.withLock { lockCondition.signal() } + } + + lock.withLock { lockCondition.await() } + + clipboardManager!! + } + } + + fun copyToClipboard(text: String) { + val clip = ClipData.newPlainText(context.getString(R.string.app_name), text) + clipboard.setPrimaryClip(clip) + } + + companion object { + private val TAG = logTag("ClipboardHelper") + } +} diff --git a/app/src/main/java/eu/darken/adsbc/common/ColorExtensions.kt b/app/src/main/java/eu/darken/adsbc/common/ColorExtensions.kt new file mode 100644 index 0000000..3922b0d --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/ColorExtensions.kt @@ -0,0 +1,13 @@ +package eu.darken.adsbc.common + +import android.content.Context +import android.text.SpannableString +import android.text.style.ForegroundColorSpan +import androidx.annotation.ColorRes +import androidx.core.content.ContextCompat + +fun colorString(context: Context, @ColorRes colorRes: Int, string: String): SpannableString { + val colored = SpannableString(string) + colored.setSpan(ForegroundColorSpan(ContextCompat.getColor(context, colorRes)), 0, string.length, 0) + return colored +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/common/ContextExtensions.kt b/app/src/main/java/eu/darken/adsbc/common/ContextExtensions.kt new file mode 100644 index 0000000..8fe8c43 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/ContextExtensions.kt @@ -0,0 +1,39 @@ +package eu.darken.adsbc.common + +import android.annotation.SuppressLint +import android.content.Context +import android.content.Intent +import android.content.res.TypedArray +import androidx.annotation.AttrRes +import androidx.annotation.ColorInt +import androidx.annotation.ColorRes +import androidx.core.content.ContextCompat +import androidx.fragment.app.Fragment + + +@ColorInt +fun Context.getColorForAttr(@AttrRes attrId: Int): Int { + var typedArray: TypedArray? = null + try { + typedArray = this.theme.obtainStyledAttributes(intArrayOf(attrId)) + return typedArray.getColor(0, 0) + } finally { + typedArray?.recycle() + } +} + +@ColorInt +fun Fragment.getColorForAttr(@AttrRes attrId: Int): Int = requireContext().getColorForAttr(attrId) + +@ColorInt +fun Context.getCompatColor(@ColorRes attrId: Int): Int { + return ContextCompat.getColor(this, attrId) +} + +@ColorInt +fun Fragment.getCompatColor(@ColorRes attrId: Int): Int = requireContext().getCompatColor(attrId) + +@SuppressLint("NewApi") +fun Context.startServiceCompat(intent: Intent) { + if (hasApiLevel(26)) startForegroundService(intent) else startService(intent) +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/common/DialogActionEnum.kt b/app/src/main/java/eu/darken/adsbc/common/DialogActionEnum.kt new file mode 100644 index 0000000..75ec079 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/DialogActionEnum.kt @@ -0,0 +1,8 @@ +package eu.darken.adsbc.common + +import eu.darken.adsbc.common.lists.differ.DifferItem + +interface DialogActionEnum : DifferItem { + override val stableId: Long + get() = (this as Enum<*>).ordinal.toLong() +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/common/EmailTool.kt b/app/src/main/java/eu/darken/adsbc/common/EmailTool.kt new file mode 100644 index 0000000..f98a288 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/EmailTool.kt @@ -0,0 +1,33 @@ +package eu.darken.adsbc.common + +import android.content.Context +import android.content.Intent +import dagger.Reusable +import dagger.hilt.android.qualifiers.ApplicationContext +import javax.inject.Inject + +@Reusable +class EmailTool @Inject constructor( + @ApplicationContext val context: Context +) { + + fun build(email: Email, offerChooser: Boolean = false): Intent { + val intent = Intent(Intent.ACTION_SEND) + intent.type = "message/rfc822" + intent.putExtra(Intent.EXTRA_EMAIL, email.receipients.toTypedArray()) + + intent.addCategory(Intent.CATEGORY_DEFAULT) + + intent.putExtra(Intent.EXTRA_SUBJECT, email.subject) + intent.putExtra(Intent.EXTRA_TEXT, email.body) + + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + return if (offerChooser) Intent.createChooser(intent, null) else intent + } + + data class Email( + val receipients: List, + val subject: String, + val body: String + ) +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/common/InstallId.kt b/app/src/main/java/eu/darken/adsbc/common/InstallId.kt new file mode 100644 index 0000000..9226c31 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/InstallId.kt @@ -0,0 +1,42 @@ +package eu.darken.adsbc.common + +import android.content.Context +import dagger.hilt.android.qualifiers.ApplicationContext +import eu.darken.adsbc.common.debug.logging.log +import eu.darken.adsbc.common.debug.logging.logTag +import java.io.File +import java.util.* +import java.util.regex.Pattern +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class InstallId @Inject constructor( + @ApplicationContext private val context: Context, +) { + private val installIDFile = File(context.filesDir, INSTALL_ID_FILENAME) + val id: String by lazy { + val existing = if (installIDFile.exists()) { + installIDFile.readText().also { + if (!UUID_PATTERN.matcher(it).matches()) throw IllegalStateException("Invalid InstallID: $it") + } + } else { + null + } + + return@lazy existing ?: UUID.randomUUID().toString().also { + log(TAG) { "New install ID created: $it" } + installIDFile.writeText(it) + } + } + + companion object { + private val TAG: String = logTag("InstallID") + private val UUID_PATTERN = Pattern.compile( + "^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" + ) + + private const val INSTALL_ID_FILENAME = "installid" + } +} + diff --git a/app/src/main/java/eu/darken/adsbc/common/LiveDataExtensions.kt b/app/src/main/java/eu/darken/adsbc/common/LiveDataExtensions.kt new file mode 100644 index 0000000..23e8032 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/LiveDataExtensions.kt @@ -0,0 +1,23 @@ +package eu.darken.adsbc.common + +import androidx.appcompat.app.AppCompatActivity +import androidx.fragment.app.Fragment +import androidx.lifecycle.LiveData +import androidx.viewbinding.ViewBinding + + +fun LiveData.observe2(fragment: Fragment, callback: (T) -> Unit) { + observe(fragment.viewLifecycleOwner) { callback.invoke(it) } +} + +inline fun LiveData.observe2( + fragment: Fragment, + ui: VB, + crossinline callback: VB.(T) -> Unit +) { + observe(fragment.viewLifecycleOwner) { callback.invoke(ui, it) } +} + +fun LiveData.observe2(activity: AppCompatActivity, callback: (T) -> Unit) { + observe(activity) { callback.invoke(it) } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/common/UnitConverter.kt b/app/src/main/java/eu/darken/adsbc/common/UnitConverter.kt new file mode 100644 index 0000000..aa620e7 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/UnitConverter.kt @@ -0,0 +1,14 @@ +package eu.darken.adsbc.common + +import android.content.Context +import android.util.TypedValue + +object UnitConverter { + fun dpToPx(context: Context, dp: Float): Int { + return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, context.resources.displayMetrics).toInt() + } + + fun spToPx(context: Context, sp: Float): Float { + return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, context.resources.displayMetrics) + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/common/WebpageTool.kt b/app/src/main/java/eu/darken/adsbc/common/WebpageTool.kt new file mode 100644 index 0000000..94d6781 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/WebpageTool.kt @@ -0,0 +1,28 @@ +package eu.darken.adsbc.common + +import android.content.Context +import android.content.Intent +import android.net.Uri +import dagger.Reusable +import dagger.hilt.android.qualifiers.ApplicationContext +import eu.darken.adsbc.common.debug.logging.Logging.Priority.ERROR +import eu.darken.adsbc.common.debug.logging.log +import javax.inject.Inject + +@Reusable +class WebpageTool @Inject constructor( + @ApplicationContext private val context: Context, +) { + + fun open(address: String) { + val intent = Intent(Intent.ACTION_VIEW, Uri.parse(address)).apply { + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + } + try { + context.startActivity(intent) + } catch (e: Exception) { + log(ERROR) { "Failed to launch" } + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/common/collections/MapExtensions.kt b/app/src/main/java/eu/darken/adsbc/common/collections/MapExtensions.kt new file mode 100644 index 0000000..9f8a282 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/collections/MapExtensions.kt @@ -0,0 +1,5 @@ +package eu.darken.adsbc.common.collections + +inline fun Map.mutate(block: MutableMap.() -> Unit): Map { + return toMutableMap().apply(block).toMap() +} diff --git a/app/src/main/java/eu/darken/adsbc/common/compression/Zipper.kt b/app/src/main/java/eu/darken/adsbc/common/compression/Zipper.kt new file mode 100644 index 0000000..cee729e --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/compression/Zipper.kt @@ -0,0 +1,40 @@ +package eu.darken.adsbc.common.compression + +import eu.darken.adsbc.common.debug.logging.Logging.Priority.VERBOSE +import eu.darken.adsbc.common.debug.logging.log +import eu.darken.adsbc.common.debug.logging.logTag +import java.io.BufferedInputStream +import java.io.BufferedOutputStream +import java.io.FileInputStream +import java.io.FileOutputStream +import java.util.zip.ZipEntry +import java.util.zip.ZipOutputStream + +// https://stackoverflow.com/a/48598099/1251958 +class Zipper { + + @Throws(Exception::class) + fun zip(files: Array, zipFile: String) { + + var origin: BufferedInputStream? + val out = ZipOutputStream(BufferedOutputStream(FileOutputStream(zipFile))) + + for (i in files.indices) { + log(TAG, VERBOSE) { "Compressing ${files[i]} into $zipFile" } + origin = BufferedInputStream(FileInputStream(files[i]), BUFFER) + + val entry = ZipEntry(files[i].substring(files[i].lastIndexOf("/") + 1)) + out.putNextEntry(entry) + + origin.use { input -> input.copyTo(out) } + } + + out.finish() + out.close() + } + + companion object { + internal val TAG = logTag("Zipper") + const val BUFFER = 2048 + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/common/coroutine/AppCoroutineScope.kt b/app/src/main/java/eu/darken/adsbc/common/coroutine/AppCoroutineScope.kt new file mode 100644 index 0000000..608ec88 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/coroutine/AppCoroutineScope.kt @@ -0,0 +1,19 @@ +package eu.darken.adsbc.common.coroutine + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import javax.inject.Inject +import javax.inject.Qualifier +import javax.inject.Singleton +import kotlin.coroutines.CoroutineContext + +@Singleton +class AppCoroutineScope @Inject constructor() : CoroutineScope { + override val coroutineContext: CoroutineContext = SupervisorJob() + Dispatchers.Default +} + +@Qualifier +@MustBeDocumented +@Retention(AnnotationRetention.RUNTIME) +annotation class AppScope diff --git a/app/src/main/java/eu/darken/adsbc/common/coroutine/CoroutineModule.kt b/app/src/main/java/eu/darken/adsbc/common/coroutine/CoroutineModule.kt new file mode 100644 index 0000000..5d19b72 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/coroutine/CoroutineModule.kt @@ -0,0 +1,19 @@ +package eu.darken.adsbc.common.coroutine + +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import kotlinx.coroutines.CoroutineScope + +@InstallIn(SingletonComponent::class) +@Module +abstract class CoroutineModule { + + @Binds + abstract fun dispatcherProvider(defaultProvider: DefaultDispatcherProvider): DispatcherProvider + + @Binds + @AppScope + abstract fun appscope(appCoroutineScope: AppCoroutineScope): CoroutineScope +} diff --git a/app/src/main/java/eu/darken/adsbc/common/coroutine/DefaultDispatcherProvider.kt b/app/src/main/java/eu/darken/adsbc/common/coroutine/DefaultDispatcherProvider.kt new file mode 100644 index 0000000..c382fa3 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/coroutine/DefaultDispatcherProvider.kt @@ -0,0 +1,7 @@ +package eu.darken.adsbc.common.coroutine + +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class DefaultDispatcherProvider @Inject constructor() : DispatcherProvider diff --git a/app/src/main/java/eu/darken/adsbc/common/coroutine/DispatcherProvider.kt b/app/src/main/java/eu/darken/adsbc/common/coroutine/DispatcherProvider.kt new file mode 100644 index 0000000..5f52b83 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/coroutine/DispatcherProvider.kt @@ -0,0 +1,21 @@ +package eu.darken.adsbc.common.coroutine + +import kotlinx.coroutines.Dispatchers +import kotlin.coroutines.CoroutineContext + +// Need this to improve testing +// Can currently only replace the main-thread dispatcher. +// https://github.com/Kotlin/kotlinx.coroutines/issues/1365 +@Suppress("PropertyName", "VariableNaming") +interface DispatcherProvider { + val Default: CoroutineContext + get() = Dispatchers.Default + val Main: CoroutineContext + get() = Dispatchers.Main + val MainImmediate: CoroutineContext + get() = Dispatchers.Main.immediate + val Unconfined: CoroutineContext + get() = Dispatchers.Unconfined + val IO: CoroutineContext + get() = Dispatchers.IO +} diff --git a/app/src/main/java/eu/darken/adsbc/common/dagger/AndroidModule.kt b/app/src/main/java/eu/darken/adsbc/common/dagger/AndroidModule.kt new file mode 100644 index 0000000..b0297c5 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/dagger/AndroidModule.kt @@ -0,0 +1,24 @@ +package eu.darken.adsbc.common.dagger + +import android.app.Application +import android.app.NotificationManager +import android.content.Context +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@InstallIn(SingletonComponent::class) +@Module +class AndroidModule { + + @Provides + @Singleton + fun context(app: Application): Context = app.applicationContext + + @Provides + @Singleton + fun notificationManager(context: Context): NotificationManager = + context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager +} diff --git a/app/src/main/java/eu/darken/adsbc/common/debug/Bugs.kt b/app/src/main/java/eu/darken/adsbc/common/debug/Bugs.kt new file mode 100644 index 0000000..9341914 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/debug/Bugs.kt @@ -0,0 +1,21 @@ +package eu.darken.adsbc.common.debug + +import com.bugsnag.android.Bugsnag +import eu.darken.adsbc.common.debug.logging.Logging.Priority.VERBOSE +import eu.darken.adsbc.common.debug.logging.Logging.Priority.WARN +import eu.darken.adsbc.common.debug.logging.log +import eu.darken.adsbc.common.debug.logging.logTag + +object Bugs { + var ready = false + fun report(exception: Exception) { + log(TAG, VERBOSE) { "Reporting $exception" } + if (!ready) { + log(TAG, WARN) { "Bug tracking not initialized yet." } + return + } + Bugsnag.notify(exception) + } + + private val TAG = logTag("Debug", "Bugs") +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/common/debug/autoreport/AutoReporting.kt b/app/src/main/java/eu/darken/adsbc/common/debug/autoreport/AutoReporting.kt new file mode 100644 index 0000000..ccb58e6 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/debug/autoreport/AutoReporting.kt @@ -0,0 +1,58 @@ +package eu.darken.adsbc.common.debug.autoreport + +import android.content.Context +import com.bugsnag.android.Bugsnag +import com.bugsnag.android.Configuration +import dagger.hilt.android.qualifiers.ApplicationContext +import eu.darken.adsbc.common.InstallId +import eu.darken.adsbc.common.debug.Bugs +import eu.darken.adsbc.common.debug.autoreport.bugsnag.BugsnagErrorHandler +import eu.darken.adsbc.common.debug.autoreport.bugsnag.BugsnagLogger +import eu.darken.adsbc.common.debug.autoreport.bugsnag.NOPBugsnagErrorHandler +import eu.darken.adsbc.common.debug.logging.Logging +import eu.darken.adsbc.common.debug.logging.log +import eu.darken.adsbc.common.debug.logging.logTag +import javax.inject.Inject +import javax.inject.Provider +import javax.inject.Singleton + +@Singleton +class AutoReporting @Inject constructor( + @ApplicationContext private val context: Context, + private val bugReportSettings: DebugSettings, + private val installId: InstallId, + private val bugsnagLogger: Provider, + private val bugsnagErrorHandler: Provider, + private val nopBugsnagErrorHandler: Provider, +) { + + fun setup() { + val isEnabled = bugReportSettings.isAutoReportingEnabled.value + log(TAG) { "setup(): isEnabled=$isEnabled" } + + try { + val bugsnagConfig = Configuration.load(context).apply { + if (bugReportSettings.isAutoReportingEnabled.value) { + Logging.install(bugsnagLogger.get()) + setUser(installId.id, null, null) + autoTrackSessions = true + addOnError(bugsnagErrorHandler.get()) + log(TAG) { "Bugsnag setup done!" } + } else { + autoTrackSessions = false + addOnError(nopBugsnagErrorHandler.get()) + log(TAG) { "Installing Bugsnag NOP error handler due to user opt-out!" } + } + } + + Bugsnag.start(context, bugsnagConfig) + Bugs.ready = true + } catch (e: IllegalStateException) { + log(TAG) { "Bugsnag API Key not configured." } + } + } + + companion object { + private val TAG = logTag("Debug", "AutoReporting") + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/common/debug/autoreport/DebugSettings.kt b/app/src/main/java/eu/darken/adsbc/common/debug/autoreport/DebugSettings.kt new file mode 100644 index 0000000..e312af5 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/debug/autoreport/DebugSettings.kt @@ -0,0 +1,35 @@ +package eu.darken.adsbc.common.debug.autoreport + +import android.content.Context +import android.content.SharedPreferences +import androidx.preference.PreferenceDataStore +import dagger.hilt.android.qualifiers.ApplicationContext +import eu.darken.adsbc.common.debug.logging.logTag +import eu.darken.adsbc.common.preferences.PreferenceStoreMapper +import eu.darken.adsbc.common.preferences.Settings +import eu.darken.adsbc.common.preferences.createFlowPreference +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class DebugSettings @Inject constructor( + @ApplicationContext private val context: Context, +) : Settings() { + + override val preferences: SharedPreferences by lazy { + context.getSharedPreferences("debug_settings", Context.MODE_PRIVATE) + } + + val isAutoReportingEnabled = preferences.createFlowPreference("debug.bugreport.automatic.enabled", false) + + val isDebugModeEnabled = preferences.createFlowPreference("debug.mode.enabled", false) + + override val preferenceDataStore: PreferenceDataStore = PreferenceStoreMapper( + isAutoReportingEnabled, + isDebugModeEnabled + ) + + companion object { + internal val TAG = logTag("General", "Settings") + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/common/debug/autoreport/bugsnag/BugsnagErrorHandler.kt b/app/src/main/java/eu/darken/adsbc/common/debug/autoreport/bugsnag/BugsnagErrorHandler.kt new file mode 100644 index 0000000..bc35631 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/debug/autoreport/bugsnag/BugsnagErrorHandler.kt @@ -0,0 +1,58 @@ +package eu.darken.adsbc.common.debug.autoreport.bugsnag + +import android.annotation.SuppressLint +import android.content.Context +import android.content.pm.PackageManager +import com.bugsnag.android.Event +import com.bugsnag.android.OnErrorCallback +import dagger.hilt.android.qualifiers.ApplicationContext +import eu.darken.adsbc.BuildConfig +import eu.darken.adsbc.common.debug.autoreport.DebugSettings +import eu.darken.adsbc.common.debug.logging.Logging.Priority.WARN +import eu.darken.adsbc.common.debug.logging.asLog +import eu.darken.adsbc.common.debug.logging.log +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class BugsnagErrorHandler @Inject constructor( + @ApplicationContext private val context: Context, + private val bugsnagLogger: BugsnagLogger, + private val debugSettings: DebugSettings, +) : OnErrorCallback { + + override fun onError(event: Event): Boolean { + bugsnagLogger.injectLog(event) + + TAB_APP.also { tab -> + event.addMetadata(tab, "gitSha", BuildConfig.GITSHA) + event.addMetadata(tab, "buildTime", BuildConfig.BUILDTIME) + + context.tryFormattedSignature()?.let { event.addMetadata(tab, "signatures", it) } + } + + return debugSettings.isAutoReportingEnabled.value && !BuildConfig.DEBUG + } + + companion object { + private const val TAB_APP = "app" + + @Suppress("DEPRECATION") + @SuppressLint("PackageManagerGetSignatures") + fun Context.tryFormattedSignature(): String? = try { + packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES).signatures?.let { sigs -> + val sb = StringBuilder("[") + for (i in sigs.indices) { + sb.append(sigs[i].hashCode()) + if (i + 1 != sigs.size) sb.append(", ") + } + sb.append("]") + sb.toString() + } + } catch (e: Exception) { + log(WARN) { e.asLog() } + null + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/common/debug/autoreport/bugsnag/BugsnagLogger.kt b/app/src/main/java/eu/darken/adsbc/common/debug/autoreport/bugsnag/BugsnagLogger.kt new file mode 100644 index 0000000..d6639d9 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/debug/autoreport/bugsnag/BugsnagLogger.kt @@ -0,0 +1,47 @@ +package eu.darken.adsbc.common.debug.autoreport.bugsnag + +import com.bugsnag.android.Event +import eu.darken.adsbc.common.debug.logging.Logging +import eu.darken.adsbc.common.debug.logging.asLog +import java.lang.String.format +import java.util.* +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class BugsnagLogger @Inject constructor() : Logging.Logger { + + // Adding one to the initial size accounts for the add before remove. + private val buffer: Deque = ArrayDeque(BUFFER_SIZE + 1) + + override fun log(priority: Logging.Priority, tag: String, message: String, metaData: Map?) { + val line = "${System.currentTimeMillis()} ${priority.toLabel()}/$tag: $message" + synchronized(buffer) { + buffer.addLast(line) + if (buffer.size > BUFFER_SIZE) { + buffer.removeFirst() + } + } + } + + fun injectLog(event: Event) { + synchronized(buffer) { + var i = 100 + buffer.forEach { event.addMetadata("Log", format(Locale.ROOT, "%03d", i++), it) } + event.addMetadata("Log", format(Locale.ROOT, "%03d", i), event.originalError?.asLog()) + } + } + + companion object { + private const val BUFFER_SIZE = 200 + + private fun Logging.Priority.toLabel(): String = when (this) { + Logging.Priority.VERBOSE -> "V" + Logging.Priority.DEBUG -> "D" + Logging.Priority.INFO -> "I" + Logging.Priority.WARN -> "W" + Logging.Priority.ERROR -> "E" + Logging.Priority.ASSERT -> "WTF" + } + } +} diff --git a/app/src/main/java/eu/darken/adsbc/common/debug/autoreport/bugsnag/NOPBugsnagErrorHandler.kt b/app/src/main/java/eu/darken/adsbc/common/debug/autoreport/bugsnag/NOPBugsnagErrorHandler.kt new file mode 100644 index 0000000..4cd683d --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/debug/autoreport/bugsnag/NOPBugsnagErrorHandler.kt @@ -0,0 +1,19 @@ +package eu.darken.adsbc.common.debug.autoreport.bugsnag + +import com.bugsnag.android.Event +import com.bugsnag.android.OnErrorCallback +import eu.darken.adsbc.common.debug.logging.Logging.Priority.WARN +import eu.darken.adsbc.common.debug.logging.asLog +import eu.darken.adsbc.common.debug.logging.log +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class NOPBugsnagErrorHandler @Inject constructor() : OnErrorCallback { + + override fun onError(event: Event): Boolean { + log(WARN) { "Error, but skipping bugsnag due to user opt-out: ${event.originalError?.asLog()}" } + return false + } + +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/common/debug/logging/FileLogger.kt b/app/src/main/java/eu/darken/adsbc/common/debug/logging/FileLogger.kt new file mode 100644 index 0000000..0dd74a5 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/debug/logging/FileLogger.kt @@ -0,0 +1,79 @@ +package eu.darken.adsbc.common.debug.logging + +import android.annotation.SuppressLint +import android.util.Log +import java.io.File +import java.io.FileOutputStream +import java.io.IOException +import java.io.OutputStreamWriter +import java.time.Instant + + +@SuppressLint("LogNotTimber") +class FileLogger(private val logFile: File) : Logging.Logger { + private var logWriter: OutputStreamWriter? = null + + @SuppressLint("SetWorldReadable") + @Synchronized + fun start() { + if (logWriter != null) return + + logFile.parentFile!!.mkdirs() + if (logFile.createNewFile()) { + Log.i(TAG, "File logger writing to " + logFile.path) + } + if (logFile.setReadable(true, false)) { + Log.i(TAG, "Debug run log read permission set") + } + + try { + logWriter = OutputStreamWriter(FileOutputStream(logFile, true)) + logWriter!!.write("=== BEGIN ===\n") + logWriter!!.write("Logfile: $logFile\n") + logWriter!!.flush() + Log.i(TAG, "File logger started.") + } catch (e: IOException) { + e.printStackTrace() + + logFile.delete() + if (logWriter != null) logWriter!!.close() + } + + } + + @Synchronized + fun stop() { + logWriter?.let { + logWriter = null + try { + it.write("=== END ===\n") + it.close() + } catch (ignore: IOException) { + } + Log.i(TAG, "File logger stopped.") + } + } + + override fun log(priority: Logging.Priority, tag: String, message: String, metaData: Map?) { + logWriter?.let { + try { + it.write("${Instant.ofEpochMilli(System.currentTimeMillis())} ${priority.shortLabel}/$tag: $message\n") + it.flush() + } catch (e: IOException) { + Log.e(TAG, "Failed to write log line.", e) + try { + it.close() + } catch (ignore: Exception) { + } + logWriter = null + } + } + } + + override fun toString(): String = "FileLogger(file=$logFile)" + + companion object { + private val TAG = logTag("Debug", "FileLogger") + } +} + diff --git a/app/src/main/java/eu/darken/adsbc/common/debug/logging/LogCatLogger.kt b/app/src/main/java/eu/darken/adsbc/common/debug/logging/LogCatLogger.kt new file mode 100644 index 0000000..2b49aef --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/debug/logging/LogCatLogger.kt @@ -0,0 +1,49 @@ +package eu.darken.adsbc.common.debug.logging + +import android.os.Build +import android.util.Log +import kotlin.math.min + +class LogCatLogger : Logging.Logger { + + override fun isLoggable(priority: Logging.Priority): Boolean = true + + override fun log(priority: Logging.Priority, tag: String, message: String, metaData: Map?) { + + val trimmedTag = if (tag.length <= MAX_TAG_LENGTH || Build.VERSION.SDK_INT >= 26) { + tag + } else { + tag.substring(0, MAX_TAG_LENGTH) + } + + if (message.length < MAX_LOG_LENGTH) { + writeToLogcat(priority.intValue, trimmedTag, message) + return + } + + // Split by line, then ensure each line can fit into Log's maximum length. + var i = 0 + val length = message.length + while (i < length) { + var newline = message.indexOf('\n', i) + newline = if (newline != -1) newline else length + do { + val end = min(newline, i + MAX_LOG_LENGTH) + val part = message.substring(i, end) + writeToLogcat(priority.intValue, trimmedTag, part) + i = end + } while (i < newline) + i++ + } + } + + private fun writeToLogcat(priority: Int, tag: String, part: String) = when (priority) { + Log.ASSERT -> Log.wtf(tag, part) + else -> Log.println(priority, tag, part) + } + + companion object { + private const val MAX_LOG_LENGTH = 4000 + private const val MAX_TAG_LENGTH = 23 + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/common/debug/logging/LogExtensions.kt b/app/src/main/java/eu/darken/adsbc/common/debug/logging/LogExtensions.kt new file mode 100644 index 0000000..0129143 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/debug/logging/LogExtensions.kt @@ -0,0 +1,10 @@ +package eu.darken.adsbc.common.debug.logging + +fun logTag(vararg tags: String): String { + val sb = StringBuilder("ADSBC:") + for (i in tags.indices) { + sb.append(tags[i]) + if (i < tags.size - 1) sb.append(":") + } + return sb.toString() +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/common/debug/logging/Logging.kt b/app/src/main/java/eu/darken/adsbc/common/debug/logging/Logging.kt new file mode 100644 index 0000000..b0342f8 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/debug/logging/Logging.kt @@ -0,0 +1,132 @@ +package eu.darken.adsbc.common.debug.logging + +import java.io.PrintWriter +import java.io.StringWriter + +/** + * Inspired by + * https://github.com/PaulWoitaschek/Slimber + * https://github.com/square/logcat + * https://github.com/JakeWharton/timber + */ + +object Logging { + enum class Priority( + val intValue: Int, + val shortLabel: String + ) { + VERBOSE(2, "V"), + DEBUG(3, "D"), + INFO(4, "I"), + WARN(5, "W"), + ERROR(6, "E"), + ASSERT(7, "WTF"); + } + + interface Logger { + fun isLoggable(priority: Priority): Boolean = true + + fun log( + priority: Priority, + tag: String, + message: String, + metaData: Map? + ) + } + + private val internalLoggers = mutableListOf() + + val loggers: List + get() = synchronized(internalLoggers) { internalLoggers.toList() } + + val hasReceivers: Boolean + get() = synchronized(internalLoggers) { + internalLoggers.isNotEmpty() + } + + fun install(logger: Logger) { + synchronized(internalLoggers) { internalLoggers.add(logger) } + log { "Was installed $logger" } + } + + fun remove(logger: Logger) { + log { "Removing: $logger" } + synchronized(internalLoggers) { internalLoggers.remove(logger) } + } + + fun logInternal( + tag: String, + priority: Priority, + metaData: Map?, + message: String + ) { + val snapshot = synchronized(internalLoggers) { internalLoggers.toList() } + snapshot + .filter { it.isLoggable(priority) } + .forEach { + it.log( + priority = priority, + tag = tag, + metaData = metaData, + message = message + ) + } + } + + fun clearAll() { + log { "Clearing all loggers" } + synchronized(internalLoggers) { internalLoggers.clear() } + } +} + +inline fun Any.log( + priority: Logging.Priority = Logging.Priority.DEBUG, + metaData: Map? = null, + message: () -> String, +) { + if (Logging.hasReceivers) { + Logging.logInternal( + tag = logTag(logTagViaCallSite()), + priority = priority, + metaData = metaData, + message = message(), + ) + } +} + +inline fun log( + tag: String, + priority: Logging.Priority = Logging.Priority.DEBUG, + metaData: Map? = null, + message: () -> String, +) { + if (Logging.hasReceivers) { + Logging.logInternal( + tag = tag, + priority = priority, + metaData = metaData, + message = message(), + ) + } +} + +fun Throwable.asLog(): String { + val stringWriter = StringWriter(256) + val printWriter = PrintWriter(stringWriter, false) + printStackTrace(printWriter) + printWriter.flush() + return stringWriter.toString() +} + +@PublishedApi +internal fun Any.logTagViaCallSite(): String { + val javaClass = this::class.java + val fullClassName = javaClass.name + val outerClassName = fullClassName.substringBefore('$') + val simplerOuterClassName = outerClassName.substringAfterLast('.') + return if (simplerOuterClassName.isEmpty()) { + fullClassName + } else { + simplerOuterClassName.removeSuffix("Kt") + } +} diff --git a/app/src/main/java/eu/darken/adsbc/common/debug/recording/core/Recorder.kt b/app/src/main/java/eu/darken/adsbc/common/debug/recording/core/Recorder.kt new file mode 100644 index 0000000..15a4240 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/debug/recording/core/Recorder.kt @@ -0,0 +1,48 @@ +package eu.darken.adsbc.common.debug.recording.core + +import eu.darken.adsbc.common.debug.logging.FileLogger +import eu.darken.adsbc.common.debug.logging.Logging +import eu.darken.adsbc.common.debug.logging.Logging.Priority.INFO +import eu.darken.adsbc.common.debug.logging.log +import eu.darken.adsbc.common.debug.logging.logTag +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock +import java.io.File +import javax.inject.Inject + +class Recorder @Inject constructor() { + private val mutex = Mutex() + private var fileLogger: FileLogger? = null + + val isRecording: Boolean + get() = path != null + + var path: File? = null + private set + + suspend fun start(path: File) = mutex.withLock { + if (fileLogger != null) return@withLock + this.path = path + fileLogger = FileLogger(path) + fileLogger?.let { + it.start() + Logging.install(it) + log(TAG, INFO) { "Now logging to file!" } + } + } + + suspend fun stop() = mutex.withLock { + fileLogger?.let { + log(TAG, INFO) { "Stopping file-logger-tree: $it" } + Logging.remove(it) + it.stop() + fileLogger = null + this.path = null + } + } + + companion object { + internal val TAG = logTag("Debug", "Log", "Recorder") + } + +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/common/debug/recording/core/RecorderModule.kt b/app/src/main/java/eu/darken/adsbc/common/debug/recording/core/RecorderModule.kt new file mode 100644 index 0000000..4684f66 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/debug/recording/core/RecorderModule.kt @@ -0,0 +1,123 @@ +package eu.darken.adsbc.common.debug.recording.core + +import android.content.Context +import android.content.Intent +import android.os.Environment +import dagger.hilt.android.qualifiers.ApplicationContext +import eu.darken.adsbc.common.BuildConfigWrap +import eu.darken.adsbc.common.coroutine.AppScope +import eu.darken.adsbc.common.coroutine.DispatcherProvider +import eu.darken.adsbc.common.debug.logging.Logging.Priority.ERROR +import eu.darken.adsbc.common.debug.logging.log +import eu.darken.adsbc.common.debug.logging.logTag +import eu.darken.adsbc.common.debug.recording.ui.RecorderActivity +import eu.darken.adsbc.common.flow.DynamicStateFlow +import eu.darken.adsbc.common.startServiceCompat +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.* +import kotlinx.coroutines.plus +import java.io.File +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class RecorderModule @Inject constructor( + @ApplicationContext private val context: Context, + @AppScope private val appScope: CoroutineScope, + private val dispatcherProvider: DispatcherProvider, +) { + + private val triggerFile = try { + File(context.getExternalFilesDir(null), FORCE_FILE) + } catch (e: Exception) { + File( + Environment.getExternalStorageDirectory(), + "/Android/data/${BuildConfigWrap.APPLICATION_ID}/files/$FORCE_FILE" + ) + } + + private val internalState = DynamicStateFlow(TAG, appScope + dispatcherProvider.IO) { + val triggerFileExists = triggerFile.exists() + State(shouldRecord = triggerFileExists) + } + val state: Flow = internalState.flow + + init { + internalState.flow + .onEach { + log(TAG) { "New Recorder state: $internalState" } + + internalState.updateBlocking { + if (!isRecording && shouldRecord) { + val newRecorder = Recorder() + newRecorder.start(createRecordingFilePath()) + triggerFile.createNewFile() + + context.startServiceCompat(Intent(context, RecorderService::class.java)) + + copy( + recorder = newRecorder + ) + } else if (!shouldRecord && isRecording) { + val currentLog = recorder!!.path!! + recorder.stop() + + if (triggerFile.exists() && !triggerFile.delete()) { + log(TAG, ERROR) { "Failed to delete trigger file" } + } + + val intent = RecorderActivity.getLaunchIntent(context, currentLog.path).apply { + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + } + context.startActivity(intent) + + copy( + recorder = null, + lastLogPath = currentLog + ) + } else { + this + } + } + } + .launchIn(appScope) + } + + private fun createRecordingFilePath() = File( + File(context.cacheDir, "debug/logs"), + "${BuildConfigWrap.APPLICATION_ID}_logfile_${System.currentTimeMillis()}.log" + ) + + suspend fun startRecorder(): File { + internalState.updateBlocking { + copy(shouldRecord = true) + } + return internalState.flow.filter { it.isRecording }.first().currentLogPath!! + } + + suspend fun stopRecorder(): File? { + val currentPath = internalState.value().currentLogPath ?: return null + internalState.updateBlocking { + copy(shouldRecord = false) + } + internalState.flow.filter { !it.isRecording }.first() + return currentPath + } + + data class State( + val shouldRecord: Boolean = false, + internal val recorder: Recorder? = null, + val lastLogPath: File? = null, + ) { + val isRecording: Boolean + get() = recorder != null + + val currentLogPath: File? + get() = recorder?.path + } + + companion object { + internal val TAG = logTag("Debug", "Log", "Recorder", "Module") + private const val FORCE_FILE = "force_debug_run" + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/common/debug/recording/core/RecorderService.kt b/app/src/main/java/eu/darken/adsbc/common/debug/recording/core/RecorderService.kt new file mode 100644 index 0000000..d9ef5c2 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/debug/recording/core/RecorderService.kt @@ -0,0 +1,111 @@ +package eu.darken.adsbc.common.debug.recording.core + +import android.app.NotificationChannel +import android.app.NotificationManager +import android.app.PendingIntent +import android.content.Intent +import android.os.IBinder +import androidx.core.app.NotificationCompat +import dagger.hilt.android.AndroidEntryPoint +import eu.darken.adsbc.R +import eu.darken.adsbc.common.BuildConfigWrap +import eu.darken.adsbc.common.coroutine.DispatcherProvider +import eu.darken.adsbc.common.debug.logging.log +import eu.darken.adsbc.common.debug.logging.logTag +import eu.darken.adsbc.common.notifications.PendingIntentCompat +import eu.darken.adsbc.common.uix.Service2 +import eu.darken.adsbc.main.ui.MainActivity +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.cancel +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch +import javax.inject.Inject + + +@AndroidEntryPoint +class RecorderService : Service2() { + private lateinit var builder: NotificationCompat.Builder + + @Inject lateinit var recorderModule: RecorderModule + @Inject lateinit var notificationManager: NotificationManager + @Inject lateinit var dispatcherProvider: DispatcherProvider + private val recorderScope by lazy { + CoroutineScope(SupervisorJob() + dispatcherProvider.IO) + } + + override fun onBind(intent: Intent?): IBinder? = null + + override fun onCreate() { + super.onCreate() + NotificationChannel( + NOTIF_CHANID_DEBUG, + getString(R.string.debug_notification_channel_label), + NotificationManager.IMPORTANCE_MIN + ).run { notificationManager.createNotificationChannel(this) } + + val openIntent = Intent(this, MainActivity::class.java) + val openPi = PendingIntent.getActivity( + this, + 0, + openIntent, + PendingIntentCompat.FLAG_IMMUTABLE + ) + + val stopIntent = Intent(this, RecorderService::class.java) + stopIntent.action = STOP_ACTION + val stopPi = PendingIntent.getService( + this, + 0, + stopIntent, + PendingIntentCompat.FLAG_IMMUTABLE + ) + + builder = NotificationCompat.Builder(this, NOTIF_CHANID_DEBUG) + .setChannelId(NOTIF_CHANID_DEBUG) + .setContentIntent(openPi) + .setPriority(NotificationCompat.PRIORITY_HIGH) + .setSmallIcon(R.drawable.ic_baseline_bug_report_24) + .setContentText("Idle") + .setContentTitle(getString(R.string.app_name)) + .addAction(NotificationCompat.Action.Builder(0, getString(R.string.general_done_action), stopPi).build()) + + startForeground(NOTIFICATION_ID, builder.build()) + + recorderModule.state + .onEach { + if (it.isRecording) { + builder.setContentText("Recording debug log: ${it.currentLogPath?.path}") + notificationManager.notify(NOTIFICATION_ID, builder.build()) + } else { + stopForeground(true) + stopSelf() + } + } + .launchIn(recorderScope) + } + + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + log(TAG) { "onStartCommand(intent=$intent, flags=$flags, startId=$startId" } + if (intent?.action == STOP_ACTION) { + recorderScope.launch { + recorderModule.stopRecorder() + } + } + return START_STICKY + } + + override fun onDestroy() { + recorderScope.coroutineContext.cancel() + super.onDestroy() + } + + companion object { + private val TAG = + logTag("Debug", "Log", "Recorder", "Service") + private val NOTIF_CHANID_DEBUG = "${BuildConfigWrap.APPLICATION_ID}.notification.channel.debug" + private const val STOP_ACTION = "STOP_SERVICE" + private const val NOTIFICATION_ID = 53 + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/common/debug/recording/ui/RecorderActivity.kt b/app/src/main/java/eu/darken/adsbc/common/debug/recording/ui/RecorderActivity.kt new file mode 100644 index 0000000..0ddfaa4 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/debug/recording/ui/RecorderActivity.kt @@ -0,0 +1,59 @@ +package eu.darken.adsbc.common.debug.recording.ui + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.text.format.Formatter +import androidx.activity.viewModels +import androidx.core.view.isInvisible +import dagger.hilt.android.AndroidEntryPoint +import eu.darken.adsbc.common.debug.logging.logTag +import eu.darken.adsbc.common.error.asErrorDialogBuilder +import eu.darken.adsbc.common.uix.Activity2 +import eu.darken.adsbc.databinding.DebugRecordingActivityBinding + +@AndroidEntryPoint +class RecorderActivity : Activity2() { + + private lateinit var ui: DebugRecordingActivityBinding + private val vm: RecorderActivityVM by viewModels() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + ui = DebugRecordingActivityBinding.inflate(layoutInflater) + setContentView(ui.root) + + vm.state.observe2 { state -> + ui.loadingIndicator.isInvisible = !state.loading + ui.share.isInvisible = state.loading + + ui.recordingPath.text = state.normalPath + + if (state.normalSize != -1L) { + ui.recordingSize.text = Formatter.formatShortFileSize(this, state.normalSize) + } + if (state.compressedSize != -1L) { + ui.recordingSizeCompressed.text = Formatter.formatShortFileSize(this, state.compressedSize) + } + } + + vm.errorEvents.observe2 { + it.asErrorDialogBuilder(this).show() + } + + ui.share.setOnClickListener { vm.share() } + vm.shareEvent.observe2 { startActivity(it) } + } + + companion object { + internal val TAG = logTag("Debug", "Log", "RecorderActivity") + const val RECORD_PATH = "logPath" + + fun getLaunchIntent(context: Context, path: String): Intent { + val intent = Intent(context, RecorderActivity::class.java) + intent.putExtra(RECORD_PATH, path) + return intent + } + } +} diff --git a/app/src/main/java/eu/darken/adsbc/common/debug/recording/ui/RecorderActivityVM.kt b/app/src/main/java/eu/darken/adsbc/common/debug/recording/ui/RecorderActivityVM.kt new file mode 100644 index 0000000..9c425ba --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/debug/recording/ui/RecorderActivityVM.kt @@ -0,0 +1,116 @@ +package eu.darken.adsbc.common.debug.recording.ui + + +import android.content.Context +import android.content.Intent +import androidx.core.content.FileProvider +import androidx.lifecycle.SavedStateHandle +import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext +import eu.darken.adsbc.R +import eu.darken.adsbc.common.BuildConfigWrap +import eu.darken.adsbc.common.compression.Zipper +import eu.darken.adsbc.common.coroutine.DispatcherProvider +import eu.darken.adsbc.common.debug.logging.logTag +import eu.darken.adsbc.common.flow.DynamicStateFlow +import eu.darken.adsbc.common.flow.onError +import eu.darken.adsbc.common.flow.replayingShare +import eu.darken.adsbc.common.livedata.SingleLiveEvent +import eu.darken.adsbc.common.uix.ViewModel3 +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.plus +import java.io.File +import javax.inject.Inject + +@HiltViewModel +class RecorderActivityVM @Inject constructor( + handle: SavedStateHandle, + dispatcherProvider: DispatcherProvider, + @ApplicationContext private val context: Context, +) : ViewModel3(dispatcherProvider) { + + private val recordedPath = handle.get(RecorderActivity.RECORD_PATH)!! + private val pathCache = MutableStateFlow(recordedPath) + private val resultCacheObs = pathCache + .map { path -> Pair(path, File(path).length()) } + .replayingShare(vmScope) + + private val resultCacheCompressedObs = resultCacheObs + .map { uncompressed -> + val zipped = "${uncompressed.first}.zip" + Zipper().zip(arrayOf(uncompressed.first), zipped) + Pair(zipped, File(zipped).length()) + } + .replayingShare(vmScope + dispatcherProvider.IO) + + private val stater = DynamicStateFlow(TAG, vmScope) { State() } + val state = stater.asLiveData2() + + val shareEvent = SingleLiveEvent() + + init { + resultCacheObs + .onEach { (path, size) -> + stater.updateBlocking { copy(normalPath = path, normalSize = size) } + } + .launchInViewModel() + + resultCacheCompressedObs + .onEach { (path, size) -> + stater.updateBlocking { + copy( + compressedPath = path, + compressedSize = size, + loading = false + ) + } + } + .onError { errorEvents.postValue(it) } + .launchInViewModel() + + } + + fun share() = launch { + val (path, size) = resultCacheCompressedObs.first() + + val intent = Intent(Intent.ACTION_SEND).apply { + val uri = FileProvider.getUriForFile( + context, + BuildConfigWrap.APPLICATION_ID + ".provider", + File(path) + ) + + putExtra(Intent.EXTRA_STREAM, uri) + addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION) + type = "application/zip" + + addCategory(Intent.CATEGORY_DEFAULT) + putExtra( + Intent.EXTRA_SUBJECT, + "${BuildConfigWrap.APPLICATION_ID} DebugLog - ${BuildConfigWrap.VERSION_DESCRIPTION})" + ) + putExtra(Intent.EXTRA_TEXT, "Your text here.") + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + } + + + val chooserIntent = Intent.createChooser(intent, context.getString(R.string.debug_debuglog_file_label)) + shareEvent.postValue(chooserIntent) + } + + data class State( + val normalPath: String? = null, + val normalSize: Long = -1L, + val compressedPath: String? = null, + val compressedSize: Long = -1L, + val loading: Boolean = true + ) + + companion object { + private val TAG = logTag("Debug", "Recorder", "VM") + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/common/error/ErrorDialog.kt b/app/src/main/java/eu/darken/adsbc/common/error/ErrorDialog.kt new file mode 100644 index 0000000..0042822 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/error/ErrorDialog.kt @@ -0,0 +1,18 @@ +package eu.darken.adsbc.common.error + +import android.content.Context +import com.google.android.material.dialog.MaterialAlertDialogBuilder + +fun Throwable.asErrorDialogBuilder( + context: Context +) = MaterialAlertDialogBuilder(context).apply { + val error = this@asErrorDialogBuilder + val localizedError = error.localized(context) + + setTitle(localizedError.label) + setMessage(localizedError.description) + + setPositiveButton(android.R.string.ok) { _, _ -> + + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/common/error/ErrorEventSource.kt b/app/src/main/java/eu/darken/adsbc/common/error/ErrorEventSource.kt new file mode 100644 index 0000000..dc379b2 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/error/ErrorEventSource.kt @@ -0,0 +1,7 @@ +package eu.darken.adsbc.common.error + +import eu.darken.adsbc.common.livedata.SingleLiveEvent + +interface ErrorEventSource { + val errorEvents: SingleLiveEvent +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/common/error/LocalizedError.kt b/app/src/main/java/eu/darken/adsbc/common/error/LocalizedError.kt new file mode 100644 index 0000000..8fab487 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/error/LocalizedError.kt @@ -0,0 +1,36 @@ +package eu.darken.adsbc.common.error + +import android.content.Context +import eu.darken.adsbc.R + +interface HasLocalizedError { + fun getLocalizedError(context: Context): LocalizedError +} + +data class LocalizedError( + val throwable: Throwable, + val label: String, + val description: String +) { + fun asText() = "$label:\n$description" +} + +fun Throwable.localized(c: Context): LocalizedError = when { + this is HasLocalizedError -> this.getLocalizedError(c) + localizedMessage != null -> LocalizedError( + throwable = this, + label = "${c.getString(R.string.general_error_label)}: ${this::class.simpleName!!}", + description = localizedMessage ?: getStackTracePeek() + ) + else -> LocalizedError( + throwable = this, + label = "${c.getString(R.string.general_error_label)}: ${this::class.simpleName!!}", + description = getStackTracePeek() + ) +} + +private fun Throwable.getStackTracePeek() = this.stackTraceToString() + .lines() + .filterIndexed { index, _ -> index > 1 } + .take(3) + .joinToString("\n") \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/common/error/ThrowableExtensions.kt b/app/src/main/java/eu/darken/adsbc/common/error/ThrowableExtensions.kt new file mode 100644 index 0000000..2f8d5a9 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/error/ThrowableExtensions.kt @@ -0,0 +1,42 @@ +package eu.darken.adsbc.common.error + +import java.io.PrintWriter +import java.io.StringWriter +import java.lang.reflect.InvocationTargetException +import kotlin.reflect.KClass + +val Throwable.causes: Sequence + get() = sequence { + var subCause = cause + while (subCause != null) { + yield(subCause) + subCause = subCause.cause + } + } + +fun Throwable.getRootCause(): Throwable { + var error = this + while (error.cause != null) { + error = error.cause!! + } + if (error is InvocationTargetException) { + error = error.targetException + } + return error +} + +fun Throwable.hasCause(exceptionClazz: KClass): Boolean { + if (exceptionClazz.isInstance(this)) return true + return exceptionClazz.isInstance(this.getRootCause()) +} + +fun Throwable.getStackTraceString(): String { + val sw = StringWriter(256) + val pw = PrintWriter(sw, false) + printStackTrace(pw) + pw.flush() + return sw.toString() +} + +fun Throwable.tryUnwrap(kClass: KClass = RuntimeException::class): Throwable = + if (!kClass.isInstance(this)) this else cause ?: this \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/common/flow/DynamicStateFlow.kt b/app/src/main/java/eu/darken/adsbc/common/flow/DynamicStateFlow.kt new file mode 100644 index 0000000..f871d58 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/flow/DynamicStateFlow.kt @@ -0,0 +1,149 @@ +package eu.darken.adsbc.common.flow + +import eu.darken.adsbc.common.debug.logging.Logging.Priority.VERBOSE +import eu.darken.adsbc.common.debug.logging.asLog +import eu.darken.adsbc.common.debug.logging.log +import kotlinx.coroutines.CancellationException +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.channels.BufferOverflow +import kotlinx.coroutines.flow.* +import kotlinx.coroutines.plus +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock +import kotlin.coroutines.CoroutineContext + +/** + * A thread safe stateful flow that can be updated blocking and async with a lazy initial value provider. + * + * @param loggingTag will be prepended to logging tag, i.e. "$loggingTag:HD" + * @param parentScope on which the update operations and callbacks will be executed on + * @param coroutineContext used in combination with [CoroutineScope] + * @param startValueProvider provides the first value, errors will be rethrown on [CoroutineScope] + */ +class DynamicStateFlow( + loggingTag: String? = null, + parentScope: CoroutineScope, + coroutineContext: CoroutineContext = parentScope.coroutineContext, + private val startValueProvider: suspend CoroutineScope.() -> T +) { + private val lTag = loggingTag?.let { "$it:DSFlow" } + + private val updateActions = MutableSharedFlow>( + replay = Int.MAX_VALUE, + extraBufferCapacity = Int.MAX_VALUE, + onBufferOverflow = BufferOverflow.SUSPEND + ) + private val valueGuard = Mutex() + + private val producer: Flow> = channelFlow { + var currentValue = valueGuard.withLock { + lTag?.let { log(it, VERBOSE) { "Providing startValue..." } } + + startValueProvider().also { startValue -> + val initializer = Update(onError = null, onModify = { startValue }) + send(State(value = startValue, updatedBy = initializer)) + lTag?.let { log(it, VERBOSE) { "...startValue provided and emitted." } } + } + } + + updateActions.collect { update -> + currentValue = valueGuard.withLock { + try { + update.onModify(currentValue).also { + send(State(value = it, updatedBy = update)) + } + } catch (e: Exception) { + lTag?.let { + log(it, VERBOSE) { "Data modifying failed (onError=${update.onError}): ${e.asLog()}" } + } + + if (update.onError != null) { + update.onError.invoke(e) + } else { + send(State(value = currentValue, error = e, updatedBy = update)) + } + + currentValue + } + } + } + + lTag?.let { log(it, VERBOSE) { "internal channelFlow finished." } } + } + + private val internalFlow = producer + .onStart { lTag?.let { log(it, VERBOSE) { "Internal onStart" } } } + .onCompletion { err -> + when { + err is CancellationException -> { + lTag?.let { log(it, VERBOSE) { "internal onCompletion() due to cancellation" } } + } + err != null -> { + lTag?.let { log(it, VERBOSE) { "internal onCompletion() due to error: ${err.asLog()}" } } + } + else -> { + lTag?.let { log(it, VERBOSE) { "internal onCompletion()" } } + } + } + } + .shareIn( + scope = parentScope + coroutineContext, + replay = 1, + started = SharingStarted.Lazily + ) + + val flow: Flow = internalFlow + .map { it.value } + .distinctUntilChanged() + + suspend fun value() = flow.first() + + /** + * Non blocking update method. + * Gets executed on the scope and context this instance was initialized with. + * + * @param onError if you don't provide this, and exception in [onUpdate] will the scope passed to this class + */ + fun updateAsync( + onError: (suspend (Exception) -> Unit) = { throw it }, + onUpdate: suspend T.() -> T, + ) { + val update: Update = Update( + onModify = onUpdate, + onError = onError + ) + runBlocking { updateActions.emit(update) } + } + + /** + * Blocking update method + * Gets executed on the scope and context this instance was initialized with. + * Waiting will happen on the callers scope. + * + * Any errors that occurred during [action] will be rethrown by this method. + */ + suspend fun updateBlocking(action: suspend T.() -> T): T { + val update: Update = Update(onModify = action) + updateActions.emit(update) + + lTag?.let { log(it, VERBOSE) { "Waiting for update." } } + val ourUpdate = internalFlow.first { it.updatedBy == update } + lTag?.let { log(it, VERBOSE) { "Finished waiting, got $ourUpdate" } } + + ourUpdate.error?.let { throw it } + + return ourUpdate.value + } + + private data class Update( + val onModify: suspend T.() -> T, + val onError: (suspend (Exception) -> Unit)? = null, + ) + + private data class State( + val value: T, + val error: Exception? = null, + val updatedBy: Update, + ) +} diff --git a/app/src/main/java/eu/darken/adsbc/common/flow/DynamicStateFlowExtensions.kt b/app/src/main/java/eu/darken/adsbc/common/flow/DynamicStateFlowExtensions.kt new file mode 100644 index 0000000..c444d6b --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/flow/DynamicStateFlowExtensions.kt @@ -0,0 +1,3 @@ +package eu.darken.adsbc.common.flow + + diff --git a/app/src/main/java/eu/darken/adsbc/common/flow/FlowCombineExtensions.kt b/app/src/main/java/eu/darken/adsbc/common/flow/FlowCombineExtensions.kt new file mode 100644 index 0000000..4538729 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/flow/FlowCombineExtensions.kt @@ -0,0 +1,213 @@ +package eu.darken.adsbc.common.flow + +import kotlinx.coroutines.flow.Flow + + +//@Suppress("UNCHECKED_CAST", "LongParameterList") +//inline fun combine( +// flow: Flow, +// flow2: Flow, +// crossinline transform: suspend (T1, T2) -> R +//): Flow = kotlinx.coroutines.flow.combine( +// flow, +// flow2 +//) { args: Array<*> -> +// transform( +// args[0] as T1, +// args[1] as T2 +// ) +//} + +@Suppress("UNCHECKED_CAST", "LongParameterList") +inline fun combine( + flow: Flow, + flow2: Flow, + flow3: Flow, + crossinline transform: suspend (T1, T2, T3) -> R +): Flow = kotlinx.coroutines.flow.combine( + flow, + flow2, + flow3, +) { args: Array<*> -> + transform( + args[0] as T1, + args[1] as T2, + args[2] as T3, + ) +} + +@Suppress("UNCHECKED_CAST", "LongParameterList") +inline fun combine( + flow: Flow, + flow2: Flow, + flow3: Flow, + flow4: Flow, + flow5: Flow, + crossinline transform: suspend (T1, T2, T3, T4, T5) -> R +): Flow = kotlinx.coroutines.flow.combine( + flow, + flow2, + flow3, + flow4, + flow5 +) { args: Array<*> -> + transform( + args[0] as T1, + args[1] as T2, + args[2] as T3, + args[3] as T4, + args[4] as T5 + ) +} + +@Suppress("UNCHECKED_CAST", "LongParameterList") +inline fun combine( + flow: Flow, + flow2: Flow, + flow3: Flow, + flow4: Flow, + flow5: Flow, + flow6: Flow, + crossinline transform: suspend (T1, T2, T3, T4, T5, T6) -> R +): Flow = kotlinx.coroutines.flow.combine( + flow, + flow2, + flow3, + flow4, + flow5, + flow6 +) { args: Array<*> -> + transform( + args[0] as T1, + args[1] as T2, + args[2] as T3, + args[3] as T4, + args[4] as T5, + args[5] as T6 + ) +} + +@Suppress("UNCHECKED_CAST", "LongParameterList") +inline fun combine( + flow: Flow, + flow2: Flow, + flow3: Flow, + flow4: Flow, + flow5: Flow, + flow6: Flow, + flow7: Flow, + crossinline transform: suspend (T1, T2, T3, T4, T5, T6, T7) -> R +): Flow = kotlinx.coroutines.flow.combine( + flow, + flow2, + flow3, + flow4, + flow5, + flow6, + flow7 +) { args: Array<*> -> + transform( + args[0] as T1, + args[1] as T2, + args[2] as T3, + args[3] as T4, + args[4] as T5, + args[5] as T6, + args[6] as T7 + ) +} + +@Suppress("UNCHECKED_CAST", "LongParameterList") +inline fun combine( + flow: Flow, + flow2: Flow, + flow3: Flow, + flow4: Flow, + flow5: Flow, + flow6: Flow, + flow7: Flow, + flow8: Flow, + crossinline transform: suspend (T1, T2, T3, T4, T5, T6, T7, T8) -> R +): Flow = kotlinx.coroutines.flow.combine( + flow, + flow2, + flow3, + flow4, + flow5, + flow6, + flow7, + flow8 +) { args: Array<*> -> + transform( + args[0] as T1, + args[1] as T2, + args[2] as T3, + args[3] as T4, + args[4] as T5, + args[5] as T6, + args[6] as T7, + args[7] as T8 + ) +} + +@Suppress("UNCHECKED_CAST", "LongParameterList") +inline fun combine( + flow: Flow, + flow2: Flow, + flow3: Flow, + flow4: Flow, + flow5: Flow, + flow6: Flow, + flow7: Flow, + flow8: Flow, + flow9: Flow, + flow10: Flow, + crossinline transform: suspend (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) -> R +): Flow = kotlinx.coroutines.flow.combine( + flow, flow2, flow3, flow4, flow5, flow6, flow7, flow8, flow9, flow10 +) { args: Array<*> -> + transform( + args[0] as T1, + args[1] as T2, + args[2] as T3, + args[3] as T4, + args[4] as T5, + args[5] as T6, + args[6] as T7, + args[7] as T8, + args[8] as T9, + args[9] as T10 + ) +} + +@Suppress("UNCHECKED_CAST", "LongParameterList") +inline fun combine( + flow: Flow, + flow2: Flow, + flow3: Flow, + flow4: Flow, + flow5: Flow, + flow6: Flow, + flow7: Flow, + flow8: Flow, + flow9: Flow, + flow10: Flow, + flow11: Flow, + crossinline transform: suspend (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11) -> R +): Flow = kotlinx.coroutines.flow.combine( + flow, flow2, flow3, flow4, flow5, flow6, flow7, flow8, flow9, flow10, flow11 +) { args: Array<*> -> + transform( + args[0] as T1, + args[1] as T2, + args[2] as T3, + args[3] as T4, + args[4] as T5, + args[5] as T6, + args[6] as T7, + args[7] as T8, + args[8] as T9, + args[9] as T10, + args[10] as T11 + ) +} diff --git a/app/src/main/java/eu/darken/adsbc/common/flow/FlowExtensions.kt b/app/src/main/java/eu/darken/adsbc/common/flow/FlowExtensions.kt new file mode 100644 index 0000000..77a8997 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/flow/FlowExtensions.kt @@ -0,0 +1,74 @@ +package eu.darken.adsbc.common.flow + +import eu.darken.adsbc.common.debug.logging.Logging.Priority.ERROR +import eu.darken.adsbc.common.debug.logging.Logging.Priority.VERBOSE +import eu.darken.adsbc.common.debug.logging.asLog +import eu.darken.adsbc.common.debug.logging.log +import eu.darken.adsbc.common.error.hasCause +import kotlinx.coroutines.CancellationException +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.* +import kotlin.time.Duration + +/** + * Create a stateful flow, with the initial value of null, but never emits a null value. + * Helper method to create a new flow without suspending and without initial value + * The flow collector will just wait for the first value + */ +fun Flow.shareLatest( + tag: String? = null, + scope: CoroutineScope, + started: SharingStarted = SharingStarted.WhileSubscribed(replayExpirationMillis = 0) +) = this + .onStart { if (tag != null) log(tag) { "shareLatest(...) start" } } + .onEach { if (tag != null) log(tag) { "shareLatest(...) emission: $it" } } + .onCompletion { if (tag != null) log(tag) { "shareLatest(...) completed." } } + .catch { + if (tag != null) log(tag) { "shareLatest(...) catch(): ${it.asLog()}" } + throw it + } + .stateIn( + scope = scope, + started = started, + initialValue = null + ) + .filterNotNull() + +fun Flow.replayingShare(scope: CoroutineScope) = this.shareIn( + scope = scope, + replay = 1, + started = SharingStarted.WhileSubscribed(replayExpiration = Duration.ZERO) +) + +internal fun Flow.withPrevious(): Flow> = this + .scan(Pair(null, null)) { previous, current -> Pair(previous.second, current) } + .drop(1) + .map { + @Suppress("UNCHECKED_CAST") + it as Pair + } + + +fun Flow.onError(block: suspend (Throwable) -> Unit) = this.catch { + block(it) + throw it +} + +fun Flow.takeUntilAfter(predicate: suspend (T) -> Boolean) = transformWhile { + val fullfilled = predicate(it) + emit(it) + !fullfilled // We keep emitting until condition is fullfilled = true +} + +fun Flow.setupCommonEventHandlers(tag: String, identifier: () -> String) = this + .onStart { log(tag, VERBOSE) { "${identifier()}.onStart()" } } + .onEach { log(tag, VERBOSE) { "${identifier()}.onEach(): $it" } } + .onCompletion { log(tag, VERBOSE) { "${identifier()}.onCompletion()" } } + .catch { + if (it.hasCause(CancellationException::class)) { + log(tag, VERBOSE) { "${identifier()} cancelled" } + } else { + log(tag, ERROR) { "${identifier()} failed: ${it.asLog()}" } + throw it + } + } \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/common/http/HttpModule.kt b/app/src/main/java/eu/darken/adsbc/common/http/HttpModule.kt new file mode 100644 index 0000000..0473ea9 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/http/HttpModule.kt @@ -0,0 +1,48 @@ +package eu.darken.adsbc.common.http + +import com.squareup.moshi.Moshi +import dagger.Module +import dagger.Provides +import dagger.Reusable +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import eu.darken.adsbc.common.debug.autoreport.DebugSettings +import eu.darken.adsbc.common.debug.logging.Logging.Priority.VERBOSE +import eu.darken.adsbc.common.debug.logging.log +import okhttp3.Interceptor +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.converter.moshi.MoshiConverterFactory + +@Module +@InstallIn(SingletonComponent::class) +class HttpModule { + + @Reusable + @Provides + fun defaultHttpClient( + moshiConverterFactory: MoshiConverterFactory, + debugSettings: DebugSettings, + ): OkHttpClient { + val interceptors: List = listOf( + HttpLoggingInterceptor { message -> + log("HTTP", VERBOSE) { message } + }.apply { + if (debugSettings.isDebugModeEnabled.value) { + setLevel(HttpLoggingInterceptor.Level.BODY) + } else { + setLevel(HttpLoggingInterceptor.Level.BASIC) + } + }, + ) + + return OkHttpClient.Builder().apply { + interceptors.forEach { addInterceptor(it) } + }.build() + } + + @Reusable + @Provides + fun moshiConverter(moshi: Moshi): MoshiConverterFactory = MoshiConverterFactory.create(moshi) + +} diff --git a/app/src/main/java/eu/darken/adsbc/common/lists/BaseAdapter.kt b/app/src/main/java/eu/darken/adsbc/common/lists/BaseAdapter.kt new file mode 100644 index 0000000..391a173 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/lists/BaseAdapter.kt @@ -0,0 +1,58 @@ +package eu.darken.adsbc.common.lists + +import android.content.Context +import android.content.res.Resources +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.annotation.* +import androidx.core.content.ContextCompat +import androidx.recyclerview.widget.RecyclerView +import eu.darken.adsbc.common.getColorForAttr + +abstract class BaseAdapter : RecyclerView.Adapter() { + + @CallSuper + final override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): T { + return onCreateBaseVH(parent, viewType) + } + + abstract fun onCreateBaseVH(parent: ViewGroup, viewType: Int): T + + @CallSuper + final override fun onBindViewHolder(holder: T, position: Int) { + onBindBaseVH(holder, position, mutableListOf()) + } + + @CallSuper + final override fun onBindViewHolder(holder: T, position: Int, payloads: MutableList) { + onBindBaseVH(holder, position, payloads) + } + + abstract fun onBindBaseVH(holder: T, position: Int, payloads: MutableList = mutableListOf()) + + abstract class VH(@LayoutRes layoutRes: Int, private val parent: ViewGroup) : RecyclerView.ViewHolder( + LayoutInflater.from(parent.context).inflate(layoutRes, parent, false) + ) { + + val context: Context + get() = parent.context + + val resources: Resources + get() = context.resources + + val layoutInflater: LayoutInflater + get() = LayoutInflater.from(context) + + fun getColor(@ColorRes colorRes: Int): Int = ContextCompat.getColor(context, colorRes) + + fun getColorForAttr(@AttrRes attrRes: Int): Int = context.getColorForAttr(attrRes) + + fun getString(@StringRes stringRes: Int, vararg args: Any): String = context.getString(stringRes, *args) + + fun getQuantityString(@PluralsRes pluralRes: Int, quantity: Int, vararg args: Any): String = + context.resources.getQuantityString(pluralRes, quantity, *args) + + fun getQuantityString(@PluralsRes pluralRes: Int, quantity: Int): String = + context.resources.getQuantityString(pluralRes, quantity, quantity) + } +} diff --git a/app/src/main/java/eu/darken/adsbc/common/lists/BindableVH.kt b/app/src/main/java/eu/darken/adsbc/common/lists/BindableVH.kt new file mode 100644 index 0000000..f9a378f --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/lists/BindableVH.kt @@ -0,0 +1,26 @@ +package eu.darken.adsbc.common.lists + +import androidx.viewbinding.ViewBinding + +interface BindableVH { + + val viewBinding: Lazy + + val onBindData: ViewBindingT.(item: ItemT, payloads: List) -> Unit + + fun bind(item: ItemT, payloads: MutableList = mutableListOf()) = with(viewBinding.value) { + onBindData(item, payloads) + } +} + +@Suppress("unused") +inline fun BindableVH.binding( + payload: Boolean = true, + crossinline block: ViewBindingT.(ItemT) -> Unit, +): ViewBindingT.(item: ItemT, payloads: List) -> Unit = { item: ItemT, payloads: List -> + val newestItem = when (payload) { + true -> payloads.filterIsInstance().lastOrNull() ?: item + false -> item + } + block(newestItem) +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/common/lists/DataAdapter.kt b/app/src/main/java/eu/darken/adsbc/common/lists/DataAdapter.kt new file mode 100644 index 0000000..1cfaf3c --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/lists/DataAdapter.kt @@ -0,0 +1,13 @@ +package eu.darken.bb.common.lists + +import androidx.recyclerview.widget.RecyclerView + +interface DataAdapter { + val data: MutableList +} + +fun X.update(newData: List?, notify: Boolean = true) where X : DataAdapter, X : RecyclerView.Adapter<*> { + data.clear() + if (newData != null) data.addAll(newData) + if (notify) notifyDataSetChanged() +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/common/lists/ItemSwipeTool.kt b/app/src/main/java/eu/darken/adsbc/common/lists/ItemSwipeTool.kt new file mode 100644 index 0000000..83ffcbd --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/lists/ItemSwipeTool.kt @@ -0,0 +1,190 @@ +package eu.darken.adsbc.common.lists + +import android.graphics.Canvas +import android.graphics.Paint +import android.graphics.PorterDuff +import android.graphics.Rect +import android.graphics.drawable.Drawable +import androidx.recyclerview.widget.ItemTouchHelper +import androidx.recyclerview.widget.RecyclerView +import eu.darken.adsbc.R +import eu.darken.adsbc.common.UnitConverter +import eu.darken.adsbc.common.debug.logging.log +import eu.darken.adsbc.common.debug.logging.logTag +import eu.darken.adsbc.common.getColorForAttr +import kotlin.math.absoluteValue + + +@Suppress("UnnecessaryVariable") +class ItemSwipeTool(vararg actions: SwipeAction) { + + init { + require(actions.isNotEmpty()) { + "SwipeTool without actions doesn't make sense." + } + require(actions.map { it.direction }.toSet().size == actions.size) { + "Duplicate direction actions are not allowed." + } + } + + private val touchCallback = object : ItemTouchHelper.SimpleCallback( + 0, + actions.map { it.direction } + .fold(initial = 0, operation = { acc: Int, dir: SwipeAction.Direction -> acc.or(dir.value) }) + ) { + override fun onMove( + recyclerView: RecyclerView, + viewHolder: RecyclerView.ViewHolder, + target: RecyclerView.ViewHolder + ): Boolean { + return false + } + + override fun onSwiped(viewHolder: RecyclerView.ViewHolder, directionValue: Int) { + val action = actions.single { it.direction.value == directionValue } + log(TAG) { "onSwiped(): $action" } + action.callback(viewHolder, action.direction) + } + + override fun onChildDraw( + canvas: Canvas, + recyclerView: RecyclerView, + viewHolder: RecyclerView.ViewHolder, + dX: Float, + dY: Float, + actionState: Int, + isCurrentlyActive: Boolean + ) { + super.onChildDraw(canvas, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive) + + val iv = viewHolder.itemView + val context = recyclerView.context + + val curDir: SwipeAction.Direction? = when { + dX > 0 -> SwipeAction.Direction.RIGHT + dX < 0 -> SwipeAction.Direction.LEFT + else -> null + } + + val background = actions.find { it.direction == curDir }?.background + when (curDir) { + SwipeAction.Direction.RIGHT -> { + background?.setBounds(iv.left, iv.top, iv.left + dX.toInt(), iv.bottom) + + } + SwipeAction.Direction.LEFT -> { + background?.setBounds(iv.right + dX.toInt(), iv.top, iv.right, iv.bottom) + } + else -> background?.setBounds(0, 0, 0, 0) + } + background?.draw(canvas) + + val defaultPadding = UnitConverter.dpToPx(recyclerView.context, 16f) + val textPaint = Paint() + textPaint.color = context.getColorForAttr(R.attr.colorOnError) + textPaint.textSize = UnitConverter.spToPx(context, 18f) + + val actionItem = actions.find { it.direction == curDir } + + when (curDir) { + SwipeAction.Direction.RIGHT -> { + val icon = actionItem?.icon + if (icon != null) { + val iconTop = iv.top + iv.height / 2 - icon.intrinsicHeight / 2 + val iconStart = defaultPadding + val iconEnd = iconStart + icon.intrinsicWidth + val iconBottom = iconTop + icon.intrinsicHeight + if (dX > iconEnd + defaultPadding) { + icon.bounds = Rect(iconStart, iconTop, iconEnd, iconBottom) + } else { + icon.bounds = Rect(0, 0, 0, 0) + } + } + + val label = actionItem?.label + if (label != null) { + val clipBounds = Rect() + canvas.getClipBounds(clipBounds) + textPaint.getTextBounds(label, 0, label.length, clipBounds) + + val textTop = iv.top + iv.height / 2 + (clipBounds.height() / 2) + var textStart = defaultPadding + if (icon != null) textStart += icon.intrinsicWidth + defaultPadding + val textEnd = textStart + clipBounds.width() + if (dX > textEnd) { + canvas.drawText(label, textStart.toFloat(), textTop.toFloat(), textPaint) + } + } + actions.filter { it.icon != icon }.forEach { + it.icon?.setBounds(0, 0, 0, 0) + } + } + SwipeAction.Direction.LEFT -> { + val icon = actions.find { it.direction == curDir }?.icon + if (icon != null) { + val iconTop = iv.top + iv.height / 2 - icon.intrinsicHeight / 2 + val iconStart = iv.width - defaultPadding - icon.intrinsicWidth + val iconEnd = iconStart + icon.intrinsicWidth + val iconBottom = iconTop + icon.intrinsicHeight + if (iv.width - dX.absoluteValue < iconStart - defaultPadding) { + icon.bounds = Rect(iconStart, iconTop, iconEnd, iconBottom) + } else { + icon.bounds = Rect(0, 0, 0, 0) + } + } + val label = actionItem?.label + if (label != null) { + val clipBounds = Rect() + canvas.getClipBounds(clipBounds) + textPaint.getTextBounds(label, 0, label.length, clipBounds) + + val textTop = iv.top + iv.height / 2 + (clipBounds.height() / 2) + var textStart = iv.width - defaultPadding - clipBounds.width() + if (icon != null) textStart = textStart - icon.intrinsicWidth - defaultPadding + val textEnd = iv.width - textStart + if (dX.absoluteValue > textEnd) { + canvas.drawText(label, textStart.toFloat(), textTop.toFloat(), textPaint) + } + } + actions.filter { it.icon != icon }.forEach { + it.icon?.setBounds(0, 0, 0, 0) + } + } + else -> { // NONE + actions.forEach { + it.icon?.setBounds(0, 0, 0, 0) + } + } + } + actions.forEach { + it.icon?.setColorFilter(context.getColorForAttr(R.attr.colorOnError), PorterDuff.Mode.SRC_IN) + it.icon?.draw(canvas) + } + } + } + private val touchHelper by lazy { ItemTouchHelper(touchCallback) } + + fun attach(recyclerView: RecyclerView) { + touchHelper.attachToRecyclerView(recyclerView) + } + + data class SwipeAction( + val direction: Direction, + val callback: (RecyclerView.ViewHolder, Direction) -> Unit, + val icon: Drawable?, + val label: String?, + val background: Drawable? + ) { + enum class Direction(val value: Int) { + LEFT(ItemTouchHelper.LEFT), + RIGHT(ItemTouchHelper.RIGHT), + START(ItemTouchHelper.START), + END(ItemTouchHelper.END), + } + } + + companion object { + internal val TAG = logTag("ItemSwipeTool") + } + +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/common/lists/ListItem.kt b/app/src/main/java/eu/darken/adsbc/common/lists/ListItem.kt new file mode 100644 index 0000000..c8c4b20 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/lists/ListItem.kt @@ -0,0 +1,3 @@ +package eu.darken.bb.common.lists + +interface ListItem \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/common/lists/RecyclerViewExtensions.kt b/app/src/main/java/eu/darken/adsbc/common/lists/RecyclerViewExtensions.kt new file mode 100644 index 0000000..798c7b3 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/lists/RecyclerViewExtensions.kt @@ -0,0 +1,13 @@ +package eu.darken.bb.common.lists + +import androidx.recyclerview.widget.DefaultItemAnimator +import androidx.recyclerview.widget.DividerItemDecoration +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView + +fun RecyclerView.setupDefaults(adapter: RecyclerView.Adapter<*>? = null, dividers: Boolean = true) = apply { + layoutManager = LinearLayoutManager(context) + itemAnimator = DefaultItemAnimator() + if (dividers) addItemDecoration(DividerItemDecoration(context, DividerItemDecoration.VERTICAL)) + if (adapter != null) this.adapter = adapter +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/common/lists/differ/AsyncDiffer.kt b/app/src/main/java/eu/darken/adsbc/common/lists/differ/AsyncDiffer.kt new file mode 100644 index 0000000..8dda1b7 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/lists/differ/AsyncDiffer.kt @@ -0,0 +1,44 @@ +package eu.darken.bb.common.lists.differ + +import androidx.recyclerview.widget.AsyncListDiffer +import androidx.recyclerview.widget.DiffUtil +import eu.darken.adsbc.common.lists.differ.DifferItem +import eu.darken.bb.common.lists.modular.ModularAdapter +import eu.darken.bb.common.lists.modular.mods.StableIdMod + +class AsyncDiffer internal constructor( + adapter: A, + compareItem: (T, T) -> Boolean = { i1, i2 -> i1.stableId == i2.stableId }, + compareItemContent: (T, T) -> Boolean = { i1, i2 -> i1 == i2 }, + determinePayload: (T, T) -> Any? = { i1, i2 -> + when { + i1::class == i2::class -> i1.payloadProvider?.invoke(i1, i2) + else -> null + } + } +) where A : HasAsyncDiffer, A : ModularAdapter<*> { + private val callback = object : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: T, newItem: T): Boolean = compareItem(oldItem, newItem) + override fun areContentsTheSame(oldItem: T, newItem: T): Boolean = compareItemContent(oldItem, newItem) + override fun getChangePayload(oldItem: T, newItem: T): Any? = determinePayload(oldItem, newItem) + } + + private val internalList = mutableListOf() + private val listDiffer = AsyncListDiffer(adapter, callback) + + val currentList: List + get() = synchronized(internalList) { internalList } + + init { + adapter.modules.add(0, StableIdMod(currentList)) + } + + fun submitUpdate(newData: List) { + listDiffer.submitList(newData) { + synchronized(internalList) { + internalList.clear() + internalList.addAll(newData) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/common/lists/differ/AsyncDifferExtensions.kt b/app/src/main/java/eu/darken/adsbc/common/lists/differ/AsyncDifferExtensions.kt new file mode 100644 index 0000000..ff35239 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/lists/differ/AsyncDifferExtensions.kt @@ -0,0 +1,17 @@ +package eu.darken.adsbc.common.lists.differ + +import androidx.recyclerview.widget.RecyclerView +import eu.darken.bb.common.lists.differ.AsyncDiffer +import eu.darken.bb.common.lists.differ.HasAsyncDiffer +import eu.darken.bb.common.lists.modular.ModularAdapter + + +fun X.update(newData: List?) + where X : HasAsyncDiffer, X : RecyclerView.Adapter<*> { + + asyncDiffer.submitUpdate(newData ?: emptyList()) +} + +fun A.setupDiffer(): AsyncDiffer + where A : HasAsyncDiffer, A : ModularAdapter<*> = + AsyncDiffer(this) diff --git a/app/src/main/java/eu/darken/adsbc/common/lists/differ/DifferItem.kt b/app/src/main/java/eu/darken/adsbc/common/lists/differ/DifferItem.kt new file mode 100644 index 0000000..b0b6f3f --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/lists/differ/DifferItem.kt @@ -0,0 +1,10 @@ +package eu.darken.adsbc.common.lists.differ + +import eu.darken.bb.common.lists.ListItem + +interface DifferItem : ListItem { + val stableId: Long + + val payloadProvider: ((DifferItem, DifferItem) -> DifferItem?)? + get() = null +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/common/lists/differ/HasAsyncDiffer.kt b/app/src/main/java/eu/darken/adsbc/common/lists/differ/HasAsyncDiffer.kt new file mode 100644 index 0000000..31bd508 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/lists/differ/HasAsyncDiffer.kt @@ -0,0 +1,12 @@ +package eu.darken.bb.common.lists.differ + +import eu.darken.adsbc.common.lists.differ.DifferItem + +interface HasAsyncDiffer { + + val data: List + get() = asyncDiffer.currentList + + val asyncDiffer: AsyncDiffer<*, T> + +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/common/lists/modular/ModularAdapter.kt b/app/src/main/java/eu/darken/adsbc/common/lists/modular/ModularAdapter.kt new file mode 100644 index 0000000..f547379 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/lists/modular/ModularAdapter.kt @@ -0,0 +1,95 @@ +package eu.darken.bb.common.lists.modular + +import android.view.ViewGroup +import androidx.annotation.CallSuper +import androidx.annotation.LayoutRes +import androidx.recyclerview.widget.RecyclerView +import eu.darken.adsbc.common.lists.BaseAdapter + +abstract class ModularAdapter : BaseAdapter() { + val modules = mutableListOf() + + init { + modules.filterIsInstance().forEach { it.onAdapterReady(this) } + } + + override fun getItemId(position: Int): Long { + modules.filterIsInstance().forEach { + val id = it.getItemId(this, position) + if (id != null) return id + } + return super.getItemId(position) + } + + @CallSuper + override fun getItemViewType(position: Int): Int { + modules.filterIsInstance().forEach { + val type = it.onGetItemType(this, position) + if (type != null) return type + } + return super.getItemViewType(position) + } + + override fun onCreateBaseVH(parent: ViewGroup, viewType: Int): VH { + modules.filterIsInstance>().forEach { + val vh = it.onCreateModularVH(this, parent, viewType) + if (vh != null) return vh + } + throw IllegalStateException("Couldn't create VH for type $viewType with $parent") + } + + @CallSuper + override fun onBindBaseVH(holder: VH, position: Int, payloads: MutableList) { + modules.filterIsInstance>().forEach { + it.onBindModularVH(this, holder, position, payloads) + it.onPostBind(this, holder, position) + } + } + + @CallSuper + override fun onAttachedToRecyclerView(recyclerView: RecyclerView) { + modules.filterIsInstance().forEach { it.onAttachedToRecyclerView(recyclerView) } + super.onAttachedToRecyclerView(recyclerView) + } + + @CallSuper + override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) { + modules.filterIsInstance().forEach { it.onDetachedFromRecyclerView(recyclerView) } + super.onDetachedFromRecyclerView(recyclerView) + } + + abstract class VH(@LayoutRes layoutRes: Int, parent: ViewGroup) : BaseAdapter.VH(layoutRes, parent) + + interface Module { + interface Setup { + fun onAdapterReady(adapter: ModularAdapter<*>) + } + + interface Creator : Module { + fun onCreateModularVH(adapter: ModularAdapter, parent: ViewGroup, viewType: Int): T? + } + + interface Binder : Module { + fun onBindModularVH(adapter: ModularAdapter, vh: T, pos: Int, payloads: MutableList) { + // NOOP + } + + fun onPostBind(adapter: ModularAdapter, vh: T, pos: Int) { + // NOOP + } + } + + interface Typing : Module { + fun onGetItemType(adapter: ModularAdapter<*>, pos: Int): Int? + } + + interface ItemId : Module { + fun getItemId(adapter: ModularAdapter<*>, position: Int): Long? + } + + interface RecyclerViewLifecycle : Module { + fun onDetachedFromRecyclerView(recyclerView: RecyclerView) + fun onAttachedToRecyclerView(recyclerView: RecyclerView) + } + } +} diff --git a/app/src/main/java/eu/darken/adsbc/common/lists/modular/mods/ClickMod.kt b/app/src/main/java/eu/darken/adsbc/common/lists/modular/mods/ClickMod.kt new file mode 100644 index 0000000..e082a96 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/lists/modular/mods/ClickMod.kt @@ -0,0 +1,12 @@ +package eu.darken.adsbc.common.lists.modular.mods + +import eu.darken.bb.common.lists.modular.ModularAdapter + +class ClickMod constructor( + private val listener: (VHT, Int) -> Unit +) : ModularAdapter.Module.Binder { + + override fun onBindModularVH(adapter: ModularAdapter, vh: VHT, pos: Int, payloads: MutableList) { + vh.itemView.setOnClickListener { listener.invoke(vh, pos) } + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/common/lists/modular/mods/DataBinderMod.kt b/app/src/main/java/eu/darken/adsbc/common/lists/modular/mods/DataBinderMod.kt new file mode 100644 index 0000000..bc585c3 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/lists/modular/mods/DataBinderMod.kt @@ -0,0 +1,17 @@ +package eu.darken.adsbc.common.lists.modular.mods + +import androidx.viewbinding.ViewBinding +import eu.darken.adsbc.common.lists.BindableVH +import eu.darken.bb.common.lists.modular.ModularAdapter + +class DataBinderMod constructor( + private val data: List, + private val customBinder: ( + (adapter: ModularAdapter, vh: HolderT, pos: Int, payload: MutableList) -> Unit + )? = null +) : ModularAdapter.Module.Binder where HolderT : BindableVH, HolderT : ModularAdapter.VH { + + override fun onBindModularVH(adapter: ModularAdapter, vh: HolderT, pos: Int, payloads: MutableList) { + customBinder?.invoke(adapter, vh, pos, mutableListOf()) ?: vh.bind(data[pos], payloads) + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/common/lists/modular/mods/SimpleVHCreatorMod.kt b/app/src/main/java/eu/darken/adsbc/common/lists/modular/mods/SimpleVHCreatorMod.kt new file mode 100644 index 0000000..a906341 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/lists/modular/mods/SimpleVHCreatorMod.kt @@ -0,0 +1,15 @@ +package eu.darken.bb.common.lists.modular.mods + +import android.view.ViewGroup +import eu.darken.bb.common.lists.modular.ModularAdapter + +class SimpleVHCreatorMod constructor( + private val viewType: Int = 0, + private val factory: (ViewGroup) -> HolderT +) : ModularAdapter.Module.Creator where HolderT : ModularAdapter.VH { + + override fun onCreateModularVH(adapter: ModularAdapter, parent: ViewGroup, viewType: Int): HolderT? { + if (this.viewType != viewType) return null + return factory.invoke(parent) + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/common/lists/modular/mods/StableIdMod.kt b/app/src/main/java/eu/darken/adsbc/common/lists/modular/mods/StableIdMod.kt new file mode 100644 index 0000000..3602af3 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/lists/modular/mods/StableIdMod.kt @@ -0,0 +1,21 @@ +package eu.darken.bb.common.lists.modular.mods + +import androidx.recyclerview.widget.RecyclerView +import eu.darken.adsbc.common.lists.differ.DifferItem +import eu.darken.bb.common.lists.modular.ModularAdapter + +class StableIdMod constructor( + private val data: List, + private val customResolver: (position: Int) -> Long = { + (data[it] as? DifferItem)?.stableId ?: RecyclerView.NO_ID + } +) : ModularAdapter.Module.ItemId, ModularAdapter.Module.Setup { + + override fun onAdapterReady(adapter: ModularAdapter<*>) { + adapter.setHasStableIds(true) + } + + override fun getItemId(adapter: ModularAdapter<*>, position: Int): Long? { + return customResolver.invoke(position) + } +} diff --git a/app/src/main/java/eu/darken/adsbc/common/lists/modular/mods/TypedVHCreatorMod.kt b/app/src/main/java/eu/darken/adsbc/common/lists/modular/mods/TypedVHCreatorMod.kt new file mode 100644 index 0000000..a76c209 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/lists/modular/mods/TypedVHCreatorMod.kt @@ -0,0 +1,29 @@ +package eu.darken.bb.common.lists.modular.mods + +import android.view.ViewGroup +import eu.darken.bb.common.lists.modular.ModularAdapter + +class TypedVHCreatorMod constructor( + private val typeResolver: (Int) -> Boolean, + private val factory: (ViewGroup) -> HolderT +) : ModularAdapter.Module.Typing, + ModularAdapter.Module.Creator where HolderT : ModularAdapter.VH { + + private fun ModularAdapter<*>.determineOurViewType(): Int { + val typingModules = modules.filterIsInstance(ModularAdapter.Module.Typing::class.java) + return typingModules.indexOf(this@TypedVHCreatorMod) + } + + override fun onGetItemType(adapter: ModularAdapter<*>, pos: Int): Int? { + return if (typeResolver.invoke(pos)) adapter.determineOurViewType() else null + } + + override fun onCreateModularVH( + adapter: ModularAdapter, + parent: ViewGroup, + viewType: Int + ): HolderT? { + if (adapter.determineOurViewType() != viewType) return null + return factory.invoke(parent) + } +} diff --git a/app/src/main/java/eu/darken/adsbc/common/livedata/SingleLiveEvent.kt b/app/src/main/java/eu/darken/adsbc/common/livedata/SingleLiveEvent.kt new file mode 100644 index 0000000..bd71ba3 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/livedata/SingleLiveEvent.kt @@ -0,0 +1,76 @@ +package eu.darken.adsbc.common.livedata + +/* + * Copyright 2017 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import androidx.annotation.MainThread +import androidx.annotation.Nullable +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.Observer +import eu.darken.adsbc.common.debug.logging.Logging.Priority.WARN +import eu.darken.adsbc.common.debug.logging.log +import java.util.concurrent.atomic.AtomicBoolean + +/** + * A lifecycle-aware observable that sends only new updates after subscription, used for events like + * navigation and Snackbar messages. + * + * + * This avoids a common problem with events: on configuration change (like rotation) an update + * can be emitted if the observer is active. This LiveData only calls the observable if there's an + * explicit call to setValue() or call(). + * + * + * Note that only one observer is going to be notified of changes. + * https://github.com/android/architecture-samples/blob/166ca3a93ad14c6e224a3ea9bfcbd773eb048fb0/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/SingleLiveEvent.java + */ +class SingleLiveEvent : MutableLiveData() { + + private val pending = AtomicBoolean(false) + + @MainThread + override fun observe(owner: LifecycleOwner, observer: Observer) { + if (hasActiveObservers()) { + log(WARN) { "Multiple observers registered but only one will be notified of changes." } + } + + // Observe the internal MutableLiveData + super.observe( + owner, + { t -> + if (pending.compareAndSet(true, false)) { + observer.onChanged(t) + } + } + ) + } + + @MainThread + override fun setValue(@Nullable t: T?) { + pending.set(true) + super.setValue(t) + } + + /** + * Used for cases where T is Void, to make calls cleaner. + */ + @MainThread + fun call() { + value = null + } +} diff --git a/app/src/main/java/eu/darken/adsbc/common/navigation/FragmentExtensions.kt b/app/src/main/java/eu/darken/adsbc/common/navigation/FragmentExtensions.kt new file mode 100644 index 0000000..5b03c7f --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/navigation/FragmentExtensions.kt @@ -0,0 +1,38 @@ +package eu.darken.adsbc.common.navigation + +import android.app.Activity +import androidx.annotation.IdRes +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentContainerView +import androidx.fragment.app.FragmentManager +import androidx.navigation.NavController +import androidx.navigation.NavDirections +import androidx.navigation.fragment.NavHostFragment +import androidx.navigation.fragment.findNavController +import eu.darken.adsbc.common.debug.logging.Logging.Priority.WARN +import eu.darken.adsbc.common.debug.logging.asLog +import eu.darken.adsbc.common.debug.logging.log + +fun Fragment.doNavigate(direction: NavDirections) = findNavController().doNavigate(direction) + +fun Fragment.popBackStack(): Boolean { + if (!isAdded) { + IllegalStateException("Fragment is not added").also { + log(WARN) { "Trying to pop backstack on Fragment that isn't added to an Activity: ${it.asLog()}" } + } + return false + } + return findNavController().popBackStack() +} + +/** + * [FragmentContainerView] does not access [NavController] in [Activity.onCreate] + * as workaround [FragmentManager] is used to get the [NavController] + * @param id [Int] NavFragment id + * @see issue-142847973 + */ +@Throws(IllegalStateException::class) +fun FragmentManager.findNavController(@IdRes id: Int): NavController { + val fragment = findFragmentById(id) ?: throw IllegalStateException("Fragment is not found for id:$id") + return NavHostFragment.findNavController(fragment) +} diff --git a/app/src/main/java/eu/darken/adsbc/common/navigation/NavArgsExtensions.kt b/app/src/main/java/eu/darken/adsbc/common/navigation/NavArgsExtensions.kt new file mode 100644 index 0000000..7149b78 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/navigation/NavArgsExtensions.kt @@ -0,0 +1,20 @@ +package eu.darken.adsbc.common.navigation + +import android.os.Bundle +import android.os.Parcelable +import androidx.lifecycle.SavedStateHandle +import androidx.navigation.NavArgs +import androidx.navigation.NavArgsLazy +import java.io.Serializable + +// TODO Remove with "androidx.navigation:navigation-safe-args-gradle-plugin:2.4.0-alpha/stable" +inline fun SavedStateHandle.navArgs() = NavArgsLazy(Args::class) { + Bundle().apply { + keys().forEach { + when (val value = get(it)) { + is Serializable -> putSerializable(it, value) + is Parcelable -> putParcelable(it, value) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/common/navigation/NavControllerExtensions.kt b/app/src/main/java/eu/darken/adsbc/common/navigation/NavControllerExtensions.kt new file mode 100644 index 0000000..d5de555 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/navigation/NavControllerExtensions.kt @@ -0,0 +1,22 @@ +package eu.darken.adsbc.common.navigation + +import android.os.Bundle +import androidx.annotation.IdRes +import androidx.navigation.NavController +import androidx.navigation.NavDirections + +fun NavController.navigateIfNotThere(@IdRes resId: Int, args: Bundle? = null) { + if (currentDestination?.id == resId) return + navigate(resId, args) +} + +fun NavController.doNavigate(direction: NavDirections) { + currentDestination?.getAction(direction.actionId)?.let { navigate(direction) } +} + +fun NavController.isGraphSet(): Boolean = try { + graph + true +} catch (e: IllegalStateException) { + false +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/common/navigation/NavDestinationExtensions.kt b/app/src/main/java/eu/darken/adsbc/common/navigation/NavDestinationExtensions.kt new file mode 100644 index 0000000..f09551b --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/navigation/NavDestinationExtensions.kt @@ -0,0 +1,9 @@ +package eu.darken.adsbc.common.navigation + +import androidx.annotation.IdRes +import androidx.navigation.NavDestination + +fun NavDestination?.hasAction(@IdRes id: Int): Boolean { + if (this == null) return false + return getAction(id) != null +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/common/navigation/NavDirectionsExtensions.kt b/app/src/main/java/eu/darken/adsbc/common/navigation/NavDirectionsExtensions.kt new file mode 100644 index 0000000..019bd3d --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/navigation/NavDirectionsExtensions.kt @@ -0,0 +1,13 @@ +package eu.darken.adsbc.common.navigation + +import androidx.lifecycle.MutableLiveData +import androidx.navigation.NavDirections +import eu.darken.adsbc.common.livedata.SingleLiveEvent + +fun NavDirections.navVia(pub: MutableLiveData) = pub.postValue(this) + +fun NavDirections.navVia(provider: NavEventSource) = this.navVia(provider.navEvents) + +interface NavEventSource { + val navEvents: SingleLiveEvent +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/common/notifications/PendingIntentCompat.kt b/app/src/main/java/eu/darken/adsbc/common/notifications/PendingIntentCompat.kt new file mode 100644 index 0000000..3ae64ac --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/notifications/PendingIntentCompat.kt @@ -0,0 +1,12 @@ +package eu.darken.adsbc.common.notifications + +import android.app.PendingIntent +import eu.darken.adsbc.common.hasApiLevel + +object PendingIntentCompat { + val FLAG_IMMUTABLE: Int = if (hasApiLevel(31)) { + PendingIntent.FLAG_IMMUTABLE + } else { + 0 + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/common/preferences/FlowPreference.kt b/app/src/main/java/eu/darken/adsbc/common/preferences/FlowPreference.kt new file mode 100644 index 0000000..9f19e91 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/preferences/FlowPreference.kt @@ -0,0 +1,67 @@ +package eu.darken.adsbc.common.preferences + +import android.content.SharedPreferences +import androidx.core.content.edit +import eu.darken.adsbc.common.debug.logging.Logging.Priority.VERBOSE +import eu.darken.adsbc.common.debug.logging.log +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow + +class FlowPreference constructor( + private val preferences: SharedPreferences, + val key: String, + val rawReader: (Any?) -> T, + val rawWriter: (T) -> Any? +) { + + private val flowInternal = MutableStateFlow(value) + val flow: Flow = flowInternal + + private val preferenceChangeListener = + SharedPreferences.OnSharedPreferenceChangeListener { changedPrefs, changedKey -> + if (changedKey != key) return@OnSharedPreferenceChangeListener + + val newValue = rawReader(changedPrefs.all[key]) + + val currentValue = flowInternal.value + if (currentValue != newValue && flowInternal.compareAndSet(currentValue, newValue)) { + log(VERBOSE) { "$changedPrefs:$changedKey changed to $newValue" } + } + } + + init { + preferences.registerOnSharedPreferenceChangeListener(preferenceChangeListener) + } + + var value: T + get() = rawReader(valueRaw) + set(newVal) { + valueRaw = rawWriter(newVal) + } + + var valueRaw: Any? + get() = preferences.all[key] ?: rawWriter(rawReader(null)) + set(value) { + preferences.edit { + when (value) { + is Boolean -> putBoolean(key, value) + is String -> putString(key, value) + is Int -> putInt(key, value) + is Long -> putLong(key, value) + is Float -> putFloat(key, value) + null -> remove(key) + else -> throw NotImplementedError() + } + + } + flowInternal.value = rawReader(value) + } + + fun update(update: (T) -> T) { + value = update(value) + } + + fun reset() { + valueRaw = null + } +} diff --git a/app/src/main/java/eu/darken/adsbc/common/preferences/FlowPreferenceExtension.kt b/app/src/main/java/eu/darken/adsbc/common/preferences/FlowPreferenceExtension.kt new file mode 100644 index 0000000..d1ef239 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/preferences/FlowPreferenceExtension.kt @@ -0,0 +1,43 @@ +package eu.darken.adsbc.common.preferences + +import android.content.SharedPreferences + + +inline fun basicReader(defaultValue: T): (rawValue: Any?) -> T = + { rawValue -> + (rawValue ?: defaultValue) as T + } + +inline fun basicWriter(): (T) -> Any? = + { value -> + when (value) { + is Boolean -> value + is String -> value + is Int -> value + is Long -> value + is Float -> value + null -> null + else -> throw NotImplementedError() + } + } + +inline fun SharedPreferences.createFlowPreference( + key: String, + defaultValue: T = null as T +) = FlowPreference( + preferences = this, + key = key, + rawReader = basicReader(defaultValue), + rawWriter = basicWriter() +) + +inline fun SharedPreferences.createFlowPreference( + key: String, + noinline reader: (rawValue: Any?) -> T, + noinline writer: (value: T) -> Any? +) = FlowPreference( + preferences = this, + key = key, + rawReader = reader, + rawWriter = writer +) diff --git a/app/src/main/java/eu/darken/adsbc/common/preferences/FlowPreferenceMoshiExtension.kt b/app/src/main/java/eu/darken/adsbc/common/preferences/FlowPreferenceMoshiExtension.kt new file mode 100644 index 0000000..132fb86 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/preferences/FlowPreferenceMoshiExtension.kt @@ -0,0 +1,35 @@ +package eu.darken.adsbc.common.preferences + +import android.content.SharedPreferences +import com.squareup.moshi.Moshi + +inline fun moshiReader( + moshi: Moshi, + defaultValue: T, +): (Any?) -> T { + val adapter = moshi.adapter(T::class.java) + return { rawValue -> + rawValue as String? + rawValue?.let { adapter.fromJson(it) } ?: defaultValue + } +} + +inline fun moshiWriter( + moshi: Moshi, +): (T) -> Any? { + val adapter = moshi.adapter(T::class.java) + return { newValue: T -> + newValue?.let { adapter.toJson(it) } + } +} + +inline fun SharedPreferences.createFlowPreference( + key: String, + defaultValue: T = null as T, + moshi: Moshi, +) = FlowPreference( + preferences = this, + key = key, + rawReader = moshiReader(moshi, defaultValue), + rawWriter = moshiWriter(moshi) +) \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/common/preferences/PreferenceStoreMapper.kt b/app/src/main/java/eu/darken/adsbc/common/preferences/PreferenceStoreMapper.kt new file mode 100644 index 0000000..1199049 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/preferences/PreferenceStoreMapper.kt @@ -0,0 +1,76 @@ +package eu.darken.adsbc.common.preferences + +import androidx.preference.PreferenceDataStore + +open class PreferenceStoreMapper( + private vararg val flowPreferences: FlowPreference<*> +) : PreferenceDataStore() { + + override fun getBoolean(key: String, defValue: Boolean): Boolean { + return flowPreferences.singleOrNull { it.key == key }?.let { flowPref -> + flowPref.valueRaw as Boolean + } ?: throw NotImplementedError("getBoolean(key=$key, defValue=$defValue)") + } + + override fun putBoolean(key: String, value: Boolean) { + flowPreferences.singleOrNull { it.key == key }?.let { flowPref -> + flowPref.valueRaw = value + } ?: throw NotImplementedError("putBoolean(key=$key, defValue=$value)") + } + + override fun getString(key: String, defValue: String?): String? { + return flowPreferences.singleOrNull { it.key == key }?.let { flowPref -> + flowPref.valueRaw as String? + } ?: throw NotImplementedError("getString(key=$key, defValue=$defValue)") + } + + override fun putString(key: String, value: String?) { + flowPreferences.singleOrNull { it.key == key }?.let { flowPref -> + flowPref.valueRaw = value + } ?: throw NotImplementedError("putString(key=$key, defValue=$value)") + } + + override fun getInt(key: String?, defValue: Int): Int { + return flowPreferences.singleOrNull { it.key == key }?.let { flowPref -> + flowPref.valueRaw as Int + } ?: throw NotImplementedError("getInt(key=$key, defValue=$defValue)") + } + + override fun putInt(key: String?, value: Int) { + flowPreferences.singleOrNull { it.key == key }?.let { flowPref -> + flowPref.valueRaw = value + } ?: throw NotImplementedError("putInt(key=$key, defValue=$value)") + } + + override fun getLong(key: String?, defValue: Long): Long { + return flowPreferences.singleOrNull { it.key == key }?.let { flowPref -> + flowPref.valueRaw as Long + } ?: throw NotImplementedError("getLong(key=$key, defValue=$defValue)") + } + + override fun putLong(key: String?, value: Long) { + flowPreferences.singleOrNull { it.key == key }?.let { flowPref -> + flowPref.valueRaw = value + } ?: throw NotImplementedError("putLong(key=$key, defValue=$value)") + } + + override fun getFloat(key: String?, defValue: Float): Float { + return flowPreferences.singleOrNull { it.key == key }?.let { flowPref -> + flowPref.valueRaw as Float + } ?: throw NotImplementedError("getFloat(key=$key, defValue=$defValue)") + } + + override fun putFloat(key: String?, value: Float) { + flowPreferences.singleOrNull { it.key == key }?.let { flowPref -> + flowPref.valueRaw = value + } ?: throw NotImplementedError("putFloat(key=$key, defValue=$value)") + } + + override fun putStringSet(key: String?, values: MutableSet?) { + throw NotImplementedError("putStringSet(key=$key, defValue=$values)") + } + + override fun getStringSet(key: String?, defValues: MutableSet?): MutableSet { + throw NotImplementedError("getStringSet(key=$key, defValue=$defValues)") + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/common/preferences/Settings.kt b/app/src/main/java/eu/darken/adsbc/common/preferences/Settings.kt new file mode 100644 index 0000000..ade0f4f --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/preferences/Settings.kt @@ -0,0 +1,12 @@ +package eu.darken.adsbc.common.preferences + +import android.content.SharedPreferences +import androidx.preference.PreferenceDataStore + +abstract class Settings { + + abstract val preferenceDataStore: PreferenceDataStore + + abstract val preferences: SharedPreferences + +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/common/preferences/SharedPreferenceExtensions.kt b/app/src/main/java/eu/darken/adsbc/common/preferences/SharedPreferenceExtensions.kt new file mode 100644 index 0000000..5ca2deb --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/preferences/SharedPreferenceExtensions.kt @@ -0,0 +1,16 @@ +package eu.darken.adsbc.common.preferences + +import android.content.SharedPreferences +import androidx.core.content.edit +import eu.darken.adsbc.common.debug.logging.Logging.Priority.VERBOSE +import eu.darken.adsbc.common.debug.logging.log + +fun SharedPreferences.clearAndNotify() { + val currentKeys = this.all.keys.toSet() + log(VERBOSE) { "$this clearAndNotify(): $currentKeys" } + edit { + currentKeys.forEach { remove(it) } + } + // Clear does not notify anyone using registerOnSharedPreferenceChangeListener + edit(commit = true) { clear() } +} diff --git a/app/src/main/java/eu/darken/adsbc/common/room/CommonConverters.kt b/app/src/main/java/eu/darken/adsbc/common/room/CommonConverters.kt new file mode 100644 index 0000000..8ae1373 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/room/CommonConverters.kt @@ -0,0 +1,35 @@ +package eu.darken.adsbc.common.room + +import androidx.room.TypeConverter +import eu.darken.adsbc.feeder.core.ADSBxId +import eu.darken.adsbc.feeder.core.FeederId +import java.time.Instant +import java.util.* + +class CommonConverters { + + @TypeConverter + fun toUUID(value: String?): UUID? = value?.let { UUID.fromString(it) } + + @TypeConverter + fun fromUUID(uuid: UUID?): String? = uuid?.toString() + + @TypeConverter + fun toInstant(value: String?): Instant? = value?.let { Instant.parse(it) } + + @TypeConverter + fun fromInstant(date: Instant?): String? = date?.toString() + + @TypeConverter + fun toAdsbxId(value: String?): ADSBxId? = value?.let { ADSBxId(it) } + + @TypeConverter + fun fromAdsbxId(date: ADSBxId?): String? = date?.value + + @TypeConverter + fun toFeederId(value: String?): FeederId? = value?.let { FeederId(it) } + + @TypeConverter + fun fromFeederId(id: FeederId?): String? = id?.value + +} diff --git a/app/src/main/java/eu/darken/adsbc/common/serialiation/SerializationModule.kt b/app/src/main/java/eu/darken/adsbc/common/serialiation/SerializationModule.kt new file mode 100644 index 0000000..8b0fd65 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/serialiation/SerializationModule.kt @@ -0,0 +1,19 @@ +package eu.darken.adsbc.common.serialiation + +import com.squareup.moshi.Moshi +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + + +@Module +@InstallIn(SingletonComponent::class) +class SerializationModule { + + @Provides + @Singleton + fun moshi(): Moshi = Moshi.Builder() + .build() +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/common/ui/ConfirmableActionAdapterVH.kt b/app/src/main/java/eu/darken/adsbc/common/ui/ConfirmableActionAdapterVH.kt new file mode 100644 index 0000000..c75a613 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/ui/ConfirmableActionAdapterVH.kt @@ -0,0 +1,78 @@ +package eu.darken.adsbc.common.ui + +import android.view.ViewGroup +import androidx.annotation.DrawableRes +import androidx.core.view.isGone +import eu.darken.adsbc.R +import eu.darken.adsbc.common.lists.BindableVH +import eu.darken.adsbc.common.lists.differ.DifferItem +import eu.darken.adsbc.databinding.ViewActionAdapterLineBinding +import eu.darken.bb.common.lists.modular.ModularAdapter + +abstract class ConfirmableActionAdapterVH(parent: ViewGroup) : + ModularAdapter.VH(R.layout.view_action_adapter_line, parent), + BindableVH, ViewActionAdapterLineBinding> { + + override val viewBinding = lazy { ViewActionAdapterLineBinding.bind(itemView) } + override val onBindData: ViewActionAdapterLineBinding.( + item: Confirmable, + payloads: List + ) -> Unit = { item, _ -> + val data = item.data + icon.setImageResource(getIcon(data)) + name.text = getLabel(data) + + val updateDescription = { + when { + item.currentLvl > 1 -> { + description.setText(R.string.general_confirmation_confirmation_label) + itemView.setBackgroundColor(getColorForAttr(R.attr.colorErrorContainer)) + } + item.currentLvl == 1 -> { + description.setText(R.string.general_confirmation_label) + itemView.setBackgroundColor(getColorForAttr(R.attr.colorErrorContainer)) + } + else -> { + description.text = getDesc(data) + itemView.setBackgroundColor(getColorForAttr(R.attr.backgroundColor)) + } + } + description.isGone = description.text.isNullOrEmpty() + } + updateDescription() + + + itemView.setOnClickListener { + item.guardedAction { item.onClick(it) } + updateDescription() + } + } + + @DrawableRes + abstract fun getIcon(item: T): Int + + abstract fun getLabel(item: T): String + + open fun getDesc(item: T): String? = null + +} + +data class Confirmable( + val data: T, + val requiredLvl: Int = 0, + var currentLvl: Int = 0, + val onClick: (T) -> Unit +) : DifferItem { + + fun guardedAction(action: (T) -> Unit) { + if (currentLvl >= requiredLvl || requiredLvl == 0) { + action(data) + currentLvl = 0 + } else { + currentLvl++ + } + } + + override val stableId: Long + get() = data.stableId +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/common/uix/Activity2.kt b/app/src/main/java/eu/darken/adsbc/common/uix/Activity2.kt new file mode 100644 index 0000000..de0c7f0 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/uix/Activity2.kt @@ -0,0 +1,44 @@ +package eu.darken.adsbc.common.uix + +import android.content.Intent +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.LiveData +import eu.darken.adsbc.common.debug.logging.Logging.Priority.VERBOSE +import eu.darken.adsbc.common.debug.logging.log +import eu.darken.adsbc.common.debug.logging.logTag + +abstract class Activity2 : AppCompatActivity() { + internal val tag: String = + logTag("Activity", this.javaClass.simpleName + "(" + Integer.toHexString(hashCode()) + ")") + + override fun onCreate(savedInstanceState: Bundle?) { + log(tag, VERBOSE) { "onCreate(savedInstanceState=$savedInstanceState)" } + super.onCreate(savedInstanceState) + } + + override fun onResume() { + log(tag, VERBOSE) { "onResume()" } + super.onResume() + } + + override fun onPause() { + log(tag, VERBOSE) { "onPause()" } + super.onPause() + } + + override fun onDestroy() { + log(tag, VERBOSE) { "onDestroy()" } + super.onDestroy() + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + log(tag, VERBOSE) { "onActivityResult(requestCode=$requestCode, resultCode=$resultCode, data=$data)" } + super.onActivityResult(requestCode, resultCode, data) + } + + fun LiveData.observe2(callback: (T) -> Unit) { + observe(this@Activity2) { callback.invoke(it) } + } + +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/common/uix/BottomSheetDialogFragment2.kt b/app/src/main/java/eu/darken/adsbc/common/uix/BottomSheetDialogFragment2.kt new file mode 100644 index 0000000..d2d367f --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/uix/BottomSheetDialogFragment2.kt @@ -0,0 +1,95 @@ +package eu.darken.adsbc.common.uix + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.lifecycle.LiveData +import androidx.viewbinding.ViewBinding +import com.google.android.material.bottomsheet.BottomSheetDialogFragment +import eu.darken.adsbc.common.debug.logging.Logging.Priority.VERBOSE +import eu.darken.adsbc.common.debug.logging.log +import eu.darken.adsbc.common.debug.logging.logTag +import eu.darken.adsbc.common.error.asErrorDialogBuilder +import eu.darken.adsbc.common.navigation.doNavigate +import eu.darken.adsbc.common.navigation.popBackStack +import eu.darken.adsbc.common.observe2 + + +abstract class BottomSheetDialogFragment2 : BottomSheetDialogFragment() { + + abstract val ui: ViewBinding + abstract val vm: ViewModel3 + + internal val tag: String = + logTag("Fragment", "${this.javaClass.simpleName}(${Integer.toHexString(hashCode())})") + + override fun onAttach(context: Context) { + log(tag, VERBOSE) { "onAttach(context=$context)" } + super.onAttach(context) + } + + override fun onCreate(savedInstanceState: Bundle?) { + log(tag, VERBOSE) { "onCreate(savedInstanceState=$savedInstanceState)" } + super.onCreate(savedInstanceState) + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + log(tag, VERBOSE) { + "onCreateView(inflater=$inflater, container=$container, savedInstanceState=$savedInstanceState" + } + return super.onCreateView(inflater, container, savedInstanceState) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + log(tag, VERBOSE) { "onViewCreated(view=$view, savedInstanceState=$savedInstanceState)" } + super.onViewCreated(view, savedInstanceState) + + vm.navEvents.observe2(this, ui) { dir -> dir?.let { doNavigate(it) } ?: popBackStack() } + vm.errorEvents.observe2(this, ui) { it.asErrorDialogBuilder(requireContext()).show() } + } + + override fun onActivityCreated(savedInstanceState: Bundle?) { + log(tag, VERBOSE) { "onActivityCreated(savedInstanceState=$savedInstanceState)" } + super.onActivityCreated(savedInstanceState) + } + + override fun onResume() { + log(tag, VERBOSE) { "onResume()" } + super.onResume() + } + + override fun onPause() { + log(tag, VERBOSE) { "onPause()" } + super.onPause() + } + + override fun onDestroyView() { + log(tag, VERBOSE) { "onDestroyView()" } + super.onDestroyView() + } + + override fun onDetach() { + log(tag, VERBOSE) { "onDetach()" } + super.onDetach() + } + + override fun onDestroy() { + log(tag, VERBOSE) { "onDestroy()" } + super.onDestroy() + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + log(tag, VERBOSE) { "onActivityResult(requestCode=$requestCode, resultCode=$resultCode, data=$data)" } + super.onActivityResult(requestCode, resultCode, data) + } + + inline fun LiveData.observe2( + ui: VB, + crossinline callback: VB.(T) -> Unit + ) { + observe(viewLifecycleOwner) { callback.invoke(ui, it) } + } +} diff --git a/app/src/main/java/eu/darken/adsbc/common/uix/Fragment2.kt b/app/src/main/java/eu/darken/adsbc/common/uix/Fragment2.kt new file mode 100644 index 0000000..e745217 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/uix/Fragment2.kt @@ -0,0 +1,80 @@ +package eu.darken.adsbc.common.uix + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.annotation.LayoutRes +import androidx.fragment.app.Fragment +import eu.darken.adsbc.common.debug.logging.Logging.Priority.VERBOSE +import eu.darken.adsbc.common.debug.logging.log +import eu.darken.adsbc.common.debug.logging.logTag + + +abstract class Fragment2(@LayoutRes val layoutRes: Int?) : Fragment(layoutRes ?: 0) { + + constructor() : this(null) + + internal val tag: String = + logTag("Fragment", "${this.javaClass.simpleName}(${Integer.toHexString(hashCode())})") + + override fun onAttach(context: Context) { + log(tag, VERBOSE) { "onAttach(context=$context)" } + super.onAttach(context) + } + + override fun onCreate(savedInstanceState: Bundle?) { + log(tag, VERBOSE) { "onCreate(savedInstanceState=$savedInstanceState)" } + super.onCreate(savedInstanceState) + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + log(tag, VERBOSE) { + "onCreateView(inflater=$inflater, container=$container, savedInstanceState=$savedInstanceState" + } + return layoutRes?.let { inflater.inflate(it, container, false) } + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + log(tag, VERBOSE) { "onViewCreated(view=$view, savedInstanceState=$savedInstanceState)" } + super.onViewCreated(view, savedInstanceState) + } + + override fun onActivityCreated(savedInstanceState: Bundle?) { + log(tag, VERBOSE) { "onActivityCreated(savedInstanceState=$savedInstanceState)" } + super.onActivityCreated(savedInstanceState) + } + + override fun onResume() { + log(tag, VERBOSE) { "onResume()" } + super.onResume() + } + + override fun onPause() { + log(tag, VERBOSE) { "onPause()" } + super.onPause() + } + + override fun onDestroyView() { + log(tag, VERBOSE) { "onDestroyView()" } + super.onDestroyView() + } + + override fun onDetach() { + log(tag, VERBOSE) { "onDetach()" } + super.onDetach() + } + + override fun onDestroy() { + log(tag, VERBOSE) { "onDestroy()" } + super.onDestroy() + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + log(tag, VERBOSE) { "onActivityResult(requestCode=$requestCode, resultCode=$resultCode, data=$data)" } + super.onActivityResult(requestCode, resultCode, data) + } + +} diff --git a/app/src/main/java/eu/darken/adsbc/common/uix/Fragment3.kt b/app/src/main/java/eu/darken/adsbc/common/uix/Fragment3.kt new file mode 100644 index 0000000..9a043d0 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/uix/Fragment3.kt @@ -0,0 +1,53 @@ +package eu.darken.adsbc.common.uix + +import android.os.Bundle +import android.view.View +import androidx.annotation.LayoutRes +import androidx.lifecycle.LiveData +import androidx.viewbinding.ViewBinding +import eu.darken.adsbc.common.debug.logging.log +import eu.darken.adsbc.common.error.asErrorDialogBuilder +import eu.darken.adsbc.common.navigation.doNavigate +import eu.darken.adsbc.common.navigation.popBackStack + + +abstract class Fragment3(@LayoutRes layoutRes: Int?) : Fragment2(layoutRes) { + + constructor() : this(null) + + abstract val ui: ViewBinding? + abstract val vm: ViewModel3 + + var onErrorEvent: ((Throwable) -> Boolean)? = null + + var onFinishEvent: (() -> Unit)? = null + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + vm.navEvents.observe2(ui) { + log { "navEvents: $it" } + + it?.run { doNavigate(this) } ?: onFinishEvent?.invoke() ?: popBackStack() + } + + vm.errorEvents.observe2(ui) { + val showDialog = onErrorEvent?.invoke(it) ?: true + if (showDialog) it.asErrorDialogBuilder(requireContext()).show() + } + } + + inline fun LiveData.observe2( + crossinline callback: (T) -> Unit + ) { + observe(viewLifecycleOwner) { callback.invoke(it) } + } + + inline fun LiveData.observe2( + ui: VB, + crossinline callback: VB.(T) -> Unit + ) { + observe(viewLifecycleOwner) { callback.invoke(ui, it) } + } + +} diff --git a/app/src/main/java/eu/darken/adsbc/common/uix/PreferenceFragment2.kt b/app/src/main/java/eu/darken/adsbc/common/uix/PreferenceFragment2.kt new file mode 100644 index 0000000..23a4d06 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/uix/PreferenceFragment2.kt @@ -0,0 +1,70 @@ +package eu.darken.adsbc.common.uix + +import android.content.SharedPreferences +import android.os.Bundle +import android.view.LayoutInflater +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup +import androidx.annotation.MenuRes +import androidx.annotation.XmlRes +import androidx.appcompat.widget.Toolbar +import androidx.fragment.app.Fragment +import androidx.preference.PreferenceFragmentCompat +import eu.darken.adsbc.common.preferences.Settings +import eu.darken.adsbc.main.ui.settings.SettingsFragment + +abstract class PreferenceFragment2 + : PreferenceFragmentCompat(), SharedPreferences.OnSharedPreferenceChangeListener { + + abstract val settings: Settings + + @get:XmlRes + abstract val preferenceFile: Int + + val toolbar: Toolbar + get() = (parentFragment as SettingsFragment).toolbar + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + toolbar.menu.clear() + return super.onCreateView(inflater, container, savedInstanceState) + } + + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + preferenceManager.preferenceDataStore = settings.preferenceDataStore + settings.preferences.registerOnSharedPreferenceChangeListener(this) + refreshPreferenceScreen() + } + + override fun onDestroy() { + settings.preferences.unregisterOnSharedPreferenceChangeListener(this) + super.onDestroy() + } + + override fun getCallbackFragment(): Fragment? = parentFragment + + fun refreshPreferenceScreen() { + if (preferenceScreen != null) preferenceScreen = null + addPreferencesFromResource(preferenceFile) + onPreferencesCreated() + } + + open fun onPreferencesCreated() { + + } + + override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) { + + } + + fun setupMenu(@MenuRes menuResId: Int, block: (MenuItem) -> Unit) { + toolbar.apply { + menu.clear() + inflateMenu(menuResId) + setOnMenuItemClickListener { + block(it) + true + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/common/uix/Service2.kt b/app/src/main/java/eu/darken/adsbc/common/uix/Service2.kt new file mode 100644 index 0000000..191cdaa --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/uix/Service2.kt @@ -0,0 +1,52 @@ +package eu.darken.adsbc.common.uix + +import android.app.Service +import android.content.Intent +import android.content.res.Configuration +import eu.darken.adsbc.common.debug.logging.log +import eu.darken.adsbc.common.debug.logging.logTag + +abstract class Service2 : Service() { + private val tag: String = + logTag("Service", this.javaClass.simpleName + "(" + Integer.toHexString(this.hashCode()) + ")") + + override fun onCreate() { + log(tag) { "onCreate()" } + super.onCreate() + } + + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + log(tag) { "onStartCommand(intent=$intent, flags=$flags startId=$startId)" } + return super.onStartCommand(intent, flags, startId) + } + + override fun onDestroy() { + log(tag) { "onDestroy()" } + super.onDestroy() + } + + override fun onConfigurationChanged(newConfig: Configuration) { + log(tag) { "onConfigurationChanged(newConfig=$newConfig)" } + super.onConfigurationChanged(newConfig) + } + + override fun onLowMemory() { + log(tag) { "onLowMemory()" } + super.onLowMemory() + } + + override fun onUnbind(intent: Intent): Boolean { + log(tag) { "onUnbind(intent=$intent)" } + return super.onUnbind(intent) + } + + override fun onRebind(intent: Intent) { + log(tag) { "onRebind(intent=$intent)" } + super.onRebind(intent) + } + + override fun onTaskRemoved(rootIntent: Intent) { + log(tag) { "onTaskRemoved(rootIntent=$rootIntent)" } + super.onTaskRemoved(rootIntent) + } +} diff --git a/app/src/main/java/eu/darken/adsbc/common/uix/ViewModel1.kt b/app/src/main/java/eu/darken/adsbc/common/uix/ViewModel1.kt new file mode 100644 index 0000000..71ac772 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/uix/ViewModel1.kt @@ -0,0 +1,20 @@ +package eu.darken.adsbc.common.uix + +import androidx.annotation.CallSuper +import androidx.lifecycle.ViewModel +import eu.darken.adsbc.common.debug.logging.log +import eu.darken.adsbc.common.debug.logging.logTag + +abstract class ViewModel1 : ViewModel() { + internal val tag: String = logTag("VM", javaClass.simpleName) + + init { + log(tag) { "Initialized" } + } + + @CallSuper + override fun onCleared() { + log(tag) { "onCleared()" } + super.onCleared() + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/common/uix/ViewModel2.kt b/app/src/main/java/eu/darken/adsbc/common/uix/ViewModel2.kt new file mode 100644 index 0000000..4734b1d --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/uix/ViewModel2.kt @@ -0,0 +1,63 @@ +package eu.darken.adsbc.common.uix + +import androidx.lifecycle.asLiveData +import androidx.lifecycle.viewModelScope +import eu.darken.adsbc.common.coroutine.DefaultDispatcherProvider +import eu.darken.adsbc.common.coroutine.DispatcherProvider +import eu.darken.adsbc.common.debug.logging.Logging.Priority.WARN +import eu.darken.adsbc.common.debug.logging.asLog +import eu.darken.adsbc.common.debug.logging.log +import eu.darken.adsbc.common.error.ErrorEventSource +import eu.darken.adsbc.common.flow.DynamicStateFlow +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.launchIn +import kotlin.coroutines.CoroutineContext + + +abstract class ViewModel2( + private val dispatcherProvider: DispatcherProvider = DefaultDispatcherProvider(), +) : ViewModel1() { + + val vmScope = viewModelScope + dispatcherProvider.Default + + var launchErrorHandler: CoroutineExceptionHandler? = null + + private fun getVMContext(): CoroutineContext { + val dispatcher = dispatcherProvider.Default + return getErrorHandler()?.let { dispatcher + it } ?: dispatcher + } + + private fun getErrorHandler(): CoroutineExceptionHandler? { + val handler = launchErrorHandler + if (handler != null) return handler + + if (this is ErrorEventSource) { + return CoroutineExceptionHandler { _, ex -> + log(WARN) { "Error during launch: ${ex.asLog()}" } + errorEvents.postValue(ex) + } + } + + return null + } + + fun DynamicStateFlow.asLiveData2() = flow.asLiveData2() + + fun Flow.asLiveData2() = this.asLiveData(context = getVMContext()) + + fun launch( + scope: CoroutineScope = viewModelScope, + context: CoroutineContext = getVMContext(), + block: suspend CoroutineScope.() -> Unit + ) { + try { + scope.launch(context = context, block = block) + } catch (e: CancellationException) { + log(tag, WARN) { "launch()ed coroutine was canceled (scope=$scope): ${e.asLog()}" } + } + } + + open fun Flow.launchInViewModel() = this.launchIn(vmScope) + +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/common/uix/ViewModel3.kt b/app/src/main/java/eu/darken/adsbc/common/uix/ViewModel3.kt new file mode 100644 index 0000000..49e6afe --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/uix/ViewModel3.kt @@ -0,0 +1,33 @@ +package eu.darken.adsbc.common.uix + +import androidx.navigation.NavDirections +import eu.darken.adsbc.common.coroutine.DispatcherProvider +import eu.darken.adsbc.common.debug.logging.asLog +import eu.darken.adsbc.common.debug.logging.log +import eu.darken.adsbc.common.error.ErrorEventSource +import eu.darken.adsbc.common.flow.setupCommonEventHandlers +import eu.darken.adsbc.common.livedata.SingleLiveEvent +import eu.darken.adsbc.common.navigation.NavEventSource +import kotlinx.coroutines.CoroutineExceptionHandler +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.launchIn + + +abstract class ViewModel3( + dispatcherProvider: DispatcherProvider, +) : ViewModel2(dispatcherProvider), NavEventSource, ErrorEventSource { + + override val navEvents = SingleLiveEvent() + override val errorEvents = SingleLiveEvent() + + init { + launchErrorHandler = CoroutineExceptionHandler { _, ex -> + log(tag) { "Error during launch: ${ex.asLog()}" } + errorEvents.postValue(ex) + } + } + + override fun Flow.launchInViewModel() = this + .setupCommonEventHandlers(tag) { "launchInViewModel()" } + .launchIn(vmScope) +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/common/uix/ViewModelLazyKeyed.kt b/app/src/main/java/eu/darken/adsbc/common/uix/ViewModelLazyKeyed.kt new file mode 100644 index 0000000..3b1ffb0 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/uix/ViewModelLazyKeyed.kt @@ -0,0 +1,174 @@ +package eu.darken.adsbc.common.uix + +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +import androidx.activity.ComponentActivity +import androidx.annotation.MainThread +import androidx.fragment.app.Fragment +import androidx.lifecycle.* +import kotlin.reflect.KClass + +/** + * Returns an existing ViewModel or creates a new one in the scope (usually, a fragment or + * an activity), associated with this `ViewModelProvider`. + * + * @see ViewModelProvider.get(Class) + */ +//@MainThread +//inline fun ViewModelProvider.get() = get(VM::class.java) + +/** + * An implementation of [Lazy] used by [androidx.fragment.app.Fragment.viewModels] and + * [androidx.activity.ComponentActivity.viewmodels]. + * + * [storeProducer] is a lambda that will be called during initialization, [VM] will be created + * in the scope of returned [ViewModelStore]. + * + * [factoryProducer] is a lambda that will be called during initialization, + * returned [ViewModelProvider.Factory] will be used for creation of [VM] + */ +class ViewModelLazyKeyed( + private val viewModelClass: KClass, + private val keyProducer: (() -> String)? = null, + private val storeProducer: () -> ViewModelStore, + private val factoryProducer: () -> ViewModelProvider.Factory +) : Lazy { + private var cached: VM? = null + + override val value: VM + get() { + val viewModel = cached + return if (viewModel == null) { + val factory = factoryProducer() + val store = storeProducer() + val key = keyProducer?.invoke() ?: "androidx.lifecycle.ViewModelProvider.DefaultKey" + ViewModelProvider(store, factory).get( + key + ":" + viewModelClass.java.canonicalName, + viewModelClass.java + ).also { + cached = it + } + } else { + viewModel + } + } + + override fun isInitialized() = cached != null +} + +/** + * Returns a property delegate to access [ViewModel] by **default** scoped to this [Fragment]: + * ``` + * class MyFragment : Fragment() { + * val viewmodel: NYViewModel by viewmodels() + * } + * ``` + * + * Custom [ViewModelProvider.Factory] can be defined via [factoryProducer] parameter, + * factory returned by it will be used to create [ViewModel]: + * ``` + * class MyFragment : Fragment() { + * val viewmodel: MYViewModel by viewmodels { myFactory } + * } + * ``` + * + * Default scope may be overridden with parameter [ownerProducer]: + * ``` + * class MyFragment : Fragment() { + * val viewmodel: MYViewModel by viewmodels ({requireParentFragment()}) + * } + * ``` + * + * This property can be accessed only after this Fragment is attached i.e., after + * [Fragment.onAttach()], and access prior to that will result in IllegalArgumentException. + */ +@MainThread +inline fun Fragment.viewModelsKeyed( + noinline keyProducer: (() -> String)? = null, + noinline ownerProducer: () -> ViewModelStoreOwner = { this }, + noinline factoryProducer: (() -> ViewModelProvider.Factory)? = null +) = createViewModelLazyKeyed(VM::class, keyProducer, { ownerProducer().viewModelStore }, factoryProducer) + +/** + * Returns a property delegate to access parent activity's [ViewModel], + * if [factoryProducer] is specified then [ViewModelProvider.Factory] + * returned by it will be used to create [ViewModel] first time. + * + * ``` + * class MyFragment : Fragment() { + * val viewmodel: MyViewModel by activityViewModels() + * } + * ``` + * + * This property can be accessed only after this Fragment is attached i.e., after + * [Fragment.onAttach()], and access prior to that will result in IllegalArgumentException. + */ +@MainThread +inline fun Fragment.activityViewModelsKeyed( + noinline keyProducer: (() -> String)? = null, + noinline factoryProducer: (() -> ViewModelProvider.Factory)? = null +) = createViewModelLazyKeyed(VM::class, keyProducer, { requireActivity().viewModelStore }, factoryProducer) + +/** + * Helper method for creation of [ViewModelLazy], that resolves `null` passed as [factoryProducer] + * to default factory. + */ +@MainThread +fun Fragment.createViewModelLazyKeyed( + viewModelClass: KClass, + keyProducer: (() -> String)? = null, + storeProducer: () -> ViewModelStore, + factoryProducer: (() -> ViewModelProvider.Factory)? = null +): Lazy { + val factoryPromise = factoryProducer ?: { + val application = activity?.application ?: throw IllegalStateException( + "ViewModel can be accessed only when Fragment is attached" + ) + ViewModelProvider.AndroidViewModelFactory.getInstance(application) + } + return ViewModelLazyKeyed(viewModelClass, keyProducer, storeProducer, factoryPromise) +} + +/** + * Returns a [Lazy] delegate to access the ComponentActivity's ViewModel, if [factoryProducer] + * is specified then [ViewModelProvider.Factory] returned by it will be used + * to create [ViewModel] first time. + * + * ``` + * class MyComponentActivity : ComponentActivity() { + * val viewmodel: MyViewModel by viewmodels() + * } + * ``` + * + * This property can be accessed only after the Activity is attached to the Application, + * and access prior to that will result in IllegalArgumentException. + */ +@MainThread +inline fun ComponentActivity.viewModelsKeyed( + noinline keyProducer: (() -> String)? = null, + noinline factoryProducer: (() -> ViewModelProvider.Factory)? = null +): Lazy { + val factoryPromise = factoryProducer ?: { + val application = application ?: throw IllegalArgumentException( + "ViewModel can be accessed only when Activity is attached" + ) + ViewModelProvider.AndroidViewModelFactory.getInstance(application) + } + + return ViewModelLazyKeyed(VM::class, keyProducer, { viewModelStore }, factoryPromise) +} diff --git a/app/src/main/java/eu/darken/adsbc/common/viewbinding/ViewBindingExtensions.kt b/app/src/main/java/eu/darken/adsbc/common/viewbinding/ViewBindingExtensions.kt new file mode 100644 index 0000000..cbf596e --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/viewbinding/ViewBindingExtensions.kt @@ -0,0 +1,94 @@ +package eu.darken.adsbc.common.viewbinding + +import android.os.Handler +import android.os.Looper +import android.view.View +import androidx.annotation.MainThread +import androidx.fragment.app.Fragment +import androidx.lifecycle.DefaultLifecycleObserver +import androidx.lifecycle.LifecycleOwner +import androidx.viewbinding.ViewBinding +import eu.darken.adsbc.common.debug.logging.Logging.Priority.VERBOSE +import eu.darken.adsbc.common.debug.logging.Logging.Priority.WARN +import eu.darken.adsbc.common.debug.logging.log +import kotlin.properties.ReadOnlyProperty +import kotlin.reflect.KProperty + +inline fun FragmentT.viewBinding() = + this.viewBinding( + bindingProvider = { + val bindingMethod = BindingT::class.java.getMethod("bind", View::class.java) + bindingMethod(null, requireView()) as BindingT + }, + lifecycleOwnerProvider = { viewLifecycleOwner } + ) + +@Suppress("unused") +fun FragmentT.viewBinding( + bindingProvider: FragmentT.() -> BindingT, + lifecycleOwnerProvider: FragmentT.() -> LifecycleOwner +) = ViewBindingProperty(bindingProvider, lifecycleOwnerProvider) + +class ViewBindingProperty( + private val bindingProvider: (ComponentT) -> BindingT, + private val lifecycleOwnerProvider: ComponentT.() -> LifecycleOwner +) : ReadOnlyProperty { + + private val uiHandler = Handler(Looper.getMainLooper()) + private var localRef: ComponentT? = null + private var viewBinding: BindingT? = null + + private val onDestroyObserver = object : DefaultLifecycleObserver { + // Called right before Fragment.onDestroyView + override fun onDestroy(owner: LifecycleOwner) { + localRef?.lifecycle?.removeObserver(this) ?: return + + localRef = null + + uiHandler.post { + log(VERBOSE) { "Resetting viewBinding" } + viewBinding = null + } + } + } + + @MainThread + override fun getValue(thisRef: ComponentT, property: KProperty<*>): BindingT { + if (localRef == null && viewBinding != null) { + log(WARN) { "Fragment.onDestroyView() was called, but the handler didn't execute our delayed reset." } + /** + * There is a fragment racecondition if you navigate to another fragment and quickly popBackStack(). + * Our uiHandler.post { } will not have executed for some reason. + * In that case we manually null the old viewBinding, to allow for clean recreation. + */ + viewBinding = null + } + + /** + * When quickly navigating, a fragment may be created that was never visible to the user. + * It's possible that [Fragment.onDestroyView] is called, but [DefaultLifecycleObserver.onDestroy] is not. + * This means the ViewBinding will is not be set to `null` and it still holds the previous layout, + * instead of the new layout that the Fragment inflated when navigating back to it. + */ + (localRef as? Fragment)?.view?.let { + if (it != viewBinding?.root && localRef === thisRef) { + log(WARN) { "Different view for the same fragment, resetting old viewBinding." } + viewBinding = null + } + } + + viewBinding?.let { + // Only accessible from within the same component + require(localRef === thisRef) + return@getValue it + } + + val lifecycle = lifecycleOwnerProvider(thisRef).lifecycle + + return bindingProvider(thisRef).also { + viewBinding = it + localRef = thisRef + lifecycle.addObserver(onDestroyObserver) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/common/worker/WorkerExtensions.kt b/app/src/main/java/eu/darken/adsbc/common/worker/WorkerExtensions.kt new file mode 100644 index 0000000..a758f49 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/common/worker/WorkerExtensions.kt @@ -0,0 +1,31 @@ +package eu.darken.adsbc.common.worker + +import android.os.Parcel +import android.os.Parcelable +import androidx.work.Data + +@Suppress("UNCHECKED_CAST") +inline fun Data.getParcelable(key: String): T? { + val parcel = Parcel.obtain() + try { + val bytes = getByteArray(key) ?: return null + parcel.unmarshall(bytes, 0, bytes.size) + parcel.setDataPosition(0) + val creator = T::class.java.getField("CREATOR").get(null) as Parcelable.Creator + return creator.createFromParcel(parcel) + } finally { + parcel.recycle() + } +} + + +fun Data.Builder.putParcelable(key: String, parcelable: Parcelable): Data.Builder { + val parcel = Parcel.obtain() + try { + parcelable.writeToParcel(parcel, 0) + putByteArray(key, parcel.marshall()) + } finally { + parcel.recycle() + } + return this +} diff --git a/app/src/main/java/eu/darken/adsbc/enterprise/core/EnterpriseSettings.kt b/app/src/main/java/eu/darken/adsbc/enterprise/core/EnterpriseSettings.kt new file mode 100644 index 0000000..39ecda1 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/enterprise/core/EnterpriseSettings.kt @@ -0,0 +1,31 @@ +package eu.darken.adsbc.enterprise.core + +import android.content.Context +import android.content.SharedPreferences +import androidx.preference.PreferenceDataStore +import dagger.hilt.android.qualifiers.ApplicationContext +import eu.darken.adsbc.common.debug.logging.logTag +import eu.darken.adsbc.common.preferences.PreferenceStoreMapper +import eu.darken.adsbc.common.preferences.Settings +import eu.darken.adsbc.common.preferences.createFlowPreference +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class EnterpriseSettings @Inject constructor( + @ApplicationContext private val context: Context +) : Settings() { + + override val preferences: SharedPreferences = + context.getSharedPreferences("settings_enterprise", Context.MODE_PRIVATE) + + val isEnterpriseMode = preferences.createFlowPreference("enterprise.enabled", false) + + override val preferenceDataStore: PreferenceDataStore = PreferenceStoreMapper( + isEnterpriseMode + ) + + companion object { + internal val TAG = logTag("Enterprise", "Settings") + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/feeder/core/ADSBxId.kt b/app/src/main/java/eu/darken/adsbc/feeder/core/ADSBxId.kt new file mode 100644 index 0000000..ea9cf36 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/feeder/core/ADSBxId.kt @@ -0,0 +1,18 @@ +package eu.darken.adsbc.feeder.core + +import android.os.Parcelable +import androidx.annotation.Keep +import com.squareup.moshi.JsonClass +import kotlinx.parcelize.Parcelize + +@Parcelize +@Keep +@JsonClass(generateAdapter = true) +data class ADSBxId(val value: String) : Parcelable { + + init { + if (value.isEmpty()) throw IllegalArgumentException("ID can't be empty or blank.") + } + + override fun toString(): String = "FeederId($value)" +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/feeder/core/Feeder.kt b/app/src/main/java/eu/darken/adsbc/feeder/core/Feeder.kt new file mode 100644 index 0000000..2313d43 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/feeder/core/Feeder.kt @@ -0,0 +1,25 @@ +package eu.darken.adsbc.feeder.core + +import eu.darken.adsbc.aircraft.core.Aircraft +import eu.darken.adsbc.feeder.core.storage.FeederData +import eu.darken.adsbc.stats.core.storage.FeederStatsData +import java.time.Instant + +data class Feeder( + val data: FeederData, + val stats: FeederStatsData, + val aircraft: Collection = emptySet(), + val lastError: Throwable? = null, +) { + val label: String + get() = data.label?.ifBlank { null } ?: adsbxId.value + + val lastSeenAt: Instant + get() = data.seenAt + + val uid: FeederId + get() = data.uid + + val adsbxId: ADSBxId + get() = data.adsbxId +} diff --git a/app/src/main/java/eu/darken/adsbc/feeder/core/FeederId.kt b/app/src/main/java/eu/darken/adsbc/feeder/core/FeederId.kt new file mode 100644 index 0000000..37b11d7 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/feeder/core/FeederId.kt @@ -0,0 +1,10 @@ +package eu.darken.adsbc.feeder.core + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize +import java.util.* + +@Parcelize +data class FeederId( + val value: String = UUID.randomUUID().toString() +) : Parcelable \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/feeder/core/FeederRepo.kt b/app/src/main/java/eu/darken/adsbc/feeder/core/FeederRepo.kt new file mode 100644 index 0000000..deb70a2 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/feeder/core/FeederRepo.kt @@ -0,0 +1,138 @@ +package eu.darken.adsbc.feeder.core + +import eu.darken.adsbc.aircraft.core.Aircraft +import eu.darken.adsbc.aircraft.core.AircraftStorage +import eu.darken.adsbc.common.collections.mutate +import eu.darken.adsbc.common.coroutine.AppScope +import eu.darken.adsbc.common.coroutine.DispatcherProvider +import eu.darken.adsbc.common.debug.logging.Logging.Priority.VERBOSE +import eu.darken.adsbc.common.debug.logging.log +import eu.darken.adsbc.common.debug.logging.logTag +import eu.darken.adsbc.common.flow.DynamicStateFlow +import eu.darken.adsbc.common.flow.replayingShare +import eu.darken.adsbc.feeder.core.storage.FeederData +import eu.darken.adsbc.feeder.core.storage.FeederStorage +import eu.darken.adsbc.stats.core.api.ADSBxStatsApi +import eu.darken.adsbc.stats.core.storage.FeederStatsData +import eu.darken.adsbc.stats.core.storage.StatsStorage +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.* +import kotlinx.coroutines.plus +import java.time.Instant +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class FeederRepo @Inject constructor( + @AppScope private val appScope: CoroutineScope, + private val dispatcherProvider: DispatcherProvider, + private val feederStorage: FeederStorage, + private val statsStorage: StatsStorage, + private val aircraftStorage: AircraftStorage, +) { + private val feederErrors = DynamicStateFlow>( + loggingTag = TAG, + parentScope = appScope + dispatcherProvider.IO + ) { emptyMap() } + + val allFeeders: Flow> = combine( + feederStorage.allFeeders, + feederErrors.flow, + aircraftStorage.allAircraft, + statsStorage.allFeederStats + ) { feeders, errorMap, aircraftMap, feederStats -> + + feeders.map { feederData -> + val feederId = feederData.uid + Feeder( + data = feederData, + stats = feederStats.singleOrNull { it.uid == feederId } ?: FeederStatsData(uid = feederId), + aircraft = aircraftMap[feederId] ?: emptySet(), + lastError = errorMap[feederId] + ) + } + }.replayingShare(appScope) + + val mergedAircrafts: Flow> = allFeeders.map { feeders -> + val aircraft = feeders.map { it.aircraft }.flatten().distinctBy { it.hexCode } + + aircraft.map { ac -> + SpottedAircraft( + aircraft = ac, + feeders = feeders + .filter { feeder -> + feeder.aircraft.any { it.hexCode == ac.hexCode } + } + .map { feeder -> + val specificAircraft = feeders + .single { it.uid == feeder.uid } + .aircraft.first { it.hexCode == ac.hexCode } + feeder to specificAircraft + } + ) + } + }.replayingShare(appScope) + + suspend fun update(feederId: FeederId, block: (FeederData) -> FeederData) { + log(TAG) { "update(feederId=$feederId, block=$block)" } + feederStorage.update(feederId, block) + } + + suspend fun updateAircraft(feederId: FeederId, aircraft: Collection) { + log(TAG) { "updateStats(feederId=$feederId, aircraft=${aircraft.size})" } + val start = System.currentTimeMillis() + + aircraftStorage.updateData(feederId, aircraft) + + feederStorage.update(feederId) { it.copy(seenAt = Instant.now()) } + + val duration = (System.currentTimeMillis() - start) + log(TAG, VERBOSE) { "Aircraft update took $duration ms" } + } + + suspend fun updateStats(feederId: FeederId, stats: ADSBxStatsApi.FeederInfo) { + log(TAG) { "updateStats(feederId=$feederId, stats=$stats)" } + val start = System.currentTimeMillis() + + statsStorage.update(feederId) { + it.copy( + statsUpdatedAt = Instant.now(), + aircraftCountLogged = stats.aircraftCountLogged + ) + } + feederStorage.update(feederId) { it.copy(seenAt = Instant.now()) } + + val duration = (System.currentTimeMillis() - start) + log(TAG, VERBOSE) { "Stats update took $duration ms" } + } + + suspend fun updateError(feederId: FeederId, e: Throwable) { + log(TAG) { "updateError(id=$feederId, error=$e)" } + feederErrors.updateBlocking { + mutate { this[feederId] = e } + } + } + + suspend fun delete(feederId: FeederId) { + log(TAG) { "delete(id=$feederId)" } + feederStorage.delete(feederId) + statsStorage.delete(feederId) + aircraftStorage.deleteByFeederId(feederId) + } + + suspend fun createFeeder(request: FeederSetupRequest): Feeder { + log(TAG) { "createFeeder(request=$request)" } + val newData = feederStorage.createFeeder(request) + statsStorage.create(newData.uid) + + return allFeeders + .map { fs -> fs.firstOrNull { it.uid == newData.uid } } + .filterNotNull() + .first() + .also { log(TAG) { "Feeder created: $it" } } + } + + companion object { + private val TAG = logTag("Feeder", "Repo") + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/feeder/core/FeederRepoExtensions.kt b/app/src/main/java/eu/darken/adsbc/feeder/core/FeederRepoExtensions.kt new file mode 100644 index 0000000..30a77a2 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/feeder/core/FeederRepoExtensions.kt @@ -0,0 +1,8 @@ +package eu.darken.adsbc.feeder.core + +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map + +fun FeederRepo.feeder(id: FeederId): Flow = allFeeders.map { fs -> + fs.singleOrNull { it.uid == id } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/feeder/core/FeederSettings.kt b/app/src/main/java/eu/darken/adsbc/feeder/core/FeederSettings.kt new file mode 100644 index 0000000..4a0ab9c --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/feeder/core/FeederSettings.kt @@ -0,0 +1,27 @@ +package eu.darken.adsbc.feeder.core + +import android.content.Context +import android.content.SharedPreferences +import androidx.preference.PreferenceDataStore +import dagger.hilt.android.qualifiers.ApplicationContext +import eu.darken.adsbc.common.debug.logging.logTag +import eu.darken.adsbc.common.preferences.PreferenceStoreMapper +import eu.darken.adsbc.common.preferences.Settings +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class FeederSettings @Inject constructor( + @ApplicationContext private val context: Context +) : Settings() { + + override val preferences: SharedPreferences = context.getSharedPreferences("settings_feeder", Context.MODE_PRIVATE) + + override val preferenceDataStore: PreferenceDataStore = PreferenceStoreMapper( + + ) + + companion object { + internal val TAG = logTag("Feeder", "Settings") + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/feeder/core/FeederSetupRequest.kt b/app/src/main/java/eu/darken/adsbc/feeder/core/FeederSetupRequest.kt new file mode 100644 index 0000000..7e14896 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/feeder/core/FeederSetupRequest.kt @@ -0,0 +1,6 @@ +package eu.darken.adsbc.feeder.core + +data class FeederSetupRequest( + val adsbxId: ADSBxId, + val label: String? = null +) \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/feeder/core/FeederUpdateService.kt b/app/src/main/java/eu/darken/adsbc/feeder/core/FeederUpdateService.kt new file mode 100644 index 0000000..073bcce --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/feeder/core/FeederUpdateService.kt @@ -0,0 +1,83 @@ +package eu.darken.adsbc.feeder.core + +import eu.darken.adsbc.aircraft.core.api.ADSBxAircraftEndpoint +import eu.darken.adsbc.common.debug.logging.Logging.Priority.* +import eu.darken.adsbc.common.debug.logging.asLog +import eu.darken.adsbc.common.debug.logging.log +import eu.darken.adsbc.common.debug.logging.logTag +import eu.darken.adsbc.main.core.GeneralSettings +import eu.darken.adsbc.stats.core.api.ADSBxStatsEndpoint +import kotlinx.coroutines.currentCoroutineContext +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.* +import kotlinx.coroutines.isActive +import java.time.Duration +import java.time.Instant +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class FeederUpdateService @Inject constructor( + private val aircraftEndpointAdsbx: ADSBxAircraftEndpoint, + private val statsEndpointAdsbx: ADSBxStatsEndpoint, + private val generalSettings: GeneralSettings, + private val feederRepo: FeederRepo, +) { + + val autoUpdates = feederRepo.allFeeders + .distinctUntilChangedBy { feeders -> + feeders.map { it.adsbxId } + } + .flatMapLatest { ids -> + generalSettings.updateInterval.flow.map { it to ids } + } + .flatMapLatest { (updateInterval, feeders) -> + flow { + while (currentCoroutineContext().isActive) { + val updates = feeders.map { + try { + updateFeeder(it.uid, updateInterval) + } catch (e: Exception) { + log(TAG, WARN) { "Auto update failed for $it" } + } + } + emit(updates) + delay(updateInterval) + } + } + } + + + suspend fun updateFeeder(feederId: FeederId, updateInterval: Long = 0) = try { + log(TAG) { "updateFeeder(feederId=$feederId)" } + val updateStart = System.currentTimeMillis() + + val feeder = feederRepo.feeder(feederId).firstOrNull() + ?: throw IllegalArgumentException("Couldn't find $feederId") + + kotlin.run { + val aircraft = aircraftEndpointAdsbx.getAircraft(feeder.adsbxId) + feederRepo.updateAircraft(feederId, aircraft) + } + + kotlin.run { + val timeSinceLastStats = Duration.between(feeder.stats.statsUpdatedAt, Instant.now()) + + if (timeSinceLastStats.seconds < updateInterval * 6) return@run + + feederRepo.updateStats(feederId, statsEndpointAdsbx.getFeederStats(feeder.adsbxId)) + } + + val updateDuration = System.currentTimeMillis() - updateStart + log(TAG, VERBOSE) { "Update took ${updateDuration}ms for $feederId" } + Unit + } catch (e: Exception) { + log(TAG, ERROR) { "Failed to update feeder info for feederId: ${e.asLog()}" } + feederRepo.updateError(feederId, e) + throw e + } + + companion object { + private val TAG = logTag("Feeder", "UpdateService") + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/feeder/core/SpottedAircraft.kt b/app/src/main/java/eu/darken/adsbc/feeder/core/SpottedAircraft.kt new file mode 100644 index 0000000..1aa73be --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/feeder/core/SpottedAircraft.kt @@ -0,0 +1,8 @@ +package eu.darken.adsbc.feeder.core + +import eu.darken.adsbc.aircraft.core.Aircraft + +data class SpottedAircraft( + val aircraft: Aircraft, + val feeders: Collection> +) diff --git a/app/src/main/java/eu/darken/adsbc/feeder/core/storage/FeederDao.kt b/app/src/main/java/eu/darken/adsbc/feeder/core/storage/FeederDao.kt new file mode 100644 index 0000000..57a68c3 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/feeder/core/storage/FeederDao.kt @@ -0,0 +1,31 @@ +package eu.darken.adsbc.feeder.core.storage + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.Query +import androidx.room.Update +import eu.darken.adsbc.feeder.core.ADSBxId +import eu.darken.adsbc.feeder.core.FeederId +import kotlinx.coroutines.flow.Flow + +@Dao +interface FeederDao { + + @Query("SELECT * FROM feeders") + fun allFeeders(): Flow> + + @Query("SELECT * FROM feeders WHERE adsbxId IN (:ids)") + fun getFeederByAdsbxId(vararg ids: ADSBxId): FeederData? + + @Query("SELECT * FROM feeders WHERE uid IN (:ids)") + fun getFeederById(vararg ids: FeederId): FeederData? + + @Insert + suspend fun insertFeeder(feeder: FeederData) + + @Update(entity = FeederData::class) + suspend fun updateFeeder(feeder: FeederData) + + @Query("DELETE FROM feeders WHERE uid = :id") + suspend fun deleteFeeder(id: FeederId) +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/feeder/core/storage/FeederData.kt b/app/src/main/java/eu/darken/adsbc/feeder/core/storage/FeederData.kt new file mode 100644 index 0000000..8275c47 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/feeder/core/storage/FeederData.kt @@ -0,0 +1,20 @@ +package eu.darken.adsbc.feeder.core.storage + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import eu.darken.adsbc.feeder.core.ADSBxId +import eu.darken.adsbc.feeder.core.FeederId +import java.time.Instant + +@Entity(tableName = "feeders") +data class FeederData( + @PrimaryKey val uid: FeederId = FeederId(), + @ColumnInfo(name = "addedAt") val addedAt: Instant = Instant.now(), + @ColumnInfo(name = "label") val label: String? = null, + @ColumnInfo(name = "comment") val comment: String? = null, + + @ColumnInfo(name = "adsbxId") val adsbxId: ADSBxId, + + @ColumnInfo(name = "seenAt") val seenAt: Instant = Instant.MIN, +) \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/feeder/core/storage/FeederDatabase.kt b/app/src/main/java/eu/darken/adsbc/feeder/core/storage/FeederDatabase.kt new file mode 100644 index 0000000..aa52ef9 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/feeder/core/storage/FeederDatabase.kt @@ -0,0 +1,27 @@ +package eu.darken.adsbc.feeder.core.storage + +import android.content.Context +import androidx.room.Database +import androidx.room.Room +import androidx.room.RoomDatabase +import androidx.room.TypeConverters +import dagger.hilt.android.qualifiers.ApplicationContext +import eu.darken.adsbc.common.room.CommonConverters +import javax.inject.Inject + + +@Database( + entities = [FeederData::class], + version = 1 +) +@TypeConverters(CommonConverters::class) +abstract class FeederDatabase : RoomDatabase() { + + abstract fun feederDao(): FeederDao + + class Factory @Inject constructor(@ApplicationContext private val context: Context) { + fun create(): FeederDatabase = Room + .databaseBuilder(context, FeederDatabase::class.java, "feeder.db") + .build() + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/feeder/core/storage/FeederStorage.kt b/app/src/main/java/eu/darken/adsbc/feeder/core/storage/FeederStorage.kt new file mode 100644 index 0000000..3200d9a --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/feeder/core/storage/FeederStorage.kt @@ -0,0 +1,57 @@ +package eu.darken.adsbc.feeder.core.storage + +import androidx.room.withTransaction +import eu.darken.adsbc.common.debug.logging.log +import eu.darken.adsbc.common.debug.logging.logTag +import eu.darken.adsbc.feeder.core.FeederId +import eu.darken.adsbc.feeder.core.FeederSetupRequest +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class FeederStorage @Inject constructor( + private val feederDatabaseFactory: FeederDatabase.Factory, +) { + + private val db by lazy { feederDatabaseFactory.create() } + private val feederDao by lazy { db.feederDao() } + + val allFeeders = feederDao.allFeeders() + + suspend fun createFeeder(request: FeederSetupRequest): FeederData { + log(TAG) { "createFeeder(request=$request)" } + + return db.withTransaction { + val existingFeeder = feederDao.getFeederByAdsbxId(request.adsbxId) + if (existingFeeder != null) throw IllegalStateException("${request.adsbxId} already exists") + + val entity = FeederData( + adsbxId = request.adsbxId, + label = request.label?.ifBlank { null } + ) + feederDao.insertFeeder(entity) + + feederDao.getFeederByAdsbxId(request.adsbxId)!! + } + } + + suspend fun delete(feederId: FeederId) { + log(TAG) { "delete(feederId=$feederId)" } + feederDao.deleteFeeder(feederId) + } + + suspend fun update(id: FeederId, block: (FeederData) -> FeederData): FeederData { + log(TAG) { "updateInfo(id=$id, block=$block)" } + return db.withTransaction { + val oldFeeder = feederDao.getFeederById(id) ?: throw IllegalArgumentException("Can't find $id") + val updatedFeeder = block(oldFeeder) + log(TAG) { "Updated feeder $id\nBefore:$oldFeeder\nNow: $oldFeeder" } + feederDao.updateFeeder(updatedFeeder) + updatedFeeder + } + } + + companion object { + private val TAG = logTag("Feeder", "Storage") + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/feeder/ui/list/FeederAdapter.kt b/app/src/main/java/eu/darken/adsbc/feeder/ui/list/FeederAdapter.kt new file mode 100644 index 0000000..0094e61 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/feeder/ui/list/FeederAdapter.kt @@ -0,0 +1,75 @@ +package eu.darken.adsbc.feeder.ui.list + +import android.text.format.DateUtils +import android.view.ViewGroup +import eu.darken.adsbc.R +import eu.darken.adsbc.common.lists.BindableVH +import eu.darken.adsbc.common.lists.binding +import eu.darken.adsbc.common.lists.differ.DifferItem +import eu.darken.adsbc.common.lists.differ.setupDiffer +import eu.darken.adsbc.common.lists.modular.mods.DataBinderMod +import eu.darken.adsbc.databinding.FeederListLineBinding +import eu.darken.adsbc.feeder.core.Feeder +import eu.darken.bb.common.lists.differ.AsyncDiffer +import eu.darken.bb.common.lists.differ.HasAsyncDiffer +import eu.darken.bb.common.lists.modular.ModularAdapter +import eu.darken.bb.common.lists.modular.mods.SimpleVHCreatorMod +import java.time.Instant +import javax.inject.Inject + +class FeederAdapter @Inject constructor() : ModularAdapter(), + HasAsyncDiffer { + + override val asyncDiffer: AsyncDiffer<*, Item> = setupDiffer() + + override fun getItemCount(): Int = data.size + + init { + modules.add(DataBinderMod(data)) + modules.add(SimpleVHCreatorMod { FeederVH(it) }) + } + + data class Item( + val feeder: Feeder, + val onClickAction: (Feeder) -> Unit + ) : DifferItem { + override val payloadProvider: ((DifferItem, DifferItem) -> DifferItem?) = { old, new -> + if (new::class.isInstance(old)) new else null + } + + override val stableId: Long = feeder.adsbxId.hashCode().toLong() + } + + class FeederVH(parent: ViewGroup) : ModularAdapter.VH(R.layout.feeder_list_line, parent), + BindableVH { + + override val viewBinding: Lazy = lazy { FeederListLineBinding.bind(itemView) } + + override val onBindData: FeederListLineBinding.( + item: Item, + payloads: List + ) -> Unit = binding { item -> + val feeder = item.feeder + label.text = feeder.label + + lastSeen.text = if (feeder.lastSeenAt == Instant.MIN) { + getString(R.string.last_seen_not_seen_label) + } else { + val timeAgo = DateUtils.getRelativeTimeSpanString( + feeder.lastSeenAt.toEpochMilli(), + Instant.now().toEpochMilli(), + DateUtils.FORMAT_ABBREV_RELATIVE.toLong() + ) + "Seen $timeAgo" + } + + planesInfoLast.text = if (feeder.lastError != null) { + feeder.lastError.toString() + } else { + "Tracking ${feeder.aircraft.size} aircrafts. Aircraft count logged ${feeder.stats.aircraftCountLogged} times." + } + + itemView.setOnClickListener { item.onClickAction(feeder) } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/feeder/ui/list/FeederListFragment.kt b/app/src/main/java/eu/darken/adsbc/feeder/ui/list/FeederListFragment.kt new file mode 100644 index 0000000..356d627 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/feeder/ui/list/FeederListFragment.kt @@ -0,0 +1,64 @@ +package eu.darken.adsbc.feeder.ui.list + +import android.os.Bundle +import android.view.View +import androidx.fragment.app.viewModels +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import dagger.hilt.android.AndroidEntryPoint +import eu.darken.adsbc.R +import eu.darken.adsbc.common.WebpageTool +import eu.darken.adsbc.common.debug.logging.log +import eu.darken.adsbc.common.debug.logging.logTag +import eu.darken.adsbc.common.lists.differ.update +import eu.darken.adsbc.common.uix.Fragment3 +import eu.darken.adsbc.common.viewbinding.viewBinding +import eu.darken.adsbc.databinding.FeederListFragmentBinding +import eu.darken.adsbc.databinding.FeederListInputDialogBinding +import eu.darken.bb.common.lists.setupDefaults +import javax.inject.Inject + + +@AndroidEntryPoint +class FeederListFragment : Fragment3(R.layout.feeder_list_fragment) { + + override val vm: FeederListFragmentVM by viewModels() + override val ui: FeederListFragmentBinding by viewBinding() + + @Inject lateinit var webpageTool: WebpageTool + @Inject lateinit var feederAdapter: FeederAdapter + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + ui.list.setupDefaults(feederAdapter) + + ui.fab.setOnClickListener { + log(TAG) { "Launching add feeder dialog" } + + MaterialAlertDialogBuilder(requireContext()).apply { + val layout = FeederListInputDialogBinding.inflate(layoutInflater) + + setView(layout.root) + setTitle(R.string.add_feeder_title) + setMessage(R.string.add_feeder_description) + setPositiveButton(R.string.general_add_action) { _, _ -> + vm.addFeeder( + label = layout.feederLabelInputText.text.toString(), + adsbxId = layout.adsbxIdInputText.text.toString() + ) + } + setNegativeButton(R.string.general_cancel_action) { _, _ -> } + setNeutralButton(R.string.general_help_info) { _, _ -> + webpageTool.open("https://github.com/adsbxchange/adsbexchange-stats") + } + }.show() + } + + vm.feeders.observe2(ui) { + feederAdapter.update(it) + } + super.onViewCreated(view, savedInstanceState) + } + + companion object { + private val TAG = logTag("Feeder", "List", "Fragment") + } +} diff --git a/app/src/main/java/eu/darken/adsbc/feeder/ui/list/FeederListFragmentVM.kt b/app/src/main/java/eu/darken/adsbc/feeder/ui/list/FeederListFragmentVM.kt new file mode 100644 index 0000000..5e26c97 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/feeder/ui/list/FeederListFragmentVM.kt @@ -0,0 +1,64 @@ +package eu.darken.adsbc.feeder.ui.list + +import androidx.lifecycle.SavedStateHandle +import dagger.hilt.android.lifecycle.HiltViewModel +import eu.darken.adsbc.common.coroutine.DispatcherProvider +import eu.darken.adsbc.common.debug.logging.Logging.Priority.ERROR +import eu.darken.adsbc.common.debug.logging.log +import eu.darken.adsbc.common.debug.logging.logTag +import eu.darken.adsbc.common.navigation.navVia +import eu.darken.adsbc.common.uix.ViewModel3 +import eu.darken.adsbc.feeder.core.* +import eu.darken.adsbc.main.ui.main.MainFragmentDirections +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.isActive +import javax.inject.Inject +import kotlin.coroutines.coroutineContext + +@HiltViewModel +class FeederListFragmentVM @Inject constructor( + handle: SavedStateHandle, + dispatcherProvider: DispatcherProvider, + private val feederRepo: FeederRepo, + private val feederUpdateService: FeederUpdateService, +) : ViewModel3(dispatcherProvider = dispatcherProvider) { + + private val ticker = flow { + while (coroutineContext.isActive) { + emit(Unit) + delay(1000) + } + } + val feeders = combine( + feederRepo.allFeeders, + ticker + ) { feeders, _ -> + feeders.map { feeder -> + FeederAdapter.Item(feeder = feeder) { editFeeder(it.uid) } + } + }.asLiveData2() + + fun addFeeder(label: String, adsbxId: String) = launch { + log(TAG) { "addFeeder(label=$label, adsbxId=$adsbxId)" } + val request = FeederSetupRequest( + label = label, + adsbxId = ADSBxId(adsbxId) + ) + val createdFeeder = feederRepo.createFeeder(request) + try { + feederUpdateService.updateFeeder(createdFeeder.uid) + } catch (e: Exception) { + log(TAG, ERROR) { "Failed to do initial feeder update." } + } + } + + fun editFeeder(id: FeederId) { + MainFragmentDirections.actionMainFragmentToFeederActionDialog(id).navVia(this) + } + + companion object { + private val TAG = logTag("Feeder", "List", "VM") + } +} diff --git a/app/src/main/java/eu/darken/adsbc/feeder/ui/list/actions/ActionsAdapter.kt b/app/src/main/java/eu/darken/adsbc/feeder/ui/list/actions/ActionsAdapter.kt new file mode 100644 index 0000000..532370e --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/feeder/ui/list/actions/ActionsAdapter.kt @@ -0,0 +1,32 @@ +package eu.darken.adsbc.feeder.ui.list.actions + +import android.view.ViewGroup +import eu.darken.adsbc.common.lists.differ.setupDiffer +import eu.darken.adsbc.common.lists.modular.mods.DataBinderMod +import eu.darken.adsbc.common.ui.Confirmable +import eu.darken.adsbc.common.ui.ConfirmableActionAdapterVH +import eu.darken.bb.common.lists.differ.AsyncDiffer +import eu.darken.bb.common.lists.differ.HasAsyncDiffer +import eu.darken.bb.common.lists.modular.ModularAdapter +import eu.darken.bb.common.lists.modular.mods.SimpleVHCreatorMod +import javax.inject.Inject + +class ActionsAdapter @Inject constructor() : + ModularAdapter(), + HasAsyncDiffer> { + + override val asyncDiffer: AsyncDiffer> = setupDiffer() + + override fun getItemCount(): Int = data.size + + init { + modules.add(DataBinderMod(data)) + modules.add(SimpleVHCreatorMod { VH(it) }) + } + + class VH(parent: ViewGroup) : ConfirmableActionAdapterVH(parent) { + override fun getIcon(item: FeederAction): Int = item.iconRes + + override fun getLabel(item: FeederAction): String = getString(item.labelRes) + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/feeder/ui/list/actions/FeederAction.kt b/app/src/main/java/eu/darken/adsbc/feeder/ui/list/actions/FeederAction.kt new file mode 100644 index 0000000..a66f6c5 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/feeder/ui/list/actions/FeederAction.kt @@ -0,0 +1,15 @@ +package eu.darken.adsbc.feeder.ui.list.actions + +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes +import eu.darken.adsbc.R +import eu.darken.adsbc.common.DialogActionEnum + +enum class FeederAction constructor( + @DrawableRes val iconRes: Int, + @StringRes val labelRes: Int +) : DialogActionEnum { + RENAME(R.drawable.ic_baseline_drive_file_rename_outline_24, R.string.feeder_rename_action), + REFRESH(R.drawable.ic_baseline_refresh_24, R.string.feeder_refresh_action), + DELETE(R.drawable.ic_baseline_delete_sweep_24, R.string.feeder_delete_action) +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/feeder/ui/list/actions/FeederActionDialog.kt b/app/src/main/java/eu/darken/adsbc/feeder/ui/list/actions/FeederActionDialog.kt new file mode 100644 index 0000000..782a493 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/feeder/ui/list/actions/FeederActionDialog.kt @@ -0,0 +1,61 @@ +package eu.darken.adsbc.feeder.ui.list.actions + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.EditText +import androidx.fragment.app.viewModels +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import dagger.hilt.android.AndroidEntryPoint +import eu.darken.adsbc.R +import eu.darken.adsbc.common.lists.differ.update +import eu.darken.adsbc.common.uix.BottomSheetDialogFragment2 +import eu.darken.adsbc.databinding.FeederListActionDialogBinding +import eu.darken.bb.common.lists.setupDefaults +import javax.inject.Inject + +@AndroidEntryPoint +class FeederActionDialog : BottomSheetDialogFragment2() { + override val vm: FeederActionDialogVM by viewModels() + override lateinit var ui: FeederListActionDialogBinding + @Inject lateinit var actionsAdapter: ActionsAdapter + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + ui = FeederListActionDialogBinding.inflate(inflater, container, false) + return ui.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + ui.recyclerview.setupDefaults(actionsAdapter) + + vm.state.observe2(ui) { state -> + feederName.text = state.label + + actionsAdapter.update(state.allowedActions) + + ui.recyclerview.visibility = if (state.loading) View.INVISIBLE else View.VISIBLE + ui.progressCircular.visibility = if (state.loading) View.VISIBLE else View.INVISIBLE + if (state.finished) dismissAllowingStateLoss() + } + + vm.renameFeederEvent.observe2(ui) { feederId -> + MaterialAlertDialogBuilder(requireContext()).apply { + val alertLayout = layoutInflater.inflate(R.layout.feeder_list_rename_dialog, null) + val input: EditText = alertLayout.findViewById(R.id.input_text) + setView(alertLayout) + setTitle(R.string.rename_feeder_title) + setMessage(R.string.rename_feeder_description) + setPositiveButton(R.string.feeder_rename_action) { _, _ -> + vm.renameFeeder(feederId, input.text.toString()) + } + setNegativeButton(R.string.general_cancel_action) { _, _ -> } + setNeutralButton(R.string.general_reset_action) { _, _ -> + vm.renameFeeder(feederId, null) + } + }.show() + } + + super.onViewCreated(view, savedInstanceState) + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/feeder/ui/list/actions/FeederActionDialogVM.kt b/app/src/main/java/eu/darken/adsbc/feeder/ui/list/actions/FeederActionDialogVM.kt new file mode 100644 index 0000000..1859e9a --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/feeder/ui/list/actions/FeederActionDialogVM.kt @@ -0,0 +1,104 @@ +package eu.darken.adsbc.feeder.ui.list.actions + +import androidx.lifecycle.SavedStateHandle +import dagger.hilt.android.lifecycle.HiltViewModel +import eu.darken.adsbc.common.coroutine.DispatcherProvider +import eu.darken.adsbc.common.debug.logging.log +import eu.darken.adsbc.common.debug.logging.logTag +import eu.darken.adsbc.common.flow.DynamicStateFlow +import eu.darken.adsbc.common.livedata.SingleLiveEvent +import eu.darken.adsbc.common.navigation.navArgs +import eu.darken.adsbc.common.ui.Confirmable +import eu.darken.adsbc.common.uix.ViewModel3 +import eu.darken.adsbc.feeder.core.FeederId +import eu.darken.adsbc.feeder.core.FeederRepo +import eu.darken.adsbc.feeder.core.FeederUpdateService +import eu.darken.adsbc.feeder.core.feeder +import eu.darken.adsbc.feeder.ui.list.actions.FeederAction.* +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.first +import javax.inject.Inject + +@HiltViewModel +class FeederActionDialogVM @Inject constructor( + handle: SavedStateHandle, + dispatcherProvider: DispatcherProvider, + private val feederRepo: FeederRepo, + private val feederUpdateService: FeederUpdateService, +) : ViewModel3(dispatcherProvider) { + + private val navArgs by handle.navArgs() + private val feederId: FeederId = navArgs.feederId + private val stateUpdater = DynamicStateFlow(TAG, vmScope) { State(loading = true) } + + val renameFeederEvent = SingleLiveEvent() + + val state = stateUpdater.asLiveData2() + + init { + launch { + val feeder = feederRepo.feeder(feederId).first() + + val actions = listOf( + Confirmable(REFRESH) { feederAction(it) }, + Confirmable(RENAME) { feederAction(it) }, + Confirmable(DELETE, requiredLvl = 1) { feederAction(it) }, + ) + + stateUpdater.updateBlocking { + if (feeder == null) { + copy(loading = true, finished = true) + } else { + copy( + label = feeder.label, + loading = false, + allowedActions = actions + ) + } + } + } + } + + private fun feederAction(action: FeederAction) = launch { + stateUpdater.updateBlocking { copy(loading = true) } + + when (action) { + RENAME -> { + delay(200) + renameFeederEvent.postValue(feederId) + } + REFRESH -> { + try { + feederUpdateService.updateFeeder(feederId) + } finally { + stateUpdater.updateBlocking { copy(loading = false, finished = true) } + } + } + DELETE -> { + delay(200) + feederRepo.delete(feederId) + stateUpdater.updateBlocking { copy(loading = false, finished = true) } + } + } + } + + fun renameFeeder(id: FeederId, label: String?) = launch { + log(TAG) { "renameFeeder(id=$id, label=$label)" } + + feederRepo.update(id) { it.copy(label = label) } + stateUpdater.updateBlocking { copy(loading = false, finished = true) } + } + + data class State( + val loading: Boolean = false, + val finished: Boolean = false, + val label: String = "", + val allowedActions: List> = listOf() + ) + + companion object { + private val TAG = logTag("Feeder", "ActionDialog", "VDC") + } + +} + diff --git a/app/src/main/java/eu/darken/adsbc/main/App.kt b/app/src/main/java/eu/darken/adsbc/main/App.kt new file mode 100644 index 0000000..75abec7 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/main/App.kt @@ -0,0 +1,41 @@ +package eu.darken.adsbc.main + +import android.app.Application +import dagger.hilt.android.HiltAndroidApp +import eu.darken.adsbc.BuildConfig +import eu.darken.adsbc.common.coroutine.AppScope +import eu.darken.adsbc.common.debug.autoreport.AutoReporting +import eu.darken.adsbc.common.debug.logging.* +import eu.darken.adsbc.feeder.core.FeederUpdateService +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.launchIn +import javax.inject.Inject + +@HiltAndroidApp +class App : Application() { + + @Inject lateinit var bugReporter: AutoReporting + @Inject lateinit var feederUpdateService: FeederUpdateService + @AppScope @Inject lateinit var appScope: CoroutineScope + + override fun onCreate() { + super.onCreate() + if (BuildConfig.DEBUG) Logging.install(LogCatLogger()) + +// ReLinker +// .log { message -> log(TAG) { "ReLinker: $message" } } +// .loadLibrary(this, "bugsnag-plugin-android-anr") + + bugReporter.setup() + + feederUpdateService + .autoUpdates + .launchIn(appScope) + + log(TAG) { "onCreate() done! ${Exception().asLog()}" } + } + + companion object { + internal val TAG = logTag("ADSBC") + } +} diff --git a/app/src/main/java/eu/darken/adsbc/main/core/GeneralSettings.kt b/app/src/main/java/eu/darken/adsbc/main/core/GeneralSettings.kt new file mode 100644 index 0000000..02a852a --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/main/core/GeneralSettings.kt @@ -0,0 +1,37 @@ +package eu.darken.adsbc.main.core + +import android.content.Context +import android.content.SharedPreferences +import androidx.preference.PreferenceDataStore +import dagger.hilt.android.qualifiers.ApplicationContext +import eu.darken.adsbc.common.debug.autoreport.DebugSettings +import eu.darken.adsbc.common.debug.logging.logTag +import eu.darken.adsbc.common.preferences.PreferenceStoreMapper +import eu.darken.adsbc.common.preferences.Settings +import eu.darken.adsbc.common.preferences.createFlowPreference +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class GeneralSettings @Inject constructor( + @ApplicationContext private val context: Context, + private val debugSettings: DebugSettings, +) : Settings() { + + override val preferences: SharedPreferences = context.getSharedPreferences("settings_core", Context.MODE_PRIVATE) + + val isBugTrackingEnabled = preferences.createFlowPreference("core.bugtracking.enabled", true) + + val updateInterval = preferences.createFlowPreference("core.update.interval", 10000L) + + override val preferenceDataStore: PreferenceDataStore = PreferenceStoreMapper( + isBugTrackingEnabled, + updateInterval, + debugSettings.isAutoReportingEnabled, + debugSettings.isDebugModeEnabled + ) + + companion object { + internal val TAG = logTag("General", "Settings") + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/main/core/SomeRepo.kt b/app/src/main/java/eu/darken/adsbc/main/core/SomeRepo.kt new file mode 100644 index 0000000..cca8399 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/main/core/SomeRepo.kt @@ -0,0 +1,53 @@ +package eu.darken.adsbc.main.core + +import eu.darken.adsbc.common.coroutine.AppScope +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.currentCoroutineContext +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.shareIn +import kotlinx.coroutines.isActive +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class SomeRepo @Inject constructor( + @AppScope private val appCoroutineScope: CoroutineScope, +) { + + val countsWhileSubscribed: Flow = flow { + var sub = 0L + while (currentCoroutineContext().isActive) { + val toEmit = sub++ +// log { "Emitting (sub) $toEmit" } + emit(toEmit) + delay(1_000) + } + } + + val countsAlways: Flow = flow { + var counter = 0L + while (currentCoroutineContext().isActive) { + val toEmit = counter++ +// log { "Emitting (perm) $toEmit" } + emit(toEmit) + delay(1_000) + } + }.shareIn( + scope = appCoroutineScope, + started = SharingStarted.Lazily, + replay = 1 + ) + + val emojis: Flow = flow { + val emoji = EMOJIS[(Math.random() * EMOJIS.size).toInt()] +// log { "Emitting $emoji" } + emit(emoji) + } + + companion object { + internal val EMOJIS = listOf("\uD83D\uDE00", "\uD83D\uDE02", "\uD83E\uDD17", "\uD83D\uDE32") + } +} diff --git a/app/src/main/java/eu/darken/adsbc/main/core/receiver/ExampleReceiver.kt b/app/src/main/java/eu/darken/adsbc/main/core/receiver/ExampleReceiver.kt new file mode 100644 index 0000000..14dca1e --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/main/core/receiver/ExampleReceiver.kt @@ -0,0 +1,24 @@ +package eu.darken.adsbc.main.core.receiver + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import dagger.hilt.android.AndroidEntryPoint +import eu.darken.adsbc.common.debug.logging.Logging.Priority.WARN +import eu.darken.adsbc.common.debug.logging.log +import eu.darken.adsbc.main.core.SomeRepo +import javax.inject.Inject + +@AndroidEntryPoint +class ExampleReceiver : BroadcastReceiver() { + + @Inject lateinit var someRepo: SomeRepo + + override fun onReceive(context: Context, intent: Intent) { + log { "onReceive($context, $intent)" } + if (intent.action != Intent.ACTION_HEADSET_PLUG) { + log(WARN) { "Unknown action: $intent.action" } + return + } + } +} diff --git a/app/src/main/java/eu/darken/adsbc/main/core/service/ExampleBinder.kt b/app/src/main/java/eu/darken/adsbc/main/core/service/ExampleBinder.kt new file mode 100644 index 0000000..757d842 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/main/core/service/ExampleBinder.kt @@ -0,0 +1,9 @@ +package eu.darken.adsbc.main.core.service + +import android.os.Binder +import javax.inject.Inject + + +class ExampleBinder @Inject constructor( +// service: ExampleService +) : Binder() diff --git a/app/src/main/java/eu/darken/adsbc/main/core/service/ExampleService.kt b/app/src/main/java/eu/darken/adsbc/main/core/service/ExampleService.kt new file mode 100644 index 0000000..bf65dfe --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/main/core/service/ExampleService.kt @@ -0,0 +1,17 @@ +package eu.darken.adsbc.main.core.service + +import android.content.Intent +import android.os.IBinder +import dagger.hilt.android.AndroidEntryPoint +import eu.darken.adsbc.common.uix.Service2 +import javax.inject.Inject + + +@AndroidEntryPoint +class ExampleService : Service2() { + + @Inject lateinit var binder: ExampleBinder + + override fun onBind(intent: Intent): IBinder = binder + +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/main/ui/MainActivity.kt b/app/src/main/java/eu/darken/adsbc/main/ui/MainActivity.kt new file mode 100644 index 0000000..6cc2ce6 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/main/ui/MainActivity.kt @@ -0,0 +1,45 @@ +package eu.darken.adsbc.main.ui + +import android.os.Bundle +import androidx.activity.viewModels +import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen +import dagger.hilt.android.AndroidEntryPoint +import eu.darken.adsbc.R +import eu.darken.adsbc.common.debug.recording.core.RecorderModule +import eu.darken.adsbc.common.navigation.findNavController +import eu.darken.adsbc.common.uix.Activity2 +import eu.darken.adsbc.databinding.MainActivityBinding +import javax.inject.Inject + +@AndroidEntryPoint +class MainActivity : Activity2() { + + private val vm: MainActivityVM by viewModels() + private lateinit var ui: MainActivityBinding + private val navController by lazy { supportFragmentManager.findNavController(R.id.nav_host) } + + var showSplashScreen = true + + @Inject lateinit var recorderModule: RecorderModule + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + val splashScreen = installSplashScreen() + splashScreen.setKeepOnScreenCondition { showSplashScreen && savedInstanceState == null } + + ui = MainActivityBinding.inflate(layoutInflater) + setContentView(ui.root) + + vm.readyState.observe2 { showSplashScreen = false } + } + + override fun onSaveInstanceState(outState: Bundle) { + outState.putBoolean(B_KEY_SPLASH, showSplashScreen) + super.onSaveInstanceState(outState) + } + + companion object { + private const val B_KEY_SPLASH = "showSplashScreen" + } +} diff --git a/app/src/main/java/eu/darken/adsbc/main/ui/MainActivityVM.kt b/app/src/main/java/eu/darken/adsbc/main/ui/MainActivityVM.kt new file mode 100644 index 0000000..f275174 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/main/ui/MainActivityVM.kt @@ -0,0 +1,45 @@ +package eu.darken.adsbc.main.ui + +import androidx.lifecycle.SavedStateHandle +import dagger.hilt.android.lifecycle.HiltViewModel +import eu.darken.adsbc.common.coroutine.DispatcherProvider +import eu.darken.adsbc.common.debug.logging.Logging.Priority.VERBOSE +import eu.darken.adsbc.common.debug.logging.log +import eu.darken.adsbc.common.uix.ViewModel2 +import eu.darken.adsbc.main.core.SomeRepo +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.onEach +import javax.inject.Inject + + +@HiltViewModel +class MainActivityVM @Inject constructor( + dispatcherProvider: DispatcherProvider, + handle: SavedStateHandle, + private val someRepo: SomeRepo +) : ViewModel2(dispatcherProvider = dispatcherProvider) { + + private val stateFlow = MutableStateFlow(State()) + val state = stateFlow + .onEach { log(VERBOSE) { "New state: $it" } } + .asLiveData2() + + private val readyStateInternal = MutableStateFlow(true) + val readyState = readyStateInternal.asLiveData2() + + init { + log { "ViewModel: $ this" } + log { "SavedStateHandle: ${handle.keys()}" } + log { "Persisted value: ${handle.get("key")}" } + handle.set("key", "valueActivity") + } + + fun onGo() { + stateFlow.value = stateFlow.value.copy(ready = true) + } + + data class State( + val ready: Boolean = false + ) + +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/main/ui/main/MainFragment.kt b/app/src/main/java/eu/darken/adsbc/main/ui/main/MainFragment.kt new file mode 100644 index 0000000..983ad9c --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/main/ui/main/MainFragment.kt @@ -0,0 +1,90 @@ +package eu.darken.adsbc.main.ui.main + +import android.os.Bundle +import android.view.View +import androidx.appcompat.widget.Toolbar +import androidx.fragment.app.viewModels +import androidx.viewpager2.widget.ViewPager2 +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.google.android.material.tabs.TabLayout +import com.google.android.material.tabs.TabLayoutMediator +import dagger.hilt.android.AndroidEntryPoint +import eu.darken.adsbc.R +import eu.darken.adsbc.common.colorString +import eu.darken.adsbc.common.navigation.doNavigate +import eu.darken.adsbc.common.uix.Fragment3 +import eu.darken.adsbc.common.viewbinding.viewBinding +import eu.darken.adsbc.databinding.MainFragmentBinding + +@AndroidEntryPoint +class MainFragment : Fragment3(R.layout.main_fragment) { + + override val vm: MainFragmentVM by viewModels() + override val ui: MainFragmentBinding by viewBinding() + + lateinit var adapter: MainPagerAdapter + + private var showEnterPriseUpgrade: Boolean = false + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + ui.toolbar.apply { + setOnMenuItemClickListener { + when (it.itemId) { + R.id.action_settings -> { + doNavigate(MainFragmentDirections.actionExampleFragmentToSettingsContainerFragment()) + true + } + R.id.action_upgrade -> { + showUpgradeDialog() + true + } + else -> super.onOptionsItemSelected(it) + } + } + } + + vm.state.observe2(ui) { state -> + adapter = MainPagerAdapter(childFragmentManager, lifecycle, state.pages) + + viewpager.adapter = adapter + tablayout.tabMode = TabLayout.MODE_SCROLLABLE + // When smoothScroll is enabled and we navigate to an unloaded fragment, ??? happens we jump to the wrong position + TabLayoutMediator(tablayout, viewpager, true, false) { tab, position -> + tab.setText(adapter.pages[position].titleRes) + }.attach() + + viewpager.setCurrentItem(state.pagePosition, false) + + toolbar.menu.findItem(R.id.action_upgrade).isVisible = !state.isEnterprise + toolbar.updateTitle(state.isEnterprise) + } + + ui.viewpager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { + override fun onPageSelected(position: Int) { + vm.updateCurrentPage(position) + } + }) + + super.onViewCreated(view, savedInstanceState) + } + + private fun Toolbar.updateTitle(isEnterprise: Boolean) { + subtitle = if (isEnterprise) { + colorString(context, R.color.enterprise, getString(R.string.enterprise_mode_label) + " (╯°□°)╯︵ ┻━┻") + } else { + null + } + + } + + private fun showUpgradeDialog() { + MaterialAlertDialogBuilder(requireContext()).apply { + setTitle(R.string.general_upgrade_action) + setMessage(R.string.upgrade_enterprise_description) + setPositiveButton(R.string.general_upgrade_action) { _, _ -> + vm.upgradeEnterprise() + } + setNegativeButton(R.string.general_cancel_action) { _, _ -> } + }.show() + } +} diff --git a/app/src/main/java/eu/darken/adsbc/main/ui/main/MainFragmentVM.kt b/app/src/main/java/eu/darken/adsbc/main/ui/main/MainFragmentVM.kt new file mode 100644 index 0000000..812bf3a --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/main/ui/main/MainFragmentVM.kt @@ -0,0 +1,55 @@ +package eu.darken.adsbc.main.ui.main + +import androidx.lifecycle.SavedStateHandle +import dagger.hilt.android.lifecycle.HiltViewModel +import eu.darken.adsbc.R +import eu.darken.adsbc.aircraft.ui.AircraftListFragment +import eu.darken.adsbc.common.coroutine.DispatcherProvider +import eu.darken.adsbc.common.uix.ViewModel3 +import eu.darken.adsbc.enterprise.core.EnterpriseSettings +import eu.darken.adsbc.feeder.ui.list.FeederListFragment +import eu.darken.adsbc.main.core.GeneralSettings +import kotlinx.coroutines.flow.combine +import javax.inject.Inject + +@HiltViewModel +class MainFragmentVM @Inject constructor( + handle: SavedStateHandle, + dispatcherProvider: DispatcherProvider, + private val generalSettings: GeneralSettings, + private val enterpriseSettings: EnterpriseSettings, +) : ViewModel3(dispatcherProvider = dispatcherProvider) { + + private var currentPosition: Int = 0 + + data class State( + val pages: List, + val pagePosition: Int, + val isEnterprise: Boolean, + ) + + val state = combine( + generalSettings.isBugTrackingEnabled.flow, + enterpriseSettings.isEnterpriseMode.flow + ) { _, isEnterprise -> + val basePages = listOf( + MainPagerAdapter.Page(FeederListFragment::class, R.string.feeder_list_page_label), + MainPagerAdapter.Page(AircraftListFragment::class, R.string.aircraft_list_page_label), + ) + + State( + pages = basePages, + pagePosition = currentPosition, + isEnterprise = isEnterprise, + ) + }.asLiveData2() + + fun updateCurrentPage(position: Int) { + currentPosition = position + } + + fun upgradeEnterprise() { + enterpriseSettings.isEnterpriseMode.value = true + } + +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/main/ui/main/MainPagerAdapter.kt b/app/src/main/java/eu/darken/adsbc/main/ui/main/MainPagerAdapter.kt new file mode 100644 index 0000000..1c240f0 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/main/ui/main/MainPagerAdapter.kt @@ -0,0 +1,39 @@ +package eu.darken.adsbc.main.ui.main + +import androidx.annotation.StringRes +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentManager +import androidx.lifecycle.Lifecycle +import androidx.viewpager2.adapter.FragmentStateAdapter +import eu.darken.adsbc.common.debug.logging.log +import kotlin.reflect.KClass + + +class MainPagerAdapter constructor( + fragmentManager: FragmentManager, + lifecycle: Lifecycle, + val pages: List +) : FragmentStateAdapter( + fragmentManager, + lifecycle +) { + + private val fragmentFactory = fragmentManager.fragmentFactory + + init { + log { "pages=$pages" } + } + + override fun getItemCount(): Int = pages.size + + override fun createFragment(position: Int): Fragment { + val targetPage = pages[position] + log { "createFragment(): targetPage=$targetPage" } + return fragmentFactory.instantiate(this.javaClass.classLoader!!, targetPage.fragmentClazz.qualifiedName!!) + } + + data class Page( + val fragmentClazz: KClass, + @StringRes val titleRes: Int + ) +} diff --git a/app/src/main/java/eu/darken/adsbc/main/ui/settings/SettingsFragment.kt b/app/src/main/java/eu/darken/adsbc/main/ui/settings/SettingsFragment.kt new file mode 100644 index 0000000..417beac --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/main/ui/settings/SettingsFragment.kt @@ -0,0 +1,120 @@ +package eu.darken.adsbc.main.ui.settings + +import android.os.Bundle +import android.os.Parcelable +import android.view.View +import androidx.appcompat.widget.Toolbar +import androidx.fragment.app.viewModels +import androidx.preference.Preference +import androidx.preference.PreferenceFragmentCompat +import dagger.hilt.android.AndroidEntryPoint +import eu.darken.adsbc.R +import eu.darken.adsbc.common.uix.Fragment2 +import eu.darken.adsbc.common.viewbinding.viewBinding +import eu.darken.adsbc.databinding.SettingsFragmentBinding +import kotlinx.parcelize.Parcelize + +@AndroidEntryPoint +class SettingsFragment : Fragment2(R.layout.settings_fragment), + PreferenceFragmentCompat.OnPreferenceStartFragmentCallback { + + private val vm: SettingsFragmentVM by viewModels() + private val ui: SettingsFragmentBinding by viewBinding() + + val toolbar: Toolbar + get() = ui.toolbar + + private val screens = ArrayList() + + @Parcelize + data class Screen( + val fragmentClass: String, + val screenTitle: String? + ) : Parcelable + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + childFragmentManager.addOnBackStackChangedListener { + val backStackCnt = childFragmentManager.backStackEntryCount + val newScreenInfo = when { + backStackCnt < screens.size -> { + // We popped the backstack, restore the underlying screen infos + // If there are none left, we are at the index again + screens.removeLastOrNull() + screens.lastOrNull() ?: Screen( + fragmentClass = SettingsIndexFragment::class.qualifiedName!!, + screenTitle = getString(R.string.label_settings) + ) + } + else -> { + // We added the current fragment to the stack, the new fragment's infos were already set, do nothing. + null + } + } + + newScreenInfo?.let { setCurrentScreenInfo(it) } + } + + if (savedInstanceState == null) { + childFragmentManager + .beginTransaction() + .replace(R.id.content_frame, SettingsIndexFragment()) + .commit() + } else { + savedInstanceState.getParcelableArrayList(BKEY_SCREEN_INFOS)?.let { + screens.addAll(it) + } + screens.lastOrNull()?.let { setCurrentScreenInfo(it) } + } + + ui.toolbar.setNavigationOnClickListener { requireActivity().onBackPressed() } + + super.onViewCreated(view, savedInstanceState) + } + + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putParcelableArrayList(BKEY_SCREEN_INFOS, screens) + } + + override fun onPreferenceStartFragment(caller: PreferenceFragmentCompat, pref: Preference): Boolean { + val screenInfo = Screen( + fragmentClass = pref.fragment!!, + screenTitle = pref.title?.toString() + ) + + val args = Bundle().apply { + putAll(pref.extras) + putString(BKEY_SCREEN_TITLE, screenInfo.screenTitle) + } + + val fragment = childFragmentManager.fragmentFactory + .instantiate(this::class.java.classLoader!!, pref.fragment!!) + .apply { + arguments = args + setTargetFragment(caller, 0) + } + + setCurrentScreenInfo(screenInfo) + screens.add(screenInfo) + + childFragmentManager.beginTransaction().apply { + replace(R.id.content_frame, fragment) + addToBackStack(null) + }.commit() + + return true + } + + + private fun setCurrentScreenInfo(info: Screen) { + ui.toolbar.apply { + title = info.screenTitle + } + } + + companion object { + private const val BKEY_SCREEN_TITLE = "preferenceScreenTitle" + private const val BKEY_SCREEN_INFOS = "preferenceScreenInfos" + } +} diff --git a/app/src/main/java/eu/darken/adsbc/main/ui/settings/SettingsFragmentVM.kt b/app/src/main/java/eu/darken/adsbc/main/ui/settings/SettingsFragmentVM.kt new file mode 100644 index 0000000..3825611 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/main/ui/settings/SettingsFragmentVM.kt @@ -0,0 +1,13 @@ +package eu.darken.adsbc.main.ui.settings + +import androidx.lifecycle.SavedStateHandle +import dagger.hilt.android.lifecycle.HiltViewModel +import eu.darken.adsbc.common.coroutine.DispatcherProvider +import eu.darken.adsbc.common.uix.ViewModel2 +import javax.inject.Inject + +@HiltViewModel +class SettingsFragmentVM @Inject constructor( + private val handle: SavedStateHandle, + private val dispatcherProvider: DispatcherProvider, +) : ViewModel2(dispatcherProvider) \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/main/ui/settings/SettingsIndexFragment.kt b/app/src/main/java/eu/darken/adsbc/main/ui/settings/SettingsIndexFragment.kt new file mode 100644 index 0000000..7a7bb96 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/main/ui/settings/SettingsIndexFragment.kt @@ -0,0 +1,41 @@ +package eu.darken.adsbc.main.ui.settings + +import android.os.Bundle +import android.view.View +import androidx.preference.Preference +import dagger.hilt.android.AndroidEntryPoint +import eu.darken.adsbc.R +import eu.darken.adsbc.common.BuildConfigWrap +import eu.darken.adsbc.common.WebpageTool +import eu.darken.adsbc.common.preferences.Settings +import eu.darken.adsbc.common.uix.PreferenceFragment2 +import eu.darken.adsbc.main.core.GeneralSettings +import javax.inject.Inject + +@AndroidEntryPoint +class SettingsIndexFragment : PreferenceFragment2() { + + @Inject lateinit var generalSettings: GeneralSettings + override val settings: Settings + get() = generalSettings + override val preferenceFile: Int = R.xml.preferences_index + + @Inject lateinit var webpageTool: WebpageTool + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + setupMenu(R.menu.menu_settings_index) { item -> + when (item.itemId) { + R.id.menu_item_twitter -> { + webpageTool.open("https://twitter.com/d4rken") + } + } + } + super.onViewCreated(view, savedInstanceState) + } + + override fun onPreferencesCreated() { + findPreference("core.changelog")!!.summary = BuildConfigWrap.VERSION_DESCRIPTION + + super.onPreferencesCreated() + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/main/ui/settings/acks/AcknowledgementsFragment.kt b/app/src/main/java/eu/darken/adsbc/main/ui/settings/acks/AcknowledgementsFragment.kt new file mode 100644 index 0000000..50095c0 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/main/ui/settings/acks/AcknowledgementsFragment.kt @@ -0,0 +1,22 @@ +package eu.darken.adsbc.main.ui.settings.acks + +import androidx.annotation.Keep +import androidx.fragment.app.viewModels +import dagger.hilt.android.AndroidEntryPoint +import eu.darken.adsbc.R +import eu.darken.adsbc.common.uix.PreferenceFragment2 +import eu.darken.adsbc.main.core.GeneralSettings +import javax.inject.Inject + +@Keep +@AndroidEntryPoint +class AcknowledgementsFragment : PreferenceFragment2() { + + private val vm: AcknowledgementsFragmentVM by viewModels() + + override val preferenceFile: Int = R.xml.preferences_acknowledgements + @Inject lateinit var debugSettings: GeneralSettings + + override val settings: GeneralSettings by lazy { debugSettings } + +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/main/ui/settings/acks/AcknowledgementsFragmentVM.kt b/app/src/main/java/eu/darken/adsbc/main/ui/settings/acks/AcknowledgementsFragmentVM.kt new file mode 100644 index 0000000..85dac0d --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/main/ui/settings/acks/AcknowledgementsFragmentVM.kt @@ -0,0 +1,17 @@ +package eu.darken.adsbc.main.ui.settings.acks + +import androidx.lifecycle.SavedStateHandle +import dagger.assisted.AssistedInject +import eu.darken.adsbc.common.coroutine.DispatcherProvider +import eu.darken.adsbc.common.debug.logging.logTag +import eu.darken.adsbc.common.uix.ViewModel3 + +class AcknowledgementsFragmentVM @AssistedInject constructor( + private val handle: SavedStateHandle, + private val dispatcherProvider: DispatcherProvider +) : ViewModel3(dispatcherProvider) { + + companion object { + private val TAG = logTag("Settings", "Acknowledgements", "VM") + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/main/ui/settings/general/GeneralSettingsFragment.kt b/app/src/main/java/eu/darken/adsbc/main/ui/settings/general/GeneralSettingsFragment.kt new file mode 100644 index 0000000..2c23122 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/main/ui/settings/general/GeneralSettingsFragment.kt @@ -0,0 +1,62 @@ +package eu.darken.adsbc.main.ui.settings.general + +import androidx.annotation.Keep +import androidx.fragment.app.viewModels +import androidx.preference.Preference +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import dagger.hilt.android.AndroidEntryPoint +import eu.darken.adsbc.R +import eu.darken.adsbc.common.debug.logging.Logging.Priority.ERROR +import eu.darken.adsbc.common.debug.logging.log +import eu.darken.adsbc.common.debug.logging.logTag +import eu.darken.adsbc.common.uix.PreferenceFragment2 +import eu.darken.adsbc.databinding.SettingsNumberInputDialogBinding +import eu.darken.adsbc.main.core.GeneralSettings +import javax.inject.Inject + +@Keep +@AndroidEntryPoint +class GeneralSettingsFragment : PreferenceFragment2() { + + private val vdc: GeneralSettingsFragmentVM by viewModels() + + @Inject lateinit var generalSettings: GeneralSettings + + override val settings: GeneralSettings by lazy { generalSettings } + override val preferenceFile: Int = R.xml.preferences_general + + override fun onPreferenceTreeClick(preference: Preference): Boolean = when (preference.key) { + "feeder.update.interval" -> showFeederUpdateIntervalDialog(preference) + else -> super.onPreferenceTreeClick(preference) + } + + private fun showFeederUpdateIntervalDialog(preference: Preference): Boolean { + MaterialAlertDialogBuilder(requireContext()).apply { + val layout = SettingsNumberInputDialogBinding.inflate(layoutInflater) + + layout.inputText.setText(generalSettings.updateInterval.value.let { it / 1000 }.toString()) + layout.inputLayout.hint = "Seconds" + + setView(layout.root) + setTitle("Feeder update interval") + setMessage("Time in seconds how often each feeder should be polled.") + setPositiveButton(R.string.general_set_action) { _, _ -> + val textToParse = layout.inputText.text.toString() + try { + generalSettings.updateInterval.value = textToParse.toLong() * 1000L + } catch (e: Exception) { + log(TAG, ERROR) { "Failed to update feeder update interval with $textToParse" } + } + } + setNegativeButton(R.string.general_cancel_action) { _, _ -> } + setNeutralButton(R.string.general_reset_action) { _, _ -> + generalSettings.updateInterval.reset() + } + }.show() + return true + } + + companion object { + private val TAG = logTag("Settings", "General") + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/main/ui/settings/general/GeneralSettingsFragmentVM.kt b/app/src/main/java/eu/darken/adsbc/main/ui/settings/general/GeneralSettingsFragmentVM.kt new file mode 100644 index 0000000..894e850 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/main/ui/settings/general/GeneralSettingsFragmentVM.kt @@ -0,0 +1,20 @@ +package eu.darken.adsbc.main.ui.settings.general + +import androidx.lifecycle.SavedStateHandle +import dagger.hilt.android.lifecycle.HiltViewModel +import eu.darken.adsbc.common.coroutine.DispatcherProvider +import eu.darken.adsbc.common.debug.logging.logTag +import eu.darken.adsbc.common.uix.ViewModel3 +import javax.inject.Inject + +@HiltViewModel +class GeneralSettingsFragmentVM @Inject constructor( + private val handle: SavedStateHandle, + private val dispatcherProvider: DispatcherProvider, +) : ViewModel3(dispatcherProvider) { + + + companion object { + private val TAG = logTag("Settings", "General", "VM") + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/main/ui/settings/general/advanced/AdvancedSettingsFragment.kt b/app/src/main/java/eu/darken/adsbc/main/ui/settings/general/advanced/AdvancedSettingsFragment.kt new file mode 100644 index 0000000..46a70db --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/main/ui/settings/general/advanced/AdvancedSettingsFragment.kt @@ -0,0 +1,22 @@ +package eu.darken.adsbc.main.ui.settings.general.advanced + +import androidx.annotation.Keep +import androidx.fragment.app.viewModels +import dagger.hilt.android.AndroidEntryPoint +import eu.darken.adsbc.R +import eu.darken.adsbc.common.uix.PreferenceFragment2 +import eu.darken.adsbc.main.core.GeneralSettings +import javax.inject.Inject + +@Keep +@AndroidEntryPoint +class AdvancedSettingsFragment : PreferenceFragment2() { + + private val vdc: AdvancedSettingsFragmentVM by viewModels() + + @Inject lateinit var debugSettings: GeneralSettings + + override val settings: GeneralSettings by lazy { debugSettings } + override val preferenceFile: Int = R.xml.preferences_advanced + +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/main/ui/settings/general/advanced/AdvancedSettingsFragmentVM.kt b/app/src/main/java/eu/darken/adsbc/main/ui/settings/general/advanced/AdvancedSettingsFragmentVM.kt new file mode 100644 index 0000000..9ed1caf --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/main/ui/settings/general/advanced/AdvancedSettingsFragmentVM.kt @@ -0,0 +1,19 @@ +package eu.darken.adsbc.main.ui.settings.general.advanced + +import androidx.lifecycle.SavedStateHandle +import dagger.hilt.android.lifecycle.HiltViewModel +import eu.darken.adsbc.common.coroutine.DispatcherProvider +import eu.darken.adsbc.common.debug.logging.logTag +import eu.darken.adsbc.common.uix.ViewModel3 +import javax.inject.Inject + +@HiltViewModel +class AdvancedSettingsFragmentVM @Inject constructor( + private val handle: SavedStateHandle, + private val dispatcherProvider: DispatcherProvider, +) : ViewModel3(dispatcherProvider) { + + companion object { + private val TAG = logTag("Settings", "General", "Advanced", "VM") + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/main/ui/settings/support/SupportFragment.kt b/app/src/main/java/eu/darken/adsbc/main/ui/settings/support/SupportFragment.kt new file mode 100644 index 0000000..4beadb2 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/main/ui/settings/support/SupportFragment.kt @@ -0,0 +1,59 @@ +package eu.darken.adsbc.main.ui.settings.support + +import android.os.Bundle +import android.view.View +import androidx.annotation.Keep +import androidx.fragment.app.viewModels +import androidx.preference.Preference +import com.google.android.material.snackbar.Snackbar +import dagger.hilt.android.AndroidEntryPoint +import eu.darken.adsbc.R +import eu.darken.adsbc.common.ClipboardHelper +import eu.darken.adsbc.common.observe2 +import eu.darken.adsbc.common.uix.PreferenceFragment2 +import eu.darken.adsbc.main.core.GeneralSettings +import javax.inject.Inject + +@Keep +@AndroidEntryPoint +class SupportFragment : PreferenceFragment2() { + + private val vm: SupportFragmentVM by viewModels() + + override val preferenceFile: Int = R.xml.preferences_support + @Inject lateinit var generalSettings: GeneralSettings + + override val settings: GeneralSettings by lazy { generalSettings } + + @Inject lateinit var clipboardHelper: ClipboardHelper + + private val installIdPref by lazy { findPreference("support.installid")!! } + private val supportMailPref by lazy { findPreference("support.email.darken")!! } + + override fun onPreferencesCreated() { + installIdPref.setOnPreferenceClickListener { + vm.copyInstallID() + true + } + supportMailPref.setOnPreferenceClickListener { + vm.sendSupportMail() + true + } + + super.onPreferencesCreated() + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + vm.clipboardEvent.observe2(this) { installId -> + Snackbar.make(requireView(), installId, Snackbar.LENGTH_INDEFINITE) + .setAction(R.string.general_copy_action) { + clipboardHelper.copyToClipboard(installId) + } + .show() + } + + vm.emailEvent.observe2(this) { startActivity(it) } + + super.onViewCreated(view, savedInstanceState) + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/main/ui/settings/support/SupportFragmentVM.kt b/app/src/main/java/eu/darken/adsbc/main/ui/settings/support/SupportFragmentVM.kt new file mode 100644 index 0000000..06cf93f --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/main/ui/settings/support/SupportFragmentVM.kt @@ -0,0 +1,49 @@ +package eu.darken.adsbc.main.ui.settings.support + +import android.content.Intent +import android.os.Build +import androidx.lifecycle.SavedStateHandle +import dagger.hilt.android.lifecycle.HiltViewModel +import eu.darken.adsbc.common.BuildConfigWrap +import eu.darken.adsbc.common.EmailTool +import eu.darken.adsbc.common.InstallId +import eu.darken.adsbc.common.coroutine.DispatcherProvider +import eu.darken.adsbc.common.livedata.SingleLiveEvent +import eu.darken.adsbc.common.uix.ViewModel3 +import javax.inject.Inject + +@HiltViewModel +class SupportFragmentVM @Inject constructor( + private val handle: SavedStateHandle, + private val emailTool: EmailTool, + private val installId: InstallId, + private val dispatcherProvider: DispatcherProvider, +) : ViewModel3(dispatcherProvider) { + + val emailEvent = SingleLiveEvent() + val clipboardEvent = SingleLiveEvent() + + fun sendSupportMail() = launch { + + val bodyInfo = StringBuilder("\n\n\n") + + bodyInfo.append("--- Infos for the developer ---\n") + + bodyInfo.append("App version: ").append(BuildConfigWrap.VERSION_DESCRIPTION).append("\n") + + bodyInfo.append("Device: ").append(Build.FINGERPRINT).append("\n") + bodyInfo.append("Install ID: ").append(installId.id).append("\n") + + val email = EmailTool.Email( + receipients = listOf("support@darken.eu"), + subject = "[SD Maid] Question/Suggestion/Request\n", + body = bodyInfo.toString() + ) + + emailEvent.postValue(emailTool.build(email)) + } + + fun copyInstallID() = launch { + clipboardEvent.postValue(installId.id) + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/stats/core/api/ADSBxStatsApi.kt b/app/src/main/java/eu/darken/adsbc/stats/core/api/ADSBxStatsApi.kt new file mode 100644 index 0000000..98822d6 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/stats/core/api/ADSBxStatsApi.kt @@ -0,0 +1,18 @@ +package eu.darken.adsbc.stats.core.api + +import okhttp3.ResponseBody +import retrofit2.http.GET +import retrofit2.http.Query +import java.time.Instant + +interface ADSBxStatsApi { + + @GET("/api/feeders/?") + suspend fun getStats(@Query("feed") feedId: String): ResponseBody + + data class FeederInfo( + val lastSeenAt: Instant, + val aircraftCountLogged: Int, + ) + +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/stats/core/api/ADSBxStatsEndpoint.kt b/app/src/main/java/eu/darken/adsbc/stats/core/api/ADSBxStatsEndpoint.kt new file mode 100644 index 0000000..abd7ec6 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/stats/core/api/ADSBxStatsEndpoint.kt @@ -0,0 +1,92 @@ +package eu.darken.adsbc.stats.core.api + +import dagger.Reusable +import eu.darken.adsbc.common.coroutine.DispatcherProvider +import eu.darken.adsbc.common.debug.logging.log +import eu.darken.adsbc.common.debug.logging.logTag +import eu.darken.adsbc.feeder.core.ADSBxId +import kotlinx.coroutines.withContext +import okhttp3.OkHttpClient +import org.jsoup.Jsoup +import org.jsoup.nodes.Document +import retrofit2.Retrofit +import retrofit2.converter.moshi.MoshiConverterFactory +import java.time.Instant +import java.util.regex.Pattern +import javax.inject.Inject + +@Reusable +class ADSBxStatsEndpoint @Inject constructor( + private val baseClient: OkHttpClient, + private val moshiConverterFactory: MoshiConverterFactory, + private val dispatcherProvider: DispatcherProvider, +) { + private val api by lazy { + val configHttpClient = baseClient.newBuilder().apply { + + }.build() + + Retrofit.Builder() + .client(configHttpClient) + .baseUrl("https://www.adsbexchange.com/") + .addConverterFactory(moshiConverterFactory) + .build() + .create(ADSBxStatsApi::class.java) + } + + suspend fun getFeederStats(id: ADSBxId): ADSBxStatsApi.FeederInfo = withContext(dispatcherProvider.IO) { + log(TAG) { "getFeederStats(id=$id)" } + + val body = api.getStats(id.value) + + val bodyRaw = body.string() + val jsoup = Jsoup.parse(bodyRaw) + + ADSBxStatsApi.FeederInfo( + lastSeenAt = parseLastSeen(jsoup), + aircraftCountLogged = parseAircraftCountLogged(jsoup) + ) + } + + private fun parseAircraftCountLogged(doc: Document): Int { + val infoText = doc + .select("div") + .firstOrNull { it.text().contains("Aircraft count logged") } + ?.text() + ?: throw IllegalArgumentException("Failed to find div with aircraft count logged") + + val matcher = LOG_COUNT_PATTERN.matcher(infoText) + + if (!matcher.matches()) throw IllegalArgumentException("Aircraft count log pattern doesn't match") + + val logCount = matcher.group(1)!!.toInt() + log(TAG) { "Parsed aircraft log count: $logCount" } + return logCount + } + + private fun parseLastSeen(doc: Document): Instant { + val infoText = doc + .select("div") + .firstOrNull { it.text().contains("seconds since epoc") } + ?.text() + ?: throw IllegalArgumentException("Failed find div with last seen") + + val matcher = LAST_SEEN_PATTERN.matcher(infoText) + + if (!matcher.matches()) throw IllegalArgumentException("Last seen pattern doesn't match.") + + val lastSeen = Instant.ofEpochMilli((matcher.group(1)!!.toDouble() * 1000).toLong()) + log(TAG) { "Parsed last seen: $lastSeen" } + return lastSeen + } + + companion object { + private val LOG_COUNT_PATTERN = Pattern.compile( + "^Aircraft count logged (\\d+) times \\(every 60 seconds\\).+?\$" + ) + private val LAST_SEEN_PATTERN = Pattern.compile( + "^.+?last seen \\((\\d+\\.\\d+) seconds since epoc\\).+?\$" + ) + private val TAG = logTag("Feeder", "Stats", "Endpoint") + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/stats/core/storage/FeederStatsDao.kt b/app/src/main/java/eu/darken/adsbc/stats/core/storage/FeederStatsDao.kt new file mode 100644 index 0000000..1a622d6 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/stats/core/storage/FeederStatsDao.kt @@ -0,0 +1,27 @@ +package eu.darken.adsbc.stats.core.storage + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.Query +import androidx.room.Update +import eu.darken.adsbc.feeder.core.FeederId +import kotlinx.coroutines.flow.Flow + +@Dao +interface FeederStatsDao { + + @Query("SELECT * FROM stats_feeder") + fun allFeederStats(): Flow> + + @Query("SELECT * FROM stats_feeder WHERE uid IN (:ids)") + fun getFeederStatsByFeederId(vararg ids: FeederId): FeederStatsData? + + @Insert + suspend fun insertStats(stats: FeederStatsData) + + @Update(entity = FeederStatsData::class) + suspend fun updateStats(stats: FeederStatsData) + + @Query("DELETE FROM stats_feeder WHERE uid = :id") + suspend fun deleteStats(id: FeederId) +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/stats/core/storage/FeederStatsData.kt b/app/src/main/java/eu/darken/adsbc/stats/core/storage/FeederStatsData.kt new file mode 100644 index 0000000..ab286a7 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/stats/core/storage/FeederStatsData.kt @@ -0,0 +1,15 @@ +package eu.darken.adsbc.stats.core.storage + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import eu.darken.adsbc.feeder.core.FeederId +import java.time.Instant + +@Entity(tableName = "stats_feeder") +data class FeederStatsData( + @PrimaryKey val uid: FeederId, + + @ColumnInfo(name = "statsUpdatedAt") val statsUpdatedAt: Instant = Instant.MIN, + @ColumnInfo(name = "aircraftCountLogged") val aircraftCountLogged: Int = 0, +) \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/stats/core/storage/StatsDatabase.kt b/app/src/main/java/eu/darken/adsbc/stats/core/storage/StatsDatabase.kt new file mode 100644 index 0000000..3eb35db --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/stats/core/storage/StatsDatabase.kt @@ -0,0 +1,26 @@ +package eu.darken.adsbc.stats.core.storage + +import android.content.Context +import androidx.room.Database +import androidx.room.Room +import androidx.room.RoomDatabase +import androidx.room.TypeConverters +import dagger.hilt.android.qualifiers.ApplicationContext +import eu.darken.adsbc.common.room.CommonConverters +import javax.inject.Inject + + +@Database( + entities = [FeederStatsData::class], + version = 1 +) +@TypeConverters(CommonConverters::class) +abstract class StatsDatabase : RoomDatabase() { + abstract fun statsDao(): FeederStatsDao + + class Factory @Inject constructor(@ApplicationContext private val context: Context) { + fun create(): StatsDatabase = Room + .databaseBuilder(context, StatsDatabase::class.java, "stats.db") + .build() + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/stats/core/storage/StatsStorage.kt b/app/src/main/java/eu/darken/adsbc/stats/core/storage/StatsStorage.kt new file mode 100644 index 0000000..ad7d4ee --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/stats/core/storage/StatsStorage.kt @@ -0,0 +1,59 @@ +package eu.darken.adsbc.stats.core.storage + +import androidx.room.withTransaction +import eu.darken.adsbc.common.debug.logging.Logging.Priority.ERROR +import eu.darken.adsbc.common.debug.logging.log +import eu.darken.adsbc.common.debug.logging.logTag +import eu.darken.adsbc.feeder.core.FeederId +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class StatsStorage @Inject constructor( + private val statsDatabaseFactory: StatsDatabase.Factory, +) { + + private val db by lazy { statsDatabaseFactory.create() } + private val statsDao by lazy { db.statsDao() } + + val allFeederStats = statsDao.allFeederStats() + + suspend fun create(id: FeederId, stats: FeederStatsData? = null): FeederStatsData { + log(TAG) { "create(id=$id, stats=$stats)" } + + return db.withTransaction { + val existing = statsDao.getFeederStatsByFeederId(id) + if (existing != null) { + log(TAG, ERROR) { "Stats already exist: $id" } + throw IllegalStateException("$id already exists") + } + + val newStats = FeederStatsData(uid = id) + + statsDao.insertStats(newStats) + statsDao.getFeederStatsByFeederId(id)!!.also { + log(TAG) { "Stats created: $it" } + } + } + } + + suspend fun delete(feederId: FeederId) { + log(TAG) { "delete(feederId=$feederId)" } + statsDao.deleteStats(feederId) + } + + suspend fun update(id: FeederId, block: (FeederStatsData) -> FeederStatsData) { + log(TAG) { "update(id=$id, block=$block)" } + + db.withTransaction { + val oldFeeder = statsDao.getFeederStatsByFeederId(id) ?: return@withTransaction + val updatedStats = block(oldFeeder) + log(TAG) { "Updated feeder stats for $id\nBefore:$oldFeeder\nNow: $oldFeeder" } + statsDao.updateStats(updatedStats) + } + } + + companion object { + private val TAG = logTag("Stats", "Storage") + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/adsbc/stats/core/storage/StatsStorageExtensions.kt b/app/src/main/java/eu/darken/adsbc/stats/core/storage/StatsStorageExtensions.kt new file mode 100644 index 0000000..45a7a20 --- /dev/null +++ b/app/src/main/java/eu/darken/adsbc/stats/core/storage/StatsStorageExtensions.kt @@ -0,0 +1,9 @@ +package eu.darken.adsbc.stats.core.storage + +import eu.darken.adsbc.feeder.core.FeederId +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map + +fun StatsStorage.byFeederId(feederId: FeederId): Flow = allFeederStats.map { fs -> + fs.singleOrNull { it.uid == feederId } +} \ No newline at end of file diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_baseline_add_circle_outline_24.xml b/app/src/main/res/drawable/ic_baseline_add_circle_outline_24.xml new file mode 100644 index 0000000..761b0b8 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_add_circle_outline_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_baseline_arrow_back_24.xml b/app/src/main/res/drawable/ic_baseline_arrow_back_24.xml new file mode 100644 index 0000000..e9a2ed4 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_arrow_back_24.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/drawable/ic_baseline_av_timer_24.xml b/app/src/main/res/drawable/ic_baseline_av_timer_24.xml new file mode 100644 index 0000000..625c186 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_av_timer_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_baseline_bug_report_24.xml b/app/src/main/res/drawable/ic_baseline_bug_report_24.xml new file mode 100644 index 0000000..bc724e0 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_bug_report_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_baseline_business_center_24.xml b/app/src/main/res/drawable/ic_baseline_business_center_24.xml new file mode 100644 index 0000000..443df8c --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_business_center_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_baseline_delete_sweep_24.xml b/app/src/main/res/drawable/ic_baseline_delete_sweep_24.xml new file mode 100644 index 0000000..3683aa4 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_delete_sweep_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_baseline_drive_file_rename_outline_24.xml b/app/src/main/res/drawable/ic_baseline_drive_file_rename_outline_24.xml new file mode 100644 index 0000000..1e472a8 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_drive_file_rename_outline_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_baseline_info_24.xml b/app/src/main/res/drawable/ic_baseline_info_24.xml new file mode 100644 index 0000000..7396ebc --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_info_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_baseline_radio_24.xml b/app/src/main/res/drawable/ic_baseline_radio_24.xml new file mode 100644 index 0000000..469bca5 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_radio_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_baseline_refresh_24.xml b/app/src/main/res/drawable/ic_baseline_refresh_24.xml new file mode 100644 index 0000000..566500b --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_refresh_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_baseline_science_24.xml b/app/src/main/res/drawable/ic_baseline_science_24.xml new file mode 100644 index 0000000..d0d5edc --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_science_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_baseline_settings_24.xml b/app/src/main/res/drawable/ic_baseline_settings_24.xml new file mode 100644 index 0000000..d99a8ae --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_settings_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_card_text_onsurface.xml b/app/src/main/res/drawable/ic_card_text_onsurface.xml new file mode 100644 index 0000000..4d50718 --- /dev/null +++ b/app/src/main/res/drawable/ic_card_text_onsurface.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_changelog_onsurface.xml b/app/src/main/res/drawable/ic_changelog_onsurface.xml new file mode 100644 index 0000000..cc089a2 --- /dev/null +++ b/app/src/main/res/drawable/ic_changelog_onsurface.xml @@ -0,0 +1,10 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_email_onsurface.xml b/app/src/main/res/drawable/ic_email_onsurface.xml new file mode 100644 index 0000000..d26645f --- /dev/null +++ b/app/src/main/res/drawable/ic_email_onsurface.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_heart.xml b/app/src/main/res/drawable/ic_heart.xml new file mode 100644 index 0000000..12b07aa --- /dev/null +++ b/app/src/main/res/drawable/ic_heart.xml @@ -0,0 +1,10 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_id_onsurface.xml b/app/src/main/res/drawable/ic_id_onsurface.xml new file mode 100644 index 0000000..324e614 --- /dev/null +++ b/app/src/main/res/drawable/ic_id_onsurface.xml @@ -0,0 +1,10 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_spider_thread_onsurface.xml b/app/src/main/res/drawable/ic_spider_thread_onsurface.xml new file mode 100644 index 0000000..94a8028 --- /dev/null +++ b/app/src/main/res/drawable/ic_spider_thread_onsurface.xml @@ -0,0 +1,10 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_twitter.xml b/app/src/main/res/drawable/ic_twitter.xml new file mode 100644 index 0000000..ac5a0db --- /dev/null +++ b/app/src/main/res/drawable/ic_twitter.xml @@ -0,0 +1,10 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/launch_screen.xml b/app/src/main/res/drawable/launch_screen.xml new file mode 100644 index 0000000..4f52557 --- /dev/null +++ b/app/src/main/res/drawable/launch_screen.xml @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/aircraft_list_action_dialog.xml b/app/src/main/res/layout/aircraft_list_action_dialog.xml new file mode 100644 index 0000000..357d07f --- /dev/null +++ b/app/src/main/res/layout/aircraft_list_action_dialog.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/aircraft_list_fragment.xml b/app/src/main/res/layout/aircraft_list_fragment.xml new file mode 100644 index 0000000..ea32e81 --- /dev/null +++ b/app/src/main/res/layout/aircraft_list_fragment.xml @@ -0,0 +1,16 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/aircraft_list_line.xml b/app/src/main/res/layout/aircraft_list_line.xml new file mode 100644 index 0000000..0cd7729 --- /dev/null +++ b/app/src/main/res/layout/aircraft_list_line.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/debug_recording_activity.xml b/app/src/main/res/layout/debug_recording_activity.xml new file mode 100644 index 0000000..d2cdff3 --- /dev/null +++ b/app/src/main/res/layout/debug_recording_activity.xml @@ -0,0 +1,119 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/feeder_list_action_dialog.xml b/app/src/main/res/layout/feeder_list_action_dialog.xml new file mode 100644 index 0000000..357d07f --- /dev/null +++ b/app/src/main/res/layout/feeder_list_action_dialog.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/feeder_list_fragment.xml b/app/src/main/res/layout/feeder_list_fragment.xml new file mode 100644 index 0000000..bcbd8f4 --- /dev/null +++ b/app/src/main/res/layout/feeder_list_fragment.xml @@ -0,0 +1,25 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/feeder_list_input_dialog.xml b/app/src/main/res/layout/feeder_list_input_dialog.xml new file mode 100644 index 0000000..1b27bc0 --- /dev/null +++ b/app/src/main/res/layout/feeder_list_input_dialog.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/feeder_list_line.xml b/app/src/main/res/layout/feeder_list_line.xml new file mode 100644 index 0000000..41dcf53 --- /dev/null +++ b/app/src/main/res/layout/feeder_list_line.xml @@ -0,0 +1,44 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/feeder_list_rename_dialog.xml b/app/src/main/res/layout/feeder_list_rename_dialog.xml new file mode 100644 index 0000000..47cb129 --- /dev/null +++ b/app/src/main/res/layout/feeder_list_rename_dialog.xml @@ -0,0 +1,25 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/main_activity.xml b/app/src/main/res/layout/main_activity.xml new file mode 100644 index 0000000..b6480fc --- /dev/null +++ b/app/src/main/res/layout/main_activity.xml @@ -0,0 +1,11 @@ + + \ No newline at end of file diff --git a/app/src/main/res/layout/main_fragment.xml b/app/src/main/res/layout/main_fragment.xml new file mode 100644 index 0000000..a72c0fd --- /dev/null +++ b/app/src/main/res/layout/main_fragment.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/settings_fragment.xml b/app/src/main/res/layout/settings_fragment.xml new file mode 100644 index 0000000..d6b9e87 --- /dev/null +++ b/app/src/main/res/layout/settings_fragment.xml @@ -0,0 +1,26 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/settings_number_input_dialog.xml b/app/src/main/res/layout/settings_number_input_dialog.xml new file mode 100644 index 0000000..9d49c39 --- /dev/null +++ b/app/src/main/res/layout/settings_number_input_dialog.xml @@ -0,0 +1,26 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/view_action_adapter_line.xml b/app/src/main/res/layout/view_action_adapter_line.xml new file mode 100644 index 0000000..ba76811 --- /dev/null +++ b/app/src/main/res/layout/view_action_adapter_line.xml @@ -0,0 +1,50 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml new file mode 100644 index 0000000..39ba1b6 --- /dev/null +++ b/app/src/main/res/menu/menu_main.xml @@ -0,0 +1,18 @@ + + + + diff --git a/app/src/main/res/menu/menu_settings_index.xml b/app/src/main/res/menu/menu_settings_index.xml new file mode 100644 index 0000000..c89d2d1 --- /dev/null +++ b/app/src/main/res/menu/menu_settings_index.xml @@ -0,0 +1,12 @@ + + + diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..c9ad5f9 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000..c9ad5f9 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..d02059de48f0e42d68c3d04c5891c020f165a04a GIT binary patch literal 17338 zcmWh!c|6nqAODsjjOmpPEhRKq1 z=AKzLbIg7A^ZVoT{`~iOe?E`*=k> zYUpv@F*In`{-`pRrhR#2aJEx7nDVG>=lMi1q_0?J{+?Yx_KR>(M0w-X^o^Nj|L0Lm z@3 zSoPXbg}wVeh+qvO-`anVZADHB6{n3)4?RQRZvq&liyMlzG9!>n4}Lh5xXrmT^qhmD!+0t3?^BdqK^XYZ#F$CLHlw$mcL z->TVuDrM>-1-EgX+X3qADKX(NY`YL7BS31S|MZGHw z+LZ4Zf84ZAuIyEG0W@K~3z0q$jGU`-L1eMsJ%4$t`nngo7v;tfAWS^->h$SO1B}dG z^?PfsGLwPiv)hE?UBPD(FUDoNgIxXcX4?xj>kUGTRLo$9)>(u2Cx%}xxgtgM9gz8I z4ry54@9NCEHbxeJgsAf1>3!{;spbe7?N`D%Qs1Ga(OfstMrJBHM-a&h9Vk&N-saaA* z!_lVAg}up`L0(!RD&H1g+ZJWemtxaURu zFC8fz$G7`Irzu)V*T$o>)sFu=TO6R(EAy|JCyjV_V)`2&H=RuRG<_Ou5tati7FXGt zUa;<*!-117Jol8s-?Z8rZ?>jH(rj3;5&ZXiq=3VAH2o&Zs`CnKm9zImTbRXi_ahMg z5`F}?)*RR^p2S6zsrU{Wn{K{bUn=HJj$uArVI1nyv5HKbVm(3odR0*_etqD1T z$|r@=4|Ed@3Z2F{(}MlZ$c7XU4^D=1bWfjoPY!K&mnf#Bxaijhe$J+dKtB?!6<9Ik z0*HHv$edoSW2&E_22@HYNimmZ4=={wnYD5I*lT#YAaIi07X}KJe+CdKEV(=9>NVfw z+Z!77uB9~I6J)_`=2*xPw~L-O6pMe$y8Jo(yd{lOD)Dlh(4*GfE^H0|I7$yvGBV~y zf$YZQB{VLIOD6w@^;SO-rGxUW?$r1sXiSbO`HmwXxGlnii?aOk99-i}l_*=i;e0+S z4j7|X84PVwgTE#QYH(}vjkIWh5{d;-wgNxF&Y@>(a;!Iou++^N0Lm;nz{U+H(|IR! z@#WnY@i}q1_3N*4W-R=;`V){rXDlQ#zW{t2>Yr0(-lPTpsdXf#Hy)mTwc#eI(9|)z zYaZ>bw&91Un0#@op?k}={7*HI(+^S~(ok0)Sm5Rwl5Mq40IV^m4V*zgNCM)pEE`9( z--FR;sPwa1gFfXT=sf<}lS{F@kBsy#Fsx(}C@lfL$VQp)xwPEN@IM*yP-#jo+G=}_ z?W%RxA-*u7B4Cw(~$sbN#RSq>ORAqYoFIte%ekUHRhO4bm`;d0#{l@$$#!B@DM={LbyY$d)z)B^db9nQ$EU_6 zfw)LpQ~VZVbL818MUKSp>D?u4QWyGo;hNj#e~ zdcaIN_2LhlHn|_{qQ0&Uc{ zKr9`>e%EgXA12rU+5A4fZZy#?Gxy{b4TF(<+Bgn4=ns<2Isi3v`~d!I0b|@W5ZaCs z^>Oxhjqm^Bvi<&iN*ABEm|&@<97@q*++(8;+IIg0&M{l|$A?Qkv`22e&dXNJ43HKG z{sb^+i@1=Tm&V7u-CAjgFIVGZ`X7A5L4EZf8LCp+O=PbuZr~Axt6~1$-voQ*-nCND z_doXlU zjM;RW=~qOkiq&u-YkD~9x2NjLHOTp}Xzace- z?T+L!S<}rN=To_=%f|$FhNu)N!zJtI)E&7({6Y^pw=>W?j5o61jhv4UZ>g)3Mj(Qh zF3H0Chww^Osyl%l<=UUT^|Xhzw}@7fDxPhd=#b*Pkq3P^&{$ANpMR)bU$~cGQcX0jze%Xk3}v1cQ&K5d6J1<2CAOtQRZ zdzj*rbPzi@EhKRCm{AVOTUUB71hJ`V>v`YWwsWpje9dt}Ixk_~FWJrEV<-0E@iFn= zW6qmHhlSVy#FOg0!-Qt#{9g9A@v%aL7krWHus$^3IK*7afvTx2XjS=re{o?)yEVP)>4=O0`{5s^%5X5okA zEN=X`Z~b-rRB{4IOHHmj@v^C;rDKtae&c!tys85)XU0`5v+80}b6eZ5zS3IXK}r(- z#~%+s7FWg$+CXUq=)=qEd(i>TCH5FfhhGoS;*mff{&P*K9REHwQ@+NUEpq{fB)sZ;X$e@FOf6h(um)Qs4XauSyn3N0M=RSiM zhFVYQo?VT14gZKfL8~j)46D*pq(BRN{*>^R?zU<1dw6aj98T}4=z(xFZjWiv3yJQI zAjvb7uPo)hcffDbgBe_7i*^{?>SDt&8qo5;y{bx`98p6qgS$ZnK!sYrue;|j{=(RP z(X|OUm~LDXtB8+%H|LW4S!K)XtcqKNN`A?DM|gjEq}`;%gQc%J8Y*s1q-Y|HUYKn= zDkEeN?JB{sm9SA8sDy}kz&cL-5-=D0H7L{2QMoAOz8p6~$6XCKK5+3Ovu81V^Yc8? zXzA3;VTt%`J~zMkKt@l8tG>E41*e)$F7Hd<06@??CpyiPH!!85LKIfFYNLsWf_d6C zYh(@|H6-AIfSUv*NG3_Ymy+a33Px*=qG^-b-G1Wu*6DJOj+!ggv9UYrXhwBQ@c^zo z%H;c%M-pWOi8FY&oDNl_6}xIJ;P~)J5LA0GTasM=l z+t7dVqkEx$3b*A-TU_VAXd%L9oc;CGkNaE&Z=4cF_?GyOI83vA&kl_pu(AlWi{0#= zP4m~1wk7e=6b7z((c~I!IF@+NQ4pE5k7G_+JvBb+8`I3k=*@5TH_b)6T~2(6a;tnk zk>QFli$=iYq5?+ywD>FnmEb?_jL-%$tYx3|RMR9s>pJmE`g@hP0_1EYa`=)*T*?<) z6gsmmsFO6Yyo(9$&JsA{5K%qTHOJntleFOP+&b@mnP4cm-&WZOCKqSzIpsOb8eI_B zAshpf*w<`+iSHQLPYGG1wK zn{)?WzBffL9vhCN*yqWcVy;$-3r_itaUQ{=!(v1RuZHlK8Y{EZ6|kdt3afx8-IB z_@t#g#Wgbk!`q`9-iIMB+H(2CwfvmO2j;x{Znzur#zo@3f4eh4s5_3R$>L99A@u62 zRv}l-FFT&JBRLBeDa+1d@Bb1O(2vQvzA{|_8gu0#p*ALO_}QbY|E$ebs(!J~Y*l#s z4^eF7QZeq8OQyNH{QS*_w1y*kt7DQMhbi5BL^F*k_(cxd1f!@hH;ySUJRc%II1iyW z=w+}KF8A8(@6b$lsR0@{nG%Y8TpZYR7Fh;6R51y~{6`X@ybmcy znyW3IA=dDrgZ+S^LkottdUl+9{C*D)d09 zD<6IT9qDLaDnrdIJAaR8u;3_73(Ou)87rXRW$~Zc0y2S|Eqd@<577gmh(T<&w|_8- zB*Y}Ul3xiGYMYBl&e^O2e@Y^#ul*eD{WtmL_UeoS78DbCA# zZuLQG@3ppL?nk3*kvjhY7XXl@3ww49UhTaMsWE>Q6k!8`q5XxYD^MIVda=An} zIZ@?Ff8td6Vv-?p@X~Vln9?$GSQTDcr*dY{*!$Vr_K^DU6}w*?<>*3W0IZ_dExzuB z{X%>f3Q)ms5vaz=L(Phpx#akFH30QNi=H1`LQ`q$7)gD`&=enIzofx22lyB8 zRb8+(Y=Aqq(l(tT?)%vB3}w~Oph-UGU?B1ZY0mS~)S6xJ?qVh|X#2sJvWc|D&sl!} zFH8=x79`Y%itBQj#jC|%hv7PQ@^U`EXtkB`ZL6Bn%$|_6-34sx_NE|Xq|N~qybimzA?;l<@Nx~f#tTVf2YEr1TyN@R7c;C{&JXBX>Q7-jN|6oYtP5@7R9ayBA zajop0k&lWHDgd}@?-7ccx}r_FqQOaVk>Hth4y}{~LVP50-;NSE7^el*B)FAl5?_=o zC&wyq%NPdB^XD2Q>2Jw&kIt38e&v9?`iD}ZkRG$#tr%(yb-6GDoal>7E7_-Z)>ZVf zS`qtgmu+v#@dza&0bdq;v&}c3gI6`fH(6QPy~Se6+f)CgyxVx1t_(TiH_*+sp3+ww z3B_5qqGJjMaT1lznLY$wyZX=fI*THBXrJ^$vLMss7VW32jcn!;77@p{zSX}QubAy*yG+O+ZyJEkg+ED_f#gh>Yco-G8+ zxsVH=eL8ZG1IJv1C2Q0`Gq9k=Ki5fwI4`VsHeEV7{OC2|z`bN_Hoh9H}qXgWB`(gqVGl+0SX8gK!Y38Ga zcxH`|n!F_kw`ZH`POSMpD)4g3!O{Y*wxEa=Fwy{+cX)tekil zd#G@DxJuoceR1`>R*B9f*FmHbjSR`1LZ33bg^5iaJ`1!1|{h75jU|YU1acq zwt7asak-RT>yWteX!O1M#sba#$u1|nXMy;V24@<-_l#b9uPZPrQzk=+RDMxbemPD_ z=(o+?_%z0-K~Hf2cz+IQr0^zv%Q-~u44QX4YJmqPxqFmZ`KXy{wkA0!MIwwCdzl7I z!sILjL$n>R=c6gBttgkBm12yqP@|}+t}>~$c4a2NNXwszxn|sYMGWr-5)C|hk8QN% za-H0A_^{JiaIWa{l7HYorUp*)fe0Dw6wmPg3$rU^jW1uMcYYV?iHI2Pxt8ek*T$Mh zp$`L~!<`R<#{5QT^UO|W1)mp6s3%_jOgmQJ6Wd4z@uD^o`9anVq_i#Wy z&!Zord1b@*Dyn#f*7>7CtM5(PO5=XOFXw%8ansHK#J^FCHDEdP$t3~7K91cnIdR}? zKm9Pf=y%VXos?-8)T7c@Z{7+izOS4iRh<>d(cK8}g#`IN>?{7-GE*Z-%3^G05FN&@}t;Nx?&R*GXkX z_Uu8kM{TV`=S$9*_od+2ny=gK1twq0{>hUt21!LY%hQ5=<6#eA1ktv_xm#P0SFhMe?=>yl3I-&Vtd461s)-F)}t?#h0lz^+?Zf7uBMEuXv`CETwU zbxc>xG9<@A$_@zTS$E5_!G`~oHyCuNfnCUM3Td6&=-$7y8}c~p;ti+#kUQQ#%4#WJ zES^|-a8z9@;p`~bj*9usGG*Ei_A56z^~=G_{+@v>(_}qXU)dQ34p@R(?{4qDk5-|K zE4mFNz2TQJQ1Kz?_)H{4CVBlKhgz$hkiEja*BTh6Wp6&|FmM@9J$ZL-UL;lr?!(VP z5V0DT=vk#%_v0t8v>>n*$W20|HVrU*QTDZ&i;*)##UJA>K72#+#2VE?xndwbI5KE= z=HFP`$Xtp8@51Py;y=d`#pTikv|+@bOA-5D^|OD4ocW_-FJk)rgi=?Y{NSjbM%$`W zaM)8?Wm#<`@2+j!E+;g6R4t;Z9x}|5PP7s&DsY>)v_sfEtY8Op7fWew9zUR-s-|{G z{DeNAFPhI>!2)@dXEq=&<815JZN`Q1RJDUqwQ;m1)4x@H-CT@u$~%iosMoC-F%TS# z4z7D=Y7{OUj|4?vM2Bse0q-)3UvcEi2pthX46~=BZI=f#xzgv^R(a5_N)CBpj+c~9 zD>dmn4KuiQC5sQ$70uDcreR!mVDyTTu|KEeS!qqkej$9L^v=O|>0r6U^&ij~6%m`k zoqHRB-<4rNITwlb!Z$lED=m+=IHu+z{GV6#zT1Y~4s663EcMgf zF~&eq63yTE7rk1%>dM~lC;^7Ea52O}XQ89odlYG-*dxqVVy* zNmDKHC3!%7_oKtM7X<9~7c>{n*DJ%zb=Aw?UY%)btXFXx2~Tzk4_7m6+Ah2>J0TO1 z-1*mh_34)&;6Mg?vO*BESbu4``~%?(NQl8JUR`5D(L~AhfKeg*X{!$QeTvDGj}C87 zZ`;fVUq{k#^LlgV!7ilaDj#@SbF3}53x)Zc-{4o;b1tcZf_qUGgkPbuZKM;V{kti#&<+ySczmac$a%2HZx&_zHkQcLK9Bk|z`upq>}# z^EYbBH??o+3F6l(T%{i+M+;v)8?biF0VFI}34$#j9SGMgzHXFr_1fP=NdvO-mNbyZ zC`?5M{!dIzG^8_5^n;bC0>^@h`$#M|V0R+bJbGxQuVIfpM|P`4TEYn%h&bOOQO=1`BYaO)&1pm zWSC0_CBRkX?7%6}zO*ZbXKi|J{3(shrW|3~#$n5YrF7oVKD zhbqoJB0XMR*%<{)EM*mdv=u^83B>+^C@tgxYP>dIUE{CfrQIM8z2pi%xsvFsl?QW= zYfJ-(%2KO5AJ;woc8{M-AxPLBDK)i0uyRk`u;0H#MdROUm8DS8pWv%5g!|ETf!&ct zz2p#HB00p__3AYXfbJ&j9#@Hgnmh-)zb_gF1S-iapGwcTZR<|&HY0~VzqQHvXz3JX zyAv4|8Fo7|Tkaw+e(yZnx<SiwJEclk`((B@h$XLc+E9OU?2dYkbP(CoA(O2 zh|(w+a-+{)j9+=i?HP}Qs?Gg|)W2Cnpr}>*pt5Cj{m)&5pL7)|v6TOmWhdLFV778> zWWh~ZX6l0YK&^Wawn6Vb*VxDI`&W))!cv5$StEc=OSs;MAqv`x}ur?W%uT z8t}`H2fNxgjs`jgN;A0Svl9IsY@-}*YoC}!{_r#%UY1fZ{pv~bj@g@wSu$IAW}ntK z**$Cb8FMD>-QCuumaNJUTPNLHe`)RoJLA}W6>?&UZh_ZHxP69vMtKkHrInb#!M$@u zKtoAGeEyF0vBwo|%389zsOr}rdA!N|tO(v3bxEhSZRo~q$DhL8=^D$)ov->OiMF)0 ziF`J>%!OQGDCoDR6A~?X+Q_TxBg?@hYbkBxr&`Qa=mqD|%7Bf*HCi;N#dG;x3BNJ} z9mRoQ@7LHbHrN3378tpF#h-n;1whA&!o~R0a|MFEtZj8AIth0=zKbb9Nbk?Isv=!t z!W$(!RsTBKeL3;l^4X(5`|z6tF0(goPxTxF^6$$lm4B4Xb-~U%#{YO_uB}$xN*t3Y zIn;C3heUD-9ndQtv#!Y)I`msQAJ%)=A2x=YJ395tywkYQO7Ap zv2j*5Pnm|w5Bc`&9NoAf3C`C}Id`h|V#)ig%?%=UnLrC84b4M_R?SDPvd;uS8ZUbO zEbBhB%Aa9hyC?(a4_NRgq=7KD&c1ai#yXBBHvY{ z8Ao)w)ojb(Zi+9^yELaSy95&*scM7Yw}+u#9%^7 zO_ys!>yM00=S4CS1`d=)LAHkFq7eHr-M+<`)VST}5lzXybr+nSM&IvkXj+cu@#jdV zI7B|BwBAR$f$uaK=w7BII3@Ax*rWNi*SltxJ?F-0O!iPCkv%-c*Nx8yv3DjPW>%Nj zh8I!fHhxi#1)XHW9vo^_Y}Z(a%G3Pj!`gj!l5!&B(r(gVmfknaq6tE#dPEh}zijcwo{g(<@67LVXg3{H5zpZFNMYU9(Jzr|eo@ znOEuM&@!)K;C!r6jT*O6{&<2mtY|v+QG3GZtQ{gU@3xck(=NlNnzLQkUhp(h}*ak?)vW z+6B~N8TZw6Pg-+zKKp=H@o{rOKXxWr2)}_tR?jUn&;LrlT=lC|F^lkOj}W338VnBu zqt{B;l%%J?a_6M6K=7sPf7e&`?K+{x4xuHu47jMC^J^#>5mys4^f<>tVJQ_+^b(~I;C6D%ey{bb(=wG$%a}m zOi*qo1vUn9Xv~Pj`!OLj*oCFHNA-YyY3Er<7&wM3q zi>SyXHJudHHiq}aiQB4O-rt<9 zkp$^j+am&FO2htDyLj=5W^c@NHh7pMl#KZrz=on`&>*QkwAD;JK+Utx!@WZU2`$|j z<(v#t8skcNj+`~9h$p;e0m^JWf-Q1y4>fw+DM_*8(nz3X^D^xSN_27zYlmN+1$&ItI7 zE-!18Uh+!ryL~u4{gBt&v zzP7QhDLwu%*j1b{x1bTkXD(lU6HvQb*;IWRQUvbYc`C~N6;d|&CV-~Q4iT}>qqk%@ z4Qi?^mp7hM<4a-7Cq8KMw%ymBu{7*%AA)pze`zM*(=HmS&>E~Up33zFx}537PA7%D zHvyHOlc6jui*$M)PF!!RWX;{*VSXgC|Bdi;y=UcDvbG<|WkBklaExq-7j=aIk1H_JdA`lc0@w5NP+)Aby`6WUN_G?wZ@@7|s zmJdMhVSQnK?)vCl$ehB{#(gvwrp|pXAO3 z24A+s7@Uz0leZdK7B)Qd^7GzZF!zS{M_<4~R?-iyXqsj2dW`jh+@tbJ*~JfiW~1ZZ z=}+FjEuTlvz84u|>tCW)Ufdb!)Fg**)`gwLv(>kp(l|T=h>cf%TG(?_ww*7y+e>Zb zeB^=bzS-Xyn4B(xFN|t_*ISSt>-T&A&dm!x`#3gZY>1e+^3n3$*Ka1*93#O_TzLBS z=mSnlh-PiRiZU_j+-}dvupDpj`dsY(KJm;BO|;?QP-O%@o_=q}xTM@4DWb;fss5oG zr?^{euP)0ivwzv@YIU5bXd^S52Z8{KI!b0Fkv89)xv%?gL8Jt3Lr_(m^bNj6cf4n% zKT#G%k};6JVo+;$*2d)E_{g7MZk4N9vA&Vwi6ED(;Vt=>GW^PO*l(}ny|xm%Wo!aO zb1T6hFyY_HSzY7f=w$^PR)wmrD37DmL}{&XiC{0itEX6$p{t-XNerFq4bVtpGPhjv z(&6H#3PPTQ#mv!jBlwK=?;J9l4!!tDQTr`ziXBiRbw$hL;>(BIrV3>fN_cAA?lY6s zeuoP#R7{D!uDDG3)35`nj~=0wtQ*0r)~{+Rm0|;kL$)oBkV(bjt+zyCQpKg1wfPo* zmo%W1saRifXd9!3KA2iEZg9CgMzvA*xo&Qi??KJ+&p9!F=uDWPQp` zxFWzyQ}4Dp;FVsexc}WGt7q$@KTYqeDrN}rjqLke1Cnht=mJP$8gY)|ZC_LdOUIQ@ zdFhG0)3vXM1nq_IKecdie=K-Ke8aYOosbxQx_Gvi#ieyEuz0)5C?c&`zvT69e&!VK zcv=Mfyh^xl1GxKr)|T(8`Jb%+PHbK3Cw2UmDD_#>Jd`NjbzlABn+Yf@rWQ!*UKOzE z;3f;(bp9J3y{rbb=g+gw8dX4*&RZQHOL&fkdl7;w0au;kqXNKW$R*s=EKZnsTnzB! zn6f)S=M>FR5^w7qN?-qYW#(3$Z|vb5;l}FOCx++aeuuSFltbp)w`^(TlEd6&QT8m| z>9_#({nz5ThR`hz>8{>ids@1AO?Fb%vp&co-yq{gR;`ReL`9HiMcBo>@I2;j8`9+* zeC+S@#%tp%h74W^yW)s;T5smmKFO9Ek**Q(nb{oZu>Ru*D^7@w(S9h(O`vER;IY=f zi;@osXrRWsS#8AC-uI;;R$?KVNTbWHY$~A@Jw!qqut&Ffb z&@9zkuJSoCF--7JT9!m5;@)lTC05F}8&yRl3=xLa>Wf~RE&(F-l(!#q4StE#6Wz!7#6 zie8)dd!qBNvilf8W>?_9hYWd25-9Dvm9Tc|Ro9`WGSX$7OkU+VRx$+l+ALa+fBcwS zu_v93!+4vQeK2c5ndP&c4X zykxu&6T(+p(L3kH-})^uq|$%Bl<7Ly$Zbu8t>aPznsC`QMrv+?{sNGu1kP5 zrFAa0*l9=8`R=VvLZxR^yPNFuk4V&0E#lhy=!%|EQE07JPX%8VIR5RsN{{Rc)rP&D z#IVMfx3nX!T8Ckg^wrf_mzBO7t+OW|A6$yLEmwKz>3$uZtm|gGI~oRSdBea2 z=rC>Z56<}qudeOaHUo(COm{%9Im&&o{^#{#tapb(sJOGDE)6&~vV;9+Uket_$`y#g z&Bz=nq=v1VJLDzqo!yFLt?Z(8z&Q7T?15=YHg#2BaW~}fv+-(L-|9C=M-<~;!!IY8 z*x<4RsVli6M_t80S=vYNA#*eKmG_;7?k8lNOq)V7GKftk;nayAtBIyVwou^w*W%*z zZjazMJ!fR+@sF}!p#9o_;`6JsZ6Ro^I_0tZM+TsDN98{M=I*!7deq@8AnS0vi(Tr* z9=hppNzr|ILAi2yJ!5f5T}?^`?M5$}yR`~id2+M0uR<`8*D*yVq)ary{IKqe&NzNA zjugxK0-osX(W*{>*2b>w1AnF;+Y6!EPKMuml&MZSYDnmfj+k~9v}xsm1v2An(|*JL zTuTcM=Ikk6{jMqYH;#tT;G|}n$(&Okm?}Z#{%c$_D-WyDgw2E+9~URk+IN%@OYBtp zK})muS*!8((lr+eB+ITDTnb_?#;bB+^m_2s@hZAL{rr+Bu?7ii+vLf)9zsh4Ck8Pk_R&(qZn%2$~L@{ z!9Bat-KLv^qNr+4OO=XTpPD{?UL-XC6l$%P1jDubM*v_+urvQ@vpmJ89< zd^CpJU(3U>Wgf;_jWs34{cjJe;=nQO>V(dbtma3S?s~cX(+e>-E(~C^9$%oQF7mnq zS6IU2fZdP#J1IW>Iv(rsPvQP+bG<|Kzu1Y%ISn8r-bHW85Zymg9Je7BML}&!TD2%2 z*{Cqy6aOr}Pit{VbT@a>U)Xm>P9K{NVr2tBPm4s~So?L)`;jAjjE7mdsS(j}zp{?< za;Y_ly^4BB4)wT_=yw-&Qc|}tmxVg1WzikoW=s2(HZimRjkLbMmORv=RgQ!eX=BW- zeijTKQxt}ZnGiM0@vByX>n&onL98)|cqBygR)BxHu$-gRv3@@NzlDqRLbNBK2Ux*> z9ICCM5a$&A{2b}HlXAF~@zfbfmBxxP{=%oHuG|T{p+PI%^57P#l#V= z*4<$S$ zahkLc4ee^eD@x@&{pDZJg>S%}67p*e0T7sqXzp}10Z&bto}^vLMU`(&%^QcJ zNOk~NYAZJjEu~m4?1UH*KG#&!E;MkrtOB3GYk60Vb2HK#l;L>r2(s}3ay25}cFSDA zO{)aNIUWet2>T%1ID)lAqn=Y3);zMW%7w=T#f;?a%i( z?u295IwGn&fXo%96+X5T`G}VJ&H98R6-L45u`^T0Zj| zZ_7~r5F8h8pZv-?XF|3I!8y2(i#YnMWXk>PQDYZR-F0j{LBLeMz4#XLE-wHKMrO!!vmx(7epE+HgB=8j zTfD_saX{_s*cdGZ52zPDj>AHi!F%K@?eY6>v~w*~JkvJDHk}uJYfXg2**qs1MWw^U z#udt9C~eT3Y`vqgjIw+S(Y4dd_U{VTnLo6LhhhvomXicG{^cKG1~g~kKH=N$Oy9pc1KA_4IpR?PCSFZ4XCYW$4t3D%$4G zGrK&dh5N&BMvpl#4~Dbiy#XR)5Xh`!{BHU~ct+tr&aIgK5698@+K?Tkk_l$qfyp%WJ;GdVS zBk`t)L<2pg>T(u2s`UAbQ{8yfaM>H@1kpGyavy|n zaZqTpKI{DhXkrww!Y+N4;H?1#=j%GrQwC`6juItm1JoM_PLa&%Fo%OL)N?Z%Q!r-p zrbrxXRD=dg&^FiL5Y+Ir0?3`mE~<2H4ix;BFo6$LFOFpNO_dUOCMI^gxS1S)gKk3pVWEa4+&jW-9#0Rf$Epz`SN zCa>$J?7F*aU^yEK{q7hwt{*>)hdZVbITqC5BVgAP*Y)LGzg@vktgpFDh;1E;LXJ)q z6H;(}#R+be@vIDI%w$|-IJdx$NB4v88H0;}ncY}wKuC#|cR$LT@ybubMopn@v4#cn z3>oyBY=)w-(r!PJnFTIC!O@3{%#O9`B*K1%(Nhv@E;SM_EGwH4@8OoS$-dl{@x-OC zbB#XgD51+oAy0p5=;pEa2+o=0)GWGMk?mb;Pmg1#V^@0ux{?F}%(X0Sif(7#GwMC^ zD@yeM4Xdz`&Y{dtq6uq&tfS2AG7+Gc9bdOuslJB=iZ=1VNUJIdZ+g1z2w!nfsUF_# z!_OubLyAUK$F5K3XX0}jsd=2QKT-0Yx8zQ`C&FsOQocXd5jhg zs-NN_0bV9o=9q{L=S$JW0f0P&*RZkXF@y#v@EsA;yEX(ICWchC?EVm9RdLZ6M^wa5 zQbNPhzIb}%CcSCL*F9pUNjjog3cuOl`Ewg8)Qlz( z$u=*JkPqN z=5LN#ZaLqLugzHW)Lx81kfu0xT_-u1SPEt{*-r@%f@$%8lU~z+e)b-K-HH;6vfsw@v-6(}Fdr1D+xEuO zpYlLaEQA4AhR_wMh;|zyXZFI#oD{;F*;FJ5fo&+N4!hZ2C)__x&X97_5|ci5R{NB_ zIz@8~J2NT`CZxE2OE=NycjUobCK(iMxij(+J~28wVjNbRG~JAOw!Kr1WHszQPFYrv zM5M}J&Hw_kvKtgfj6;rqdz5ptTC0a_OdVYCUI9ssM5aI=h*J>uCn@43NHP93u(6W@ zS5yE%AHUU@4+|b}1H>%uy#v_!K#f9A82V^Bi?5A2qgq(bIlG_wTzyq=YqMFZ)oMy)VhWZ=g^L4lH_ z0Y67-MQMleqYAzadk@3D5i-so)OgUC?ymhGAcsycD0K(;I*bXJ`#%U})*L&i2p^=w z9`tQ~rDoXc(0O$qVhlu;MWYmRy)(=KPWQbpmEe9wB8Q5=0>Q8R-Jgr(`fS!rP)~eHP&ApIqq$;viT*bvvLw-2DCXAe z)1H*x>FtuWcWr^CpEOnY1Vw1z2)4|kDy^Pt{pP1eYgkoFi;Dx_d|NQeQ;AabdZ+4E zpQ7TxImOojN9rYA?*Zz~4e^nqF$%+z)xrVNb|9%_0cd@ljbq4kWl>vSzPXj8zRE7KbCIhs`DjmvtowfWh8-Q9` z9Pu3yykZ-p=;>rx6y*WkTxD~X@)IS~`|sv*2z9!D z^%~LkB&;Snn;Q(fk=Y+@CL6I;sLL1!HNG)F9{fO`OV1`4f7A76`j3ZCVLzyP<}uj% zj8UIKT39{si|O(5akY%IDs5UvxStM^8N8SZJA+QJJ{9*B_`8$axw1m4hp{O3eIUBp z&5NTMF&G-G;@=Z|)JB2Dq|dJ(!GFmbwArYOR@xhW_XOU}>6aG;*w9fi^r1!hQ*v_b z0!Dy0pumldRp)!2!^^CV*HV$w{c_VSL;5I=F}%Yr!4hGdV-@kpn#G+@IigwKK=EWw zqWL+zXZ`no$IC31|0ZT6`N~DUihQShD}2W^GH-mQp7V>;CR>GSR)X7tjXLSic0Wv5 zS$|3h7tt@GXI!S%iNoMRQ7i6$+X9L~ogLltHJ6D$(#>X*3LOSJi~!}Y*V6g91X1UH z@7JR~%a;Fp)xh5FWSawfLq&z8P2s-S5(5=t3+!k#>gchKJt01#3}3q)(+^R47IVCs@5KQ577!IJ z9hF5Sa`5IN(eK48K=l3iBgwQI8oGH$pM;@)Dy7G0X1*(ZVXh}H$pk^;buNz>d7@dE>R; zxJ?~kHsG)7%c{{Y-o>3m{4BZG_hZUF8E^}Ch$ksNiIj)kDG2OJ###Tg4})>=>5WzP z-d5JP97dH%UEoF_2K_KI3r7=RZv;+ye=pFqdzK&%5qor-!A$b4su5qKz2V*&J59f? zp-ey38tBL%`%XSMlACV2=1ulsvZ0a^NC5ozP2-5+tos(xb#?U}#>ay-YkC7u%f)aF z{!029oO!t`zW~NKTX$aVw6gO}qxDNa0nN3)_2v3X-$?_&(e3U}2_VDNd^JTAL)=Ui zALhQA2;G~lu>ljqNHaHGE3>_Si$L|h*^ksi-+!KU!F8}-xnlNcYu_VS;TE!CAy)?byS3+Q&^wLbT9jQR2mked+(TqTsaSfSXB3Tfso z$Qo3?3QpjQYdCC$xyd9F*fz>?E$1!~78`B&^Is*RZodT?A-36IJVZ`j=$fCTC=ro_?uv-W$=lT< zY)}~}&$Wf+I6-X7U|XATuUrSUMZ)|V*kocJ;n||x_vLaGOb8~7OIS^p!S-yn3YAyl zYI!)`kc+dXp8wQdZHJ4`lXW}RkMndpt|cOqfaRWE2L@$0=`1s#Ogqj#mPa4-<@g*I zZ7{wUqpq(1DU3Ll_sR%t2kSf-adKetdB8Is`(WAf-mO`63z`=lypSwhsdg#h^oGpa zazRAvIFe1vIH?|(lnjgE#WG*CVLK~Zd(Z}T92@nm!1ZE1j5>SI7YV_7m3ek`a+`2Q zPR@EJAj|n&$1&!%@Qz4Ay*cAbX*X^Ll8s@c%s#&HU<1x1@@(pKY{zmd>j1FM%c~vw z*46E$IT^y*VtFjHp5>-P8=Gjh&-q!N38F9(6c-jcFBbWNAb?`)#FeZ(8`?IEFF2rl zVX(7DHw6)!F>y7-RZ+31Nj4^+kx-$lV?jjiq)i$V&9-i|L0cp-+E^Y3m~~jL(|fjT z&ob6=Z1$-mh6m4{M}B1$5razu4?RM?iQM6@<2NtJ-OB^M+t z>Mu!$X+lIIp^^?)LX*3ntY`z6khzIPl48m-j+Y}h)`PKfwsosH`(n9H&P3om`k>we zu3~1-Wwk*auY71{X>xl_R-}?xxKiPbwRolnE8$$sW#uO)9zm6WhQg*o@REeZ`EX@P zG$b9%imllO)hhpi9|qRrbL`0%iPC!uE1v060>)JhVGpI_t+=@z&yg&T*PEA_xicaUKEeh* z2OB_^d*D?O{Y{3cO@=4u*cb=Fbz)s!JzgwNmID(DWlS(88S6C9dR(dIE*L;KTahqd z**f7IiL0QI5X;rou)4+a4LIF;RTqH-Oi4K^{|zY#M2d5dHp~D3002ovPDHLkV1nhd BO-TR% literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..4350d52477a7663404e8a4ce3a8b304f218d0949 GIT binary patch literal 7357 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..2898f2d7419c822fce4b69fead9787a87e123958 GIT binary patch literal 73429 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..381ffa1e70dd890d9278caa76ea84b84abe83669 GIT binary patch literal 129454 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 + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml new file mode 100644 index 0000000..6949699 --- /dev/null +++ b/app/src/main/res/values-night/themes.xml @@ -0,0 +1,42 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..7333a11 --- /dev/null +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,58 @@ + + + #006D3C + #FFFFFF + #5FFEA5 + #00210E + #006874 + #FFFFFF + #90F1FF + #001F24 + #006C46 + #FFFFFF + #8DF7C2 + #002112 + #BA1B1B + #FFDAD4 + #FFFFFF + #410001 + #FBFDF7 + #191C1A + #FBFDF7 + #191C1A + #DCE5DB + #414942 + #717971 + #F0F1EC + #2E312E + #3CE18C + #3CE18C + #00391C + #00522B + #5FFEA5 + #4FD8EB + #00363D + #004F59 + #90F1FF + #71DBA8 + #003822 + #005234 + #8DF7C2 + #FFB4A9 + #930006 + #680003 + #FFDAD4 + #191C1A + #E1E3DE + #191C1A + #E1E3DE + #414942 + #C1C9C0 + #8B938B + #191C1A + #E1E3DE + #006D3C + #29D682 + #BA1B1B + #DE4343 + \ No newline at end of file diff --git a/app/src/main/res/values/ic_launcher_background.xml b/app/src/main/res/values/ic_launcher_background.xml new file mode 100644 index 0000000..c96bf23 --- /dev/null +++ b/app/src/main/res/values/ic_launcher_background.xml @@ -0,0 +1,4 @@ + + + #688CD6 + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..edb337d --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,56 @@ + + ADS-B Companion + Enterprise mode + Settings + Help + Example Fragment + Another Fragment + Show another fragment + What up! This is another fragment! + Error + + Share + Done + Copy + + Size + Compressed size + Debug notifications + Recorded log file + Record debug log + + Privacy policy + Licenses + Thank you + Other + + General + General tweaks that affect the whole app. + Changelog + Acknowledgements + Documentation + Email developer + + Install ID + Automatic error reports are anonymous. Share your install ID if the developer needs to find your error reports. + Feeders + Add + Add feeder + Input your feeder ID (it\'s the short feeder ID supplied by the ADSBx stats package). + Cancel + Info + Upgrade + Upgrade to enterprise mode. + Not seen yet + Delete + Really sure? + Sure? + Refresh + Rename + Aircraft details + Aircraft + Reset + Rename feeder + Give your feeder more recognizeable a local name. Only affects the app. + Set + \ No newline at end of file diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..358fd29 --- /dev/null +++ b/app/src/main/res/values/styles.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml new file mode 100644 index 0000000..13d9575 --- /dev/null +++ b/app/src/main/res/values/themes.xml @@ -0,0 +1,47 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/preferences_acknowledgements.xml b/app/src/main/res/xml/preferences_acknowledgements.xml new file mode 100644 index 0000000..685cbfa --- /dev/null +++ b/app/src/main/res/xml/preferences_acknowledgements.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/preferences_advanced.xml b/app/src/main/res/xml/preferences_advanced.xml new file mode 100644 index 0000000..227133f --- /dev/null +++ b/app/src/main/res/xml/preferences_advanced.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/preferences_general.xml b/app/src/main/res/xml/preferences_general.xml new file mode 100644 index 0000000..b3a57bd --- /dev/null +++ b/app/src/main/res/xml/preferences_general.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/preferences_index.xml b/app/src/main/res/xml/preferences_index.xml new file mode 100644 index 0000000..f578f9a --- /dev/null +++ b/app/src/main/res/xml/preferences_index.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/preferences_support.xml b/app/src/main/res/xml/preferences_support.xml new file mode 100644 index 0000000..a1e61d6 --- /dev/null +++ b/app/src/main/res/xml/preferences_support.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/test/java/eu/darken/adsbc/common/flow/DynamicStateFlowTest.kt b/app/src/test/java/eu/darken/adsbc/common/flow/DynamicStateFlowTest.kt new file mode 100644 index 0000000..838072c --- /dev/null +++ b/app/src/test/java/eu/darken/adsbc/common/flow/DynamicStateFlowTest.kt @@ -0,0 +1,351 @@ +package eu.darken.adsbc.common.flow + +import eu.darken.adsbc.common.collections.mutate +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.matchers.shouldBe +import io.kotest.matchers.types.instanceOf +import io.kotest.matchers.types.shouldBeInstanceOf +import io.mockk.coEvery +import io.mockk.coVerify +import io.mockk.mockk +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.test.TestCoroutineScope +import org.junit.jupiter.api.Test +import testhelper.BaseTest +import testhelper.coroutine.runBlockingTest2 +import testhelper.flow.test +import java.io.IOException +import java.lang.Thread.sleep +import kotlin.concurrent.thread + +class DynamicStateFlowTest : BaseTest() { + + // Without an init value, there isn't a way to keep using the flow + @Test + fun `exceptions on initialization are rethrown`() { + val testScope = TestCoroutineScope() + val hotData = DynamicStateFlow( + loggingTag = "tag", + parentScope = testScope, + coroutineContext = Dispatchers.Unconfined, + startValueProvider = { throw IOException() } + ) + runBlocking { + withTimeoutOrNull(500) { + // This blocking scope gets the init exception as the first caller + hotData.flow.firstOrNull() + } shouldBe null + } + + testScope.advanceUntilIdle() + + testScope.uncaughtExceptions.single() shouldBe instanceOf(IOException::class) + } + + @Test + fun `subscription doesn't end when no subscriber is collecting, mode Lazily`() { + val testScope = TestCoroutineScope() + val valueProvider = mockk String>() + coEvery { valueProvider.invoke(any()) } returns "Test" + + val hotData = DynamicStateFlow( + loggingTag = "tag", + parentScope = testScope, + coroutineContext = Dispatchers.Unconfined, + startValueProvider = valueProvider, + ) + + testScope.apply { + runBlockingTest2(allowUncompleted = true) { + hotData.flow.first() shouldBe "Test" + hotData.flow.first() shouldBe "Test" + } + coVerify(exactly = 1) { valueProvider.invoke(any()) } + } + } + + @Test + fun `value updates`() { + val testScope = TestCoroutineScope() + val valueProvider = mockk Long>() + coEvery { valueProvider.invoke(any()) } returns 1 + + val hotData = DynamicStateFlow( + loggingTag = "tag", + parentScope = testScope, + startValueProvider = valueProvider, + ) + + val testCollector = hotData.flow.test(startOnScope = testScope) + testCollector.silent = true + + (1..16).forEach { _ -> + thread { + (1..200).forEach { _ -> + sleep(10) + hotData.updateAsync( + onUpdate = { this + 1L }, + onError = { throw it } + ) + } + } + } + + runBlocking { + testCollector.await { list, _ -> list.size == 3201 } + testCollector.latestValues shouldBe (1..3201).toList() + } + + coVerify(exactly = 1) { valueProvider.invoke(any()) } + } + + data class TestData( + val number: Long = 1 + ) + + @Test + fun `check multi threading value updates with more complex data`() { + val testScope = TestCoroutineScope() + val valueProvider = mockk Map>() + coEvery { valueProvider.invoke(any()) } returns mapOf("data" to TestData()) + + val hotData = DynamicStateFlow( + loggingTag = "tag", + parentScope = testScope, + startValueProvider = valueProvider, + ) + + val testCollector = hotData.flow.test(startOnScope = testScope) + testCollector.silent = true + + (1..10).forEach { _ -> + thread { + (1..400).forEach { _ -> + hotData.updateAsync { + mutate { + this["data"] = getValue("data").copy( + number = getValue("data").number + 1 + ) + } + } + } + } + } + + runBlocking { + testCollector.await { list, _ -> list.size == 4001 } + testCollector.latestValues.map { it.values.single().number } shouldBe (1L..4001L).toList() + } + + coVerify(exactly = 1) { valueProvider.invoke(any()) } + } + + @Test + fun `only emit new values if they actually changed updates`() { + val testScope = TestCoroutineScope() + + val hotData = DynamicStateFlow( + loggingTag = "tag", + parentScope = testScope, + startValueProvider = { "1" }, + ) + + val testCollector = hotData.flow.test(startOnScope = testScope) + testCollector.silent = true + + hotData.updateAsync { "1" } + hotData.updateAsync { "2" } + hotData.updateAsync { "2" } + hotData.updateAsync { "1" } + + runBlocking { + testCollector.await { list, _ -> list.size == 3 } + testCollector.latestValues shouldBe listOf("1", "2", "1") + } + } + + @Test + fun `multiple subscribers share the flow`() { + val testScope = TestCoroutineScope() + val valueProvider = mockk String>() + coEvery { valueProvider.invoke(any()) } returns "Test" + + val hotData = DynamicStateFlow( + loggingTag = "tag", + parentScope = testScope, + startValueProvider = valueProvider, + ) + + testScope.runBlockingTest2(allowUncompleted = true) { + val sub1 = hotData.flow.test(tag = "sub1", startOnScope = this) + val sub2 = hotData.flow.test(tag = "sub2", startOnScope = this) + val sub3 = hotData.flow.test(tag = "sub3", startOnScope = this) + + hotData.updateAsync { "A" } + hotData.updateAsync { "B" } + hotData.updateAsync { "C" } + + listOf(sub1, sub2, sub3).forEach { + it.await { list, _ -> list.size == 4 } + it.latestValues shouldBe listOf("Test", "A", "B", "C") + it.cancel() + } + + hotData.flow.first() shouldBe "C" + } + coVerify(exactly = 1) { valueProvider.invoke(any()) } + } + + @Test + fun `value is persisted between unsubscribes`() = runBlockingTest2(allowUncompleted = true) { + val valueProvider = mockk Long>() + coEvery { valueProvider.invoke(any()) } returns 1 + + val hotData = DynamicStateFlow( + loggingTag = "tag", + parentScope = this, + coroutineContext = this.coroutineContext, + startValueProvider = valueProvider, + ) + + val testCollector1 = hotData.flow.test(tag = "collector1", startOnScope = this) + testCollector1.silent = false + + (1..10).forEach { _ -> + hotData.updateAsync { + this + 1L + } + } + + advanceUntilIdle() + + testCollector1.await { list, _ -> list.size == 11 } + testCollector1.latestValues shouldBe (1L..11L).toList() + + testCollector1.cancel() + testCollector1.awaitFinal() + + val testCollector2 = hotData.flow.test(tag = "collector2", startOnScope = this) + testCollector2.silent = false + + advanceUntilIdle() + + testCollector2.cancel() + testCollector2.awaitFinal() + + testCollector2.latestValues shouldBe listOf(11L) + + coVerify(exactly = 1) { valueProvider.invoke(any()) } + } + + @Test + fun `blocking update is actually blocking`() = runBlocking { + val testScope = TestCoroutineScope() + val hotData = DynamicStateFlow( + loggingTag = "tag", + parentScope = testScope, + coroutineContext = testScope.coroutineContext, + startValueProvider = { + delay(2000) + 2 + }, + ) + + hotData.updateAsync { + delay(2000) + this + 1 + } + + val testCollector = hotData.flow.test(startOnScope = testScope) + + testScope.advanceUntilIdle() + + hotData.updateBlocking { this - 3 } shouldBe 0 + + testCollector.await { _, i -> i == 3 } + testCollector.latestValues shouldBe listOf(2, 3, 0) + + testCollector.cancel() + } + + @Test + fun `blocking update rethrows error`() = runBlocking { + val testScope = TestCoroutineScope() + val hotData = DynamicStateFlow( + loggingTag = "tag", + parentScope = testScope, + coroutineContext = testScope.coroutineContext, + startValueProvider = { + delay(2000) + 2 + }, + ) + + val testCollector = hotData.flow.test(startOnScope = testScope) + + testScope.advanceUntilIdle() + + shouldThrow { + hotData.updateBlocking { throw IOException("Surprise") } shouldBe 0 + } + hotData.flow.first() shouldBe 2 + + hotData.updateBlocking { 3 } shouldBe 3 + hotData.flow.first() shouldBe 3 + + testScope.uncaughtExceptions.singleOrNull() shouldBe null + + testCollector.cancel() + } + + @Test + fun `async updates error handler`() { + val testScope = TestCoroutineScope() + + val hotData = DynamicStateFlow( + loggingTag = "tag", + parentScope = testScope, + startValueProvider = { 1 }, + ) + + val testCollector = hotData.flow.test(startOnScope = testScope) + testScope.advanceUntilIdle() + + hotData.updateAsync { throw IOException("Surprise") } + + testScope.advanceUntilIdle() + + testScope.uncaughtExceptions.single() shouldBe instanceOf(IOException::class) + + testCollector.cancel() + } + + @Test + fun `async updates rethrow errors on HotDataFlow scope if no error handler is set`() = runBlocking { + val testScope = TestCoroutineScope() + + val hotData = DynamicStateFlow( + loggingTag = "tag", + parentScope = testScope, + startValueProvider = { 1 }, + ) + + val testCollector = hotData.flow.test(startOnScope = testScope) + testScope.advanceUntilIdle() + + var thrownError: Exception? = null + + hotData.updateAsync( + onUpdate = { throw IOException("Surprise") }, + onError = { thrownError = it } + ) + + testScope.advanceUntilIdle() + thrownError!!.shouldBeInstanceOf() + testScope.uncaughtExceptions.singleOrNull() shouldBe null + + testCollector.cancel() + } +} diff --git a/app/src/test/java/eu/darken/adsbc/common/preferences/FlowPreferenceMoshiTest.kt b/app/src/test/java/eu/darken/adsbc/common/preferences/FlowPreferenceMoshiTest.kt new file mode 100644 index 0000000..e03a564 --- /dev/null +++ b/app/src/test/java/eu/darken/adsbc/common/preferences/FlowPreferenceMoshiTest.kt @@ -0,0 +1,131 @@ +package eu.darken.adsbc.common.preferences + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import com.squareup.moshi.Moshi +import io.kotest.matchers.shouldBe +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.test.runBlockingTest +import org.junit.jupiter.api.Test +import testhelper.BaseTest +import testhelper.json.toComparableJson +import testhelpers.preferences.MockSharedPreferences + +class FlowPreferenceMoshiTest : BaseTest() { + + private val mockPreferences = MockSharedPreferences() + + @JsonClass(generateAdapter = true) + data class TestGson( + val string: String = "", + val boolean: Boolean = true, + val float: Float = 1.0f, + val int: Int = 1, + val long: Long = 1L + ) + + @Test + fun `reading and writing using manual reader and writer`() = runBlockingTest { + val testData1 = TestGson(string = "teststring") + val testData2 = TestGson(string = "update") + val moshi = Moshi.Builder().build() + FlowPreference( + preferences = mockPreferences, + key = "testKey", + rawReader = moshiReader(moshi, testData1), + rawWriter = moshiWriter(moshi) + ).apply { + value shouldBe testData1 + flow.first() shouldBe testData1 + mockPreferences.dataMapPeek.values.isEmpty() shouldBe true + + update { + it shouldBe testData1 + it!!.copy(string = "update") + } + + value shouldBe testData2 + flow.first() shouldBe testData2 + (mockPreferences.dataMapPeek.values.first() as String).toComparableJson() shouldBe """ + { + "string":"update", + "boolean":true, + "float":1.0, + "int":1, + "long":1 + } + """.toComparableJson() + + update { + it shouldBe testData2 + null + } + value shouldBe testData1 + flow.first() shouldBe testData1 + mockPreferences.dataMapPeek.values.isEmpty() shouldBe true + } + } + + @Test + fun `reading and writing using autocreated reader and writer`() = runBlockingTest { + val testData1 = TestGson(string = "teststring") + val testData2 = TestGson(string = "update") + val moshi = Moshi.Builder().build() + + mockPreferences.createFlowPreference( + key = "testKey", + defaultValue = testData1, + moshi = moshi + ).apply { + value shouldBe testData1 + flow.first() shouldBe testData1 + mockPreferences.dataMapPeek.values.isEmpty() shouldBe true + + update { + it shouldBe testData1 + it!!.copy(string = "update") + } + + value shouldBe testData2 + flow.first() shouldBe testData2 + (mockPreferences.dataMapPeek.values.first() as String).toComparableJson() shouldBe """ + { + "string":"update", + "boolean":true, + "float":1.0, + "int":1, + "long":1 + } + """.toComparableJson() + + update { + it shouldBe testData2 + null + } + value shouldBe testData1 + flow.first() shouldBe testData1 + mockPreferences.dataMapPeek.values.isEmpty() shouldBe true + } + } + + enum class Anum { + @Json(name = "a") + A, + @Json(name = "b") + B + } + + @Test + fun `enum serialization`() = runBlockingTest { + val moshi = Moshi.Builder().build() + val monitorMode = mockPreferences.createFlowPreference( + "test.enum", + Anum.A, + moshi + ) + + monitorMode.value shouldBe Anum.A + monitorMode.update { Anum.B } + monitorMode.value shouldBe Anum.B + } +} diff --git a/app/src/test/java/eu/darken/adsbc/common/preferences/FlowPreferenceTest.kt b/app/src/test/java/eu/darken/adsbc/common/preferences/FlowPreferenceTest.kt new file mode 100644 index 0000000..db10dc4 --- /dev/null +++ b/app/src/test/java/eu/darken/adsbc/common/preferences/FlowPreferenceTest.kt @@ -0,0 +1,160 @@ +package eu.darken.capod.common.preferences + +import eu.darken.adsbc.common.preferences.createFlowPreference +import io.kotest.matchers.shouldBe +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.test.runBlockingTest +import org.junit.jupiter.api.Test +import testhelper.BaseTest +import testhelpers.preferences.MockSharedPreferences + +class FlowPreferenceTest : BaseTest() { + + private val mockPreferences = MockSharedPreferences() + + @Test + fun `reading and writing strings`() = runBlockingTest { + mockPreferences.createFlowPreference( + key = "testKey", + defaultValue = "default" + ).apply { + value shouldBe "default" + flow.first() shouldBe "default" + mockPreferences.dataMapPeek.values.isEmpty() shouldBe true + + update { + it shouldBe "default" + "newvalue" + } + + value shouldBe "newvalue" + flow.first() shouldBe "newvalue" + mockPreferences.dataMapPeek.values.first() shouldBe "newvalue" + + update { + it shouldBe "newvalue" + null + } + value shouldBe "default" + flow.first() shouldBe "default" + mockPreferences.dataMapPeek.values.isEmpty() shouldBe true + } + } + + @Test + fun `reading and writing boolean`() = runBlockingTest { + mockPreferences.createFlowPreference( + key = "testKey", + defaultValue = true + ).apply { + value shouldBe true + flow.first() shouldBe true + mockPreferences.dataMapPeek.values.isEmpty() shouldBe true + + update { + it shouldBe true + false + } + + value shouldBe false + flow.first() shouldBe false + mockPreferences.dataMapPeek.values.first() shouldBe false + + update { + it shouldBe false + null + } + value shouldBe true + flow.first() shouldBe true + mockPreferences.dataMapPeek.values.isEmpty() shouldBe true + } + } + + @Test + fun `reading and writing long`() = runBlockingTest { + mockPreferences.createFlowPreference( + key = "testKey", + defaultValue = 9000L + ).apply { + value shouldBe 9000L + flow.first() shouldBe 9000L + mockPreferences.dataMapPeek.values.isEmpty() shouldBe true + + update { + it shouldBe 9000L + 9001L + } + + value shouldBe 9001L + flow.first() shouldBe 9001L + mockPreferences.dataMapPeek.values.first() shouldBe 9001L + + update { + it shouldBe 9001L + null + } + value shouldBe 9000L + flow.first() shouldBe 9000L + mockPreferences.dataMapPeek.values.isEmpty() shouldBe true + } + } + + @Test + fun `reading and writing integer`() = runBlockingTest { + mockPreferences.createFlowPreference( + key = "testKey", + defaultValue = 123 + ).apply { + value shouldBe 123 + flow.first() shouldBe 123 + mockPreferences.dataMapPeek.values.isEmpty() shouldBe true + + update { + it shouldBe 123 + 44 + } + + value shouldBe 44 + flow.first() shouldBe 44 + mockPreferences.dataMapPeek.values.first() shouldBe 44 + + update { + it shouldBe 44 + null + } + value shouldBe 123 + flow.first() shouldBe 123 + mockPreferences.dataMapPeek.values.isEmpty() shouldBe true + } + } + + @Test + fun `reading and writing float`() = runBlockingTest { + mockPreferences.createFlowPreference( + key = "testKey", + defaultValue = 3.6f + ).apply { + value shouldBe 3.6f + flow.first() shouldBe 3.6f + mockPreferences.dataMapPeek.values.isEmpty() shouldBe true + + update { + it shouldBe 3.6f + 15000f + } + + value shouldBe 15000f + flow.first() shouldBe 15000f + mockPreferences.dataMapPeek.values.first() shouldBe 15000f + + update { + it shouldBe 15000f + null + } + value shouldBe 3.6f + flow.first() shouldBe 3.6f + mockPreferences.dataMapPeek.values.isEmpty() shouldBe true + } + } + +} diff --git a/app/src/test/java/testhelper/BaseTest.kt b/app/src/test/java/testhelper/BaseTest.kt new file mode 100644 index 0000000..5169d27 --- /dev/null +++ b/app/src/test/java/testhelper/BaseTest.kt @@ -0,0 +1,29 @@ +package testhelper + +import eu.darken.adsbc.common.debug.logging.Logging +import eu.darken.adsbc.common.debug.logging.Logging.Priority.VERBOSE +import eu.darken.adsbc.common.debug.logging.log +import io.mockk.unmockkAll +import org.junit.jupiter.api.AfterAll +import testhelpers.logging.JUnitLogger + + +open class BaseTest { + init { + Logging.clearAll() + Logging.install(JUnitLogger()) + testClassName = this.javaClass.simpleName + } + + companion object { + private var testClassName: String? = null + + @JvmStatic + @AfterAll + fun onTestClassFinished() { + unmockkAll() + log(testClassName!!, VERBOSE) { "onTestClassFinished()" } + Logging.clearAll() + } + } +} diff --git a/app/src/test/java/testhelper/coroutine/CoroutinesTestExtension.kt b/app/src/test/java/testhelper/coroutine/CoroutinesTestExtension.kt new file mode 100644 index 0000000..900333f --- /dev/null +++ b/app/src/test/java/testhelper/coroutine/CoroutinesTestExtension.kt @@ -0,0 +1,26 @@ +package testhelper.coroutine + +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestCoroutineDispatcher +import kotlinx.coroutines.test.TestCoroutineScope +import kotlinx.coroutines.test.resetMain +import kotlinx.coroutines.test.setMain +import org.junit.jupiter.api.extension.AfterEachCallback +import org.junit.jupiter.api.extension.BeforeEachCallback +import org.junit.jupiter.api.extension.ExtensionContext + +@ExperimentalCoroutinesApi +class CoroutinesTestExtension( + private val dispatcher: TestCoroutineDispatcher = TestCoroutineDispatcher() +) : BeforeEachCallback, AfterEachCallback, TestCoroutineScope by TestCoroutineScope(dispatcher) { + + override fun beforeEach(context: ExtensionContext?) { + Dispatchers.setMain(dispatcher) + } + + override fun afterEach(context: ExtensionContext?) { + cleanupTestCoroutines() + Dispatchers.resetMain() + } +} diff --git a/app/src/test/java/testhelper/coroutine/TestDispatcherProvider.kt b/app/src/test/java/testhelper/coroutine/TestDispatcherProvider.kt new file mode 100644 index 0000000..2705b54 --- /dev/null +++ b/app/src/test/java/testhelper/coroutine/TestDispatcherProvider.kt @@ -0,0 +1,23 @@ +package testhelper.coroutine + +import eu.darken.adsbc.common.coroutine.DispatcherProvider +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlin.coroutines.CoroutineContext + +class TestDispatcherProvider(private val context: CoroutineContext? = null) : DispatcherProvider { + override val Default: CoroutineContext + get() = context ?: Dispatchers.Unconfined + override val Main: CoroutineContext + get() = context ?: Dispatchers.Unconfined + override val MainImmediate: CoroutineContext + get() = context ?: Dispatchers.Unconfined + override val Unconfined: CoroutineContext + get() = context ?: Dispatchers.Unconfined + override val IO: CoroutineContext + get() = context ?: Dispatchers.Unconfined +} + +fun CoroutineScope.asDispatcherProvider() = this.coroutineContext.asDispatcherProvider() + +fun CoroutineContext.asDispatcherProvider() = TestDispatcherProvider(context = this) diff --git a/app/src/test/java/testhelper/coroutine/TestExtensions.kt b/app/src/test/java/testhelper/coroutine/TestExtensions.kt new file mode 100644 index 0000000..a17a5cc --- /dev/null +++ b/app/src/test/java/testhelper/coroutine/TestExtensions.kt @@ -0,0 +1,49 @@ +package testhelper.coroutine + +import eu.darken.adsbc.common.debug.logging.log +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.TestCoroutineScope +import kotlinx.coroutines.test.UncompletedCoroutinesError +import kotlinx.coroutines.test.runBlockingTest +import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.EmptyCoroutineContext + +/** + * If you have a test that uses a coroutine that never stops, you may use this. + */ + +@ExperimentalCoroutinesApi // Since 1.2.1, tentatively till 1.3.0 +fun TestCoroutineScope.runBlockingTest2( + allowUncompleted: Boolean = false, + block: suspend TestCoroutineScope.() -> Unit +): Unit = runBlockingTest2( + allowUncompleted = allowUncompleted, + context = coroutineContext, + testBody = block +) + +fun runBlockingTest2( + allowUncompleted: Boolean = false, + context: CoroutineContext = EmptyCoroutineContext, + testBody: suspend TestCoroutineScope.() -> Unit +) { + try { + runBlocking { + try { + runBlockingTest( + context = context, + testBody = testBody + ) + } catch (e: UncompletedCoroutinesError) { + if (!allowUncompleted) throw e + else log { "Ignoring active job." } + } + } + } catch (e: Exception) { + if (!allowUncompleted || (e.message != "This job has not completed yet")) { + throw e + } + } +} + diff --git a/app/src/test/java/testhelper/flow/FlowTest.kt b/app/src/test/java/testhelper/flow/FlowTest.kt new file mode 100644 index 0000000..4cc3ea5 --- /dev/null +++ b/app/src/test/java/testhelper/flow/FlowTest.kt @@ -0,0 +1,101 @@ +package testhelper.flow + +import eu.darken.adsbc.common.debug.logging.Logging.Priority.WARN +import eu.darken.adsbc.common.debug.logging.asLog +import eu.darken.adsbc.common.debug.logging.log +import kotlinx.coroutines.* +import kotlinx.coroutines.channels.BufferOverflow +import kotlinx.coroutines.flow.* +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock +import kotlinx.coroutines.test.TestCoroutineScope + +fun Flow.test( + tag: String? = null, + startOnScope: CoroutineScope = TestCoroutineScope() +): TestCollector = createTest(tag ?: "FlowTest").start(scope = startOnScope) + +fun Flow.createTest( + tag: String? = null +): TestCollector = TestCollector(this, tag ?: "FlowTest") + +class TestCollector( + private val flow: Flow, + private val tag: String + +) { + private var error: Throwable? = null + private lateinit var job: Job + private val cache = MutableSharedFlow( + replay = Int.MAX_VALUE, + extraBufferCapacity = Int.MAX_VALUE, + onBufferOverflow = BufferOverflow.SUSPEND + ) + private var latestInternal: T? = null + private val collectedValuesMutex = Mutex() + private val collectedValues = mutableListOf() + + var silent = false + + fun start(scope: CoroutineScope) = apply { + flow + .buffer(capacity = Int.MAX_VALUE) + .onStart { log(tag) { "Setting up." } } + .onCompletion { log(tag) { "Final." } } + .onEach { + collectedValuesMutex.withLock { + if (!silent) log(tag) { "Collecting: $it" } + latestInternal = it + collectedValues.add(it) + cache.emit(it) + } + } + .catch { e -> + log(tag, WARN) { "Caught error: ${e.asLog()}" } + error = e + } + .launchIn(scope) + .also { job = it } + } + + fun emissions(): Flow = cache + + val latestValue: T? + get() = collectedValues.last() + + val latestValues: List + get() = collectedValues + + fun await( + timeout: Long = 10_000, + condition: (List, T) -> Boolean + ): T = runBlocking { + withTimeout(timeMillis = timeout) { + emissions().first { + condition(collectedValues, it) + } + } + } + + suspend fun awaitFinal(cancel: Boolean = false) = apply { + if (cancel) cancel() + try { + job.join() + } catch (e: Exception) { + error = e + } + } + + suspend fun assertNoErrors() = apply { + awaitFinal() + require(error == null) { "Error was not null: $error" } + } + + fun cancel() { + if (job.isCompleted) throw IllegalStateException("Flow is already canceled.") + + runBlocking { + job.cancelAndJoin() + } + } +} diff --git a/app/src/test/java/testhelper/json/JsonExtensions.kt b/app/src/test/java/testhelper/json/JsonExtensions.kt new file mode 100644 index 0000000..13c56ea --- /dev/null +++ b/app/src/test/java/testhelper/json/JsonExtensions.kt @@ -0,0 +1,29 @@ +package testhelper.json + +import com.squareup.moshi.JsonReader +import com.squareup.moshi.Moshi +import okio.Buffer +import okio.ByteString.Companion.encode +import okio.buffer +import okio.sink +import java.io.File + + +fun String.toComparableJson(): String { + val value = Buffer().use { + it.writeUtf8(this) + val reader = JsonReader.of(it) + reader.readJsonValue() + } + + val adapter = Moshi.Builder().build().adapter(Any::class.java).indent(" ") + + return adapter.toJson(value) +} + +fun String.writeToFile(file: File) = encode().let { text -> + require(!file.exists()) + file.parentFile?.mkdirs() + file.createNewFile() + file.sink().buffer().use { it.write(text) } +} \ No newline at end of file diff --git a/app/src/test/java/testhelper/livedata/InstantExecutorExtension.kt b/app/src/test/java/testhelper/livedata/InstantExecutorExtension.kt new file mode 100644 index 0000000..debd59d --- /dev/null +++ b/app/src/test/java/testhelper/livedata/InstantExecutorExtension.kt @@ -0,0 +1,26 @@ +package testhelper.livedata + +import androidx.arch.core.executor.ArchTaskExecutor +import androidx.arch.core.executor.TaskExecutor +import org.junit.jupiter.api.extension.AfterEachCallback +import org.junit.jupiter.api.extension.BeforeEachCallback +import org.junit.jupiter.api.extension.ExtensionContext + +class InstantExecutorExtension : BeforeEachCallback, AfterEachCallback { + + override fun beforeEach(context: ExtensionContext?) { + ArchTaskExecutor.getInstance().setDelegate( + object : TaskExecutor() { + override fun executeOnDiskIO(runnable: Runnable) = runnable.run() + + override fun postToMainThread(runnable: Runnable) = runnable.run() + + override fun isMainThread(): Boolean = true + } + ) + } + + override fun afterEach(context: ExtensionContext?) { + ArchTaskExecutor.getInstance().setDelegate(null) + } +} diff --git a/app/src/test/java/testhelper/preferences/MockSharedPreferencesTest.kt b/app/src/test/java/testhelper/preferences/MockSharedPreferencesTest.kt new file mode 100644 index 0000000..f69e990 --- /dev/null +++ b/app/src/test/java/testhelper/preferences/MockSharedPreferencesTest.kt @@ -0,0 +1,22 @@ +package testhelper.preferences + +import androidx.core.content.edit +import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.Test +import testhelper.BaseTest +import testhelpers.preferences.MockSharedPreferences + +class MockSharedPreferencesTest : BaseTest() { + + private fun createInstance() = MockSharedPreferences() + + @Test + fun `test boolean insertion`() { + val prefs = createInstance() + prefs.dataMapPeek shouldBe emptyMap() + prefs.getBoolean("key", true) shouldBe true + prefs.edit { putBoolean("key", false) } + prefs.getBoolean("key", true) shouldBe false + prefs.dataMapPeek["key"] shouldBe false + } +} diff --git a/app/src/testShared/java/testhelpers/BaseTestInstrumentation.kt b/app/src/testShared/java/testhelpers/BaseTestInstrumentation.kt new file mode 100644 index 0000000..3b6b9c0 --- /dev/null +++ b/app/src/testShared/java/testhelpers/BaseTestInstrumentation.kt @@ -0,0 +1,29 @@ +package testhelpers + +import eu.darken.adsbc.common.debug.logging.Logging +import eu.darken.adsbc.common.debug.logging.Logging.Priority.VERBOSE +import eu.darken.adsbc.common.debug.logging.log +import io.mockk.unmockkAll +import org.junit.AfterClass +import testhelpers.logging.JUnitLogger + +abstract class BaseTestInstrumentation { + + init { + Logging.clearAll() + Logging.install(JUnitLogger()) + testClassName = this.javaClass.simpleName + } + + companion object { + private var testClassName: String? = null + + @JvmStatic + @AfterClass + fun onTestClassFinished() { + unmockkAll() + log(testClassName!!, VERBOSE) { "onTestClassFinished()" } + Logging.clearAll() + } + } +} diff --git a/app/src/testShared/java/testhelpers/IsAUnitTest.kt b/app/src/testShared/java/testhelpers/IsAUnitTest.kt new file mode 100644 index 0000000..2af3e4c --- /dev/null +++ b/app/src/testShared/java/testhelpers/IsAUnitTest.kt @@ -0,0 +1,3 @@ +package testhelpers + +class IsAUnitTest diff --git a/app/src/testShared/java/testhelpers/logging/JUnitLogger.kt b/app/src/testShared/java/testhelpers/logging/JUnitLogger.kt new file mode 100644 index 0000000..3221a2a --- /dev/null +++ b/app/src/testShared/java/testhelpers/logging/JUnitLogger.kt @@ -0,0 +1,13 @@ +package testhelpers.logging + +import eu.darken.adsbc.common.debug.logging.Logging + +class JUnitLogger(private val minLogLevel: Logging.Priority = Logging.Priority.VERBOSE) : Logging.Logger { + + override fun isLoggable(priority: Logging.Priority): Boolean = priority.intValue >= minLogLevel.intValue + + override fun log(priority: Logging.Priority, tag: String, message: String, metaData: Map?) { + println("${System.currentTimeMillis()} ${priority.shortLabel}/$tag: $message") + } + +} diff --git a/app/src/testShared/java/testhelpers/preferences/MockFlowPreference.kt b/app/src/testShared/java/testhelpers/preferences/MockFlowPreference.kt new file mode 100644 index 0000000..43d891c --- /dev/null +++ b/app/src/testShared/java/testhelpers/preferences/MockFlowPreference.kt @@ -0,0 +1,21 @@ +package testhelpers.preferences + +import eu.darken.adsbc.common.preferences.FlowPreference +import io.mockk.every +import io.mockk.mockk +import kotlinx.coroutines.flow.MutableStateFlow + +fun mockFlowPreference( + defaultValue: T +): FlowPreference { + val instance = mockk>() + val flow = MutableStateFlow(defaultValue) + every { instance.flow } answers { flow } + every { instance.value } answers { flow.value } + every { instance.update(any()) } answers { + val updateCall = arg<(T) -> T>(0) + flow.value = updateCall(flow.value) + } + + return instance +} diff --git a/app/src/testShared/java/testhelpers/preferences/MockSharedPreferences.kt b/app/src/testShared/java/testhelpers/preferences/MockSharedPreferences.kt new file mode 100644 index 0000000..c094e56 --- /dev/null +++ b/app/src/testShared/java/testhelpers/preferences/MockSharedPreferences.kt @@ -0,0 +1,99 @@ +package testhelpers.preferences + +import android.content.SharedPreferences + +class MockSharedPreferences : SharedPreferences { + private val listeners = mutableListOf() + private val dataMap = mutableMapOf() + val dataMapPeek: Map + get() = dataMap.toMap() + + override fun getAll(): MutableMap = dataMap + + override fun getString(key: String, defValue: String?): String? = + dataMap[key] as? String ?: defValue + + override fun getStringSet(key: String, defValues: MutableSet?): MutableSet { + throw NotImplementedError() + } + + override fun getInt(key: String, defValue: Int): Int = + dataMap[key] as? Int ?: defValue + + override fun getLong(key: String, defValue: Long): Long = + dataMap[key] as? Long ?: defValue + + override fun getFloat(key: String, defValue: Float): Float { + throw NotImplementedError() + } + + override fun getBoolean(key: String, defValue: Boolean): Boolean = + dataMap[key] as? Boolean ?: defValue + + override fun contains(key: String): Boolean = dataMap.contains(key) + + override fun edit(): SharedPreferences.Editor = createEditor(dataMap.toMap()) { newData -> + dataMap.clear() + dataMap.putAll(newData) + } + + override fun registerOnSharedPreferenceChangeListener(listener: SharedPreferences.OnSharedPreferenceChangeListener) { + listeners.add(listener) + } + + override fun unregisterOnSharedPreferenceChangeListener(listener: SharedPreferences.OnSharedPreferenceChangeListener) { + listeners.remove(listener) + } + + private fun createEditor( + toEdit: Map, + onSave: (Map) -> Unit + ): SharedPreferences.Editor { + return object : SharedPreferences.Editor { + private val editorData = toEdit.toMutableMap() + override fun putString(key: String, value: String?): SharedPreferences.Editor = apply { + value?.let { editorData[key] = it } ?: editorData.remove(key) + } + + override fun putStringSet( + key: String?, + values: MutableSet? + ): SharedPreferences.Editor { + throw NotImplementedError() + } + + override fun putInt(key: String, value: Int): SharedPreferences.Editor = apply { + editorData[key] = value + } + + override fun putLong(key: String, value: Long): SharedPreferences.Editor = apply { + editorData[key] = value + } + + override fun putFloat(key: String, value: Float): SharedPreferences.Editor = apply { + editorData[key] = value + } + + override fun putBoolean(key: String, value: Boolean): SharedPreferences.Editor = apply { + editorData[key] = value + } + + override fun remove(key: String): SharedPreferences.Editor = apply { + editorData.remove(key) + } + + override fun clear(): SharedPreferences.Editor = apply { + editorData.clear() + } + + override fun commit(): Boolean { + onSave(editorData) + return true + } + + override fun apply() { + onSave(editorData) + } + } + } +} diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..068ebb1 --- /dev/null +++ b/build.gradle @@ -0,0 +1,54 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +buildscript { + ext.buildConfig = [ + 'compileSdk': 31, + 'minSdk' : 26, + 'targetSdk' : 31, + + 'version' : [ + 'major': 0, + 'minor': 0, + 'patch': 1, + 'build': 0, + ], + ] + + ext.buildConfig.version['name'] = "${buildConfig.version.major}.${buildConfig.version.minor}.${buildConfig.version.patch}" + ext.buildConfig.version['fullName'] = "${buildConfig.version.name}.${buildConfig.version.build}" + ext.buildConfig.version['code'] = buildConfig.version.major * 1000000 + buildConfig.version.minor * 10000 + buildConfig.version.patch * 100 + buildConfig.version.build + + ext.versions = [ + 'kotlin' : [ + 'core' : '1.6.10', + 'coroutines': '1.5.1' + ], + 'dagger' : [ + 'core': '2.39.1' + ], + 'androidx' : [ + 'navigation': '2.3.5' + ], + ] + + repositories { + google() + mavenCentral() + } + dependencies { + classpath 'com.android.tools.build:gradle:7.1.0' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin.core}" + classpath "com.google.dagger:hilt-android-gradle-plugin:${versions.dagger.core}" + classpath "androidx.navigation:navigation-safe-args-gradle-plugin:${versions.androidx.navigation}" + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +task clean(type: Delete) { + delete rootProject.buildDir +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..2521752 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,19 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app"s APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Kotlin code style for this project: "official" or "obsolete": +kotlin.code.style=official \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..f6b961fd5a86aa5fbfe90f707c3138408be7c718 GIT binary patch literal 54329 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

<5G2M1WZP5 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 F{|7XC?P&l2 literal 0 HcmV?d00001 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..288d2e9 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Sun Feb 06 15:16:44 CET 2022 +distributionBase=GRADLE_USER_HOME +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip +distributionPath=wrapper/dists +zipStorePath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..cccdd3d --- /dev/null +++ b/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..f955316 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..09f7622 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,2 @@ +rootProject.name = "ADS-B Companion" +include ':app'