From b473a84f2d2c6c1e35454f30e568193d4fb3d44e Mon Sep 17 00:00:00 2001 From: srtvprateek Date: Tue, 8 Feb 2022 21:16:28 +0530 Subject: [PATCH 001/223] plugin architecture init --- build.gradle | 29 +- gradle/wrapper/gradle-wrapper.properties | 6 +- {pluto-no-op => plugin-logger}/.gitignore | 0 plugin-logger/build.gradle | 70 ++++ plugin-logger/src/main/AndroidManifest.xml | 5 + .../main/java/com/pluto/logger/PlutoLog.kt | 57 +++ .../com/pluto/logger/PlutoLoggerPlugin.kt | 32 ++ .../java/com/pluto/logger/PlutoTimberTree.kt | 77 ++++ .../com/pluto/logger/internal}/DataModel.kt | 11 +- .../com/pluto/logger/internal/LogsFragment.kt | 6 + .../pluto/logger/internal/LogsProcessor.kt | 71 ++++ .../com/pluto/logger/internal}/LogsRepo.kt | 15 +- .../pluto/logger/internal/LogsViewModel.kt | 18 + .../java/com/pluto/logger/internal/Session.kt | 5 + .../com/pluto/logger/internal/ThrowableKtx.kt | 56 +++ .../logger/internal/ui/DetailsFragment.kt | 137 +++++++ .../pluto/logger/internal/ui/ListFragment.kt | 76 ++-- .../logger/internal/ui/list}/LogItemHolder.kt | 30 +- .../logger/internal/ui/list}/LogsAdapter.kt | 10 +- .../pluto_logger___bg_bottom_sheet.xml | 21 + ...pluto_logger___bg_bottom_sheet_content.xml | 4 +- .../drawable/pluto_logger___ic_analytics.xml | 0 .../drawable/pluto_logger___ic_clear_all.xml | 0 .../res/drawable/pluto_logger___ic_label.xml | 0 .../pluto_logger___ic_logger_icon.xml | 10 + .../pluto_logger___fragment_details.xml | 39 +- .../layout/pluto_logger___fragment_list.xml | 140 +++++++ .../layout/pluto_logger___fragment_logs.xml | 15 + .../res/layout/pluto_logger___list_item.xml | 9 +- .../menu/pluto_logger___menu_more_options.xml | 4 +- .../navigation/pluto_logger___navigation.xml | 21 + plugin-logger/src/main/res/values/strings.xml | 15 + plugin-logger/src/main/res/values/styles.xml | 21 + pluto-no-op/build.gradle | 51 --- .../src/main/java/com/mocklets/pluto/Pluto.kt | 20 - .../com/mocklets/pluto/PlutoInterceptor.kt | 14 - .../main/java/com/mocklets/pluto/PlutoLog.kt | 27 -- .../pluto/modules/exceptions/ANRException.kt | 14 - .../pluto/modules/exceptions/ANRListener.kt | 8 - pluto-plugin/.gitignore | 1 + pluto-plugin/build.gradle | 69 ++++ .../src/main/AndroidManifest.xml | 2 +- .../main/java/com/pluto/plugin/DataModel.kt | 34 ++ .../src/main/java/com/pluto/plugin/Plugin.kt | 54 +++ .../java/com/pluto/plugin/PluginHelper.kt | 27 ++ .../pluto/plugin/PluginOptionsViewModel.kt | 18 + .../java/com/pluto/plugin/PluginUiBridge.kt | 28 ++ .../com/pluto/plugin/UiBridgeComponents.kt | 5 + .../utilities}/DeBounceClickListener.kt | 16 +- .../com/pluto/plugin/utilities}/DebugLog.kt | 17 +- .../utilities}/FragmentViewBindingDelegate.kt | 9 +- .../plugin/utilities}/SingleLiveEvent.kt | 13 +- .../pluto/plugin/utilities/device/Device.kt | 83 ++-- .../pluto/plugin/utilities/device/RootUtil.kt | 56 +++ .../utilities}/extensions/AnimationKtx.kt | 2 +- .../utilities}/extensions/ContextKtx.kt | 10 +- .../plugin/utilities}/extensions/DateKtx.kt | 2 +- .../plugin/utilities}/extensions/DimensKtx.kt | 2 +- .../utilities}/extensions/KeyboardKtx.kt | 2 +- .../plugin/utilities}/extensions/LayoutKtx.kt | 2 +- .../utilities}/extensions/LifecycleKtx.kt | 2 +- .../utilities}/extensions/LiveDataKtx.kt | 2 +- .../plugin/utilities}/extensions/PopupKtx.kt | 10 +- .../utilities}/extensions/RecyclerViewKtx.kt | 2 +- .../utilities}/extensions/StringsKtx.kt | 2 +- .../plugin/utilities}/list/BaseAdapter.kt | 6 +- .../utilities}/list/CustomItemDecorator.kt | 8 +- .../utilities}/list/DiffAwareAdapter.kt | 4 +- .../plugin/utilities}/sharing/ContentShare.kt | 16 +- .../sharing/ContentShareViewModel.kt | 6 +- .../plugin/utilities}/sharing/ShareKtx.kt | 10 +- .../utilities}/sharing/ShareOptionsDialog.kt | 18 +- .../spannable/CustomTypefaceSpan.kt | 2 +- .../plugin/utilities}/spannable/SpanUtil.kt | 14 +- .../anim/pluto___fragment_default_enter.xml | 0 .../anim/pluto___fragment_default_exit.xml | 0 .../anim/pluto___fragment_default_reenter.xml | 0 .../anim/pluto___fragment_default_return.xml | 0 .../res/drawable/pluto___bg_bottom_sheet.xml | 4 +- .../pluto___ic_plugin_placeholder_icon.xml | 9 + .../main/res/drawable/pluto___ic_search.xml | 0 .../res/drawable/pluto___ic_share_as_copy.xml | 0 .../res/drawable/pluto___ic_share_as_file.xml | 0 .../res/drawable/pluto___ic_share_as_text.xml | 0 .../res/drawable/pluto___line_divider.xml | 0 .../src/main/res/font/muli.ttf | Bin .../src/main/res/font/muli_bold.ttf | Bin .../src/main/res/font/muli_light.ttf | Bin .../src/main/res/font/muli_semibold.ttf | Bin .../pluto___layout_content_share_options.xml | 1 - .../src/main/res/values/colors.xml | 11 +- pluto-plugin/src/main/res/values/dimens.xml | 36 ++ .../src/main/res/values/integers.xml | 0 pluto-plugin/src/main/res/values/strings.xml | 6 + pluto-plugin/src/main/res/values/styles.xml | 66 +++ pluto-version.properties | 4 +- pluto/.gitignore | 2 +- pluto/build.gradle | 69 +--- pluto/consumer-rules.pro | 20 - pluto/proguard-rules.pro | 34 -- .../mocklets/pluto/ExampleInstrumentedTest.kt | 22 - pluto/src/main/AndroidManifest.xml | 14 +- .../src/main/java/com/mocklets/pluto/Pluto.kt | 72 ---- .../com/mocklets/pluto/PlutoInterceptor.kt | 64 --- .../main/java/com/mocklets/pluto/PlutoLog.kt | 97 ----- .../com/mocklets/pluto/core/DeviceInfo.kt | 21 - .../java/com/mocklets/pluto/core/RootUtil.kt | 54 --- .../java/com/mocklets/pluto/core/Session.kt | 14 - .../pluto/core/database/DatabaseManager.kt | 18 - .../pluto/core/database/PlutoDatabase.kt | 20 - .../pluto/core/extensions/ConcurrencyKtx.kt | 7 - .../pluto/core/extensions/CustomTabKtx.kt | 64 --- .../pluto/core/extensions/FragmentKtx.kt | 24 -- .../pluto/core/preferences/Preferences.kt | 51 --- .../pluto/core/ui/routing/OnBackKeyHandler.kt | 5 - .../pluto/core/ui/routing/RouteManager.kt | 130 ------ .../mocklets/pluto/core/ui/routing/Router.kt | 28 -- .../pluto/core/ui/routing/Utilities.kt | 45 --- .../modules/activities/ActivityTracker.kt | 166 -------- .../modules/appstate/AppStateFragment.kt | 154 ------- .../modules/appstate/AppStateItemAdapter.kt | 26 -- .../modules/appstate/AppStateItemHolder.kt | 31 -- .../modules/appstate/AppStateViewModel.kt | 27 -- .../pluto/modules/exceptions/ANRException.kt | 79 ---- .../pluto/modules/exceptions/ANRListener.kt | 8 - .../pluto/modules/exceptions/DataModel.kt | 110 ----- .../pluto/modules/exceptions/ExceptionRepo.kt | 65 --- .../modules/exceptions/anrs/AnrSupervisor.kt | 54 --- .../exceptions/anrs/AnrSupervisorCallback.kt | 30 -- .../exceptions/anrs/AnrSupervisorRunnable.kt | 114 ------ .../exceptions/crashes/CrashHandler.kt | 36 -- .../exceptions/crashes/CrashNotification.kt | 41 -- .../exceptions/dao/EntityConverters.kt | 20 - .../modules/exceptions/dao/ExceptionDao.kt | 25 -- .../modules/exceptions/dao/ExceptionEntity.kt | 22 - .../exceptions/ui/CrashDetailsFragment.kt | 172 -------- .../modules/exceptions/ui/CrashesAdapter.kt | 53 --- .../modules/exceptions/ui/CrashesFragment.kt | 95 ----- .../modules/exceptions/ui/CrashesViewModel.kt | 58 --- .../ui/holder/CrashItemDetailsDeviceHolder.kt | 47 --- .../ui/holder/CrashItemDetailsHeaderHolder.kt | 112 ------ .../ui/holder/CrashItemDetailsThreadHolder.kt | 39 -- .../CrashItemDetailsThreadStatesHolder.kt | 55 --- .../CrashItemDetailsThreadStatesItemHolder.kt | 50 --- .../exceptions/ui/holder/CrashItemHolder.kt | 61 --- .../modules/logging/ui/LogDetailsDialog.kt | 142 ------- .../pluto/modules/logging/ui/LogsViewModel.kt | 12 - .../pluto/modules/network/BodyTransformer.kt | 154 ------- .../pluto/modules/network/CurlBuilder.kt | 22 - .../pluto/modules/network/DataModel.kt | 63 --- .../pluto/modules/network/MediaTypeKtx.kt | 20 - .../pluto/modules/network/NetworkCallsRepo.kt | 40 -- .../interceptor/CacheDirectoryProvider.kt | 15 - .../network/interceptor/DataConvertor.kt | 149 ------- .../network/interceptor/DepletingSource.kt | 37 -- .../network/interceptor/FileFactory.kt | 30 -- .../modules/network/interceptor/OkHttpKtx.kt | 61 --- .../network/interceptor/ReportingSink.kt | 106 ----- .../interceptor/ResponseBodyProcessor.kt | 106 ----- .../modules/network/interceptor/TeeSource.kt | 62 --- .../modules/network/proxy/NetworkProxyRepo.kt | 77 ---- .../network/proxy/NetworkProxyViewModel.kt | 74 ---- .../proxy/dao/NetworkProxyConverters.kt | 19 - .../network/proxy/dao/NetworkProxyDao.kt | 25 -- .../network/proxy/dao/NetworkProxyEntity.kt | 41 -- .../proxy/ui/NetworkProxyItemAdapter.kt | 27 -- .../proxy/ui/NetworkProxyItemHolder.kt | 29 -- .../proxy/ui/NetworkProxySettingsFragment.kt | 162 -------- .../ui/NetworkProxySettingsListFragment.kt | 119 ------ .../network/transformers/BaseTransformer.kt | 8 - .../transformers/FormEncodedTransformer.kt | 48 --- .../transformers/JsonBaseTransformer.kt | 33 -- .../transformers/XmlBaseTransformer.kt | 35 -- .../pluto/modules/network/ui/ApiItemHolder.kt | 113 ------ .../modules/network/ui/NetworkAdapter.kt | 27 -- .../modules/network/ui/NetworkFragment.kt | 91 ----- .../modules/network/ui/NetworkViewModel.kt | 49 --- .../ui/details/NetworkCallDetailsFragment.kt | 186 --------- .../details/NetworkDetailsOverviewFragment.kt | 241 ----------- .../ui/details/NetworkDetailsPagerAdapter.kt | 27 -- .../details/NetworkDetailsRequestFragment.kt | 74 ---- .../details/NetworkDetailsResponseFragment.kt | 147 ------- .../modules/preferences/SharedPrefRepo.kt | 127 ------ .../pluto/modules/preferences/ui/DataModel.kt | 21 - .../preferences/ui/SharedPrefAdapter.kt | 31 -- .../preferences/ui/SharedPrefEditDialog.kt | 144 ------- .../preferences/ui/SharedPrefFragment.kt | 100 ----- .../ui/SharedPrefKeyValueItemHolder.kt | 53 --- .../preferences/ui/SharedPrefViewModel.kt | 18 - .../ui/filter/SharedPrefFilterFragment.kt | 67 --- .../ui/filter/SharedPrefFilterItemHolder.kt | 40 -- .../pluto/modules/settings/DataModel.kt | 28 -- .../pluto/modules/settings/SettingsAdapter.kt | 47 --- .../modules/settings/SettingsFragment.kt | 107 ----- .../holders/SettingsClearDataHolder.kt | 30 -- .../holders/SettingsEasyAccessHolder.kt | 30 -- .../holders/SettingsMockletsLinkHolder.kt | 27 -- .../holders/SettingsResetAllHolder.kt | 27 -- .../holders/SettingsSharePreferenceHolder.kt | 27 -- .../setup/easyaccess/EasyAccessSetupDialog.kt | 58 --- .../pluto/modules/setup/easyaccess/Popup.kt | 51 --- .../com/mocklets/pluto/ui/AboutFragment.kt | 6 - .../com/mocklets/pluto/ui/BaseFragment.kt | 65 --- .../com/mocklets/pluto/ui/PlutoActivity.kt | 58 --- .../mocklets/pluto/ui/SectionsPagerAdapter.kt | 33 -- .../DebugNotification.kt} | 19 +- pluto/src/main/java/com/pluto/Pluto.kt | 76 ++++ .../pluto/core => pluto}/PlutoFileProvider.kt | 2 +- pluto/src/main/java/com/pluto/Session.kt | 5 + .../com/pluto/applifecycle/AppLifecycle.kt | 73 ++++ .../java/com/pluto/applifecycle/AppState.kt | 6 + pluto/src/main/java/com/pluto/notch/Notch.kt | 61 +++ .../notch/NotchViewManager.kt} | 56 +-- .../notch/OnNotchInteractionListener.kt} | 4 +- .../notification/NotificationUtil.kt | 4 +- .../java/com/pluto/plugin/PluginManager.kt | 38 ++ .../pluto/plugin/PluginSelectorActivity.kt | 88 ++++ .../java/com/pluto/plugin/PluginsViewModel.kt | 18 + .../com/pluto/plugin/list/PluginAdapter.kt | 27 ++ .../com/pluto/plugin/list/PluginItemHolder.kt | 53 +++ .../plugin/options/PluginOptionAdapter.kt | 27 ++ .../plugin/options/PluginOptionItemHolder.kt | 29 ++ .../main/java/com/pluto/settings/DataModel.kt | 15 + .../com/pluto/settings/OverConsentFragment.kt | 30 ++ .../com/pluto/settings/SettingsAdapter.kt | 35 ++ .../com/pluto/settings/SettingsFragment.kt | 82 ++++ .../settings}/SettingsKtx.kt | 2 +- .../com/pluto/settings/SettingsPreferences.kt | 32 ++ .../settings/SettingsViewModel.kt | 29 +- .../holders/SettingsEasyAccessHolder.kt | 28 ++ ...SettingsEasyAccessPopupAppearanceHolder.kt | 32 +- .../holders/SettingsResetAllHolder.kt | 25 ++ .../java/com/pluto/ui/PluginOptionsDialog.kt | 103 +++++ .../main/java/com/pluto/ui/PlutoActivity.kt | 95 +++++ .../java/com/pluto/ui/RoundedFrameLayout.kt | 116 ++++++ .../main/res/anim/pluto___click_bounce.xml | 15 + .../main/res/anim/pluto___fade_in_dialog.xml | 7 + .../main/res/anim/pluto___fade_out_dialog.xml | 7 + .../main/res/anim/pluto___slide_in_bottom.xml | 5 + .../res/anim/pluto___slide_out_bottom.xml | 5 + .../color/pluto___bg_strip_button_dark.xml | 5 - .../drawable/pluto___bg_cta_dashed_blue.xml | 14 - .../res/drawable/pluto___bg_section_body.xml | 9 - .../drawable/pluto___bg_section_header.xml | 9 - .../res/drawable/pluto___bg_section_light.xml | 7 - .../pluto___bg_shared_pref_file_badge.xml | 6 - .../main/res/drawable/pluto___ic_about.xml | 9 + .../res/drawable/pluto___ic_anr_warning.xml | 9 - .../res/drawable/pluto___ic_arrow_back.xml | 9 - ...ic_check_light.xml => pluto___ic_back.xml} | 4 +- .../res/drawable/pluto___ic_chevron_right.xml | 9 - .../main/res/drawable/pluto___ic_close.xml | 2 +- .../res/drawable/pluto___ic_close_gray.xml | 9 - .../src/main/res/drawable/pluto___ic_copy.xml | 9 - .../main/res/drawable/pluto___ic_debugger.xml | 9 - .../main/res/drawable/pluto___ic_delete.xml | 9 - .../src/main/res/drawable/pluto___ic_done.xml | 9 - .../res/drawable/pluto___ic_easy_access.xml | 6 +- .../main/res/drawable/pluto___ic_filter.xml | 18 - .../src/main/res/drawable/pluto___ic_grid.xml | 9 + .../res/drawable/pluto___ic_grid_dark.xml | 9 + .../src/main/res/drawable/pluto___ic_key.xml | 11 - ..._more_vertical.xml => pluto___ic_more.xml} | 3 +- .../pluto___ic_proxy_base_request.xml | 9 - .../pluto___ic_proxy_base_request_dark.xml | 9 - .../drawable/pluto___ic_proxy_indicator.xml | 16 - .../drawable/pluto___ic_proxy_settings.xml | 16 - .../pluto___ic_proxy_settings_disabled.xml | 16 - .../pluto___ic_proxy_settings_enabled.xml | 16 - .../main/res/drawable/pluto___ic_settings.xml | 10 +- .../main/res/drawable/pluto___ic_share.xml | 9 - .../main/res/drawable/pluto___ic_success.xml | 9 - .../drawable/pluto___ic_user_properties.xml | 9 - .../src/main/res/drawable/pluto___ic_vcs.xml | 9 + .../main/res/drawable/pluto___ic_website.xml | 14 + .../pluto___activity_plugin_selector.xml | 93 +++++ .../res/layout/pluto___activity_pluto.xml | 9 +- .../res/layout/pluto___fragment_about.xml | 14 - .../res/layout/pluto___fragment_app_state.xml | 247 ------------ .../main/res/layout/pluto___fragment_base.xml | 91 ----- .../layout/pluto___fragment_crash_details.xml | 100 ----- .../res/layout/pluto___fragment_crashes.xml | 100 ----- .../main/res/layout/pluto___fragment_logs.xml | 100 ----- .../res/layout/pluto___fragment_network.xml | 100 ----- ...to___fragment_network_details_overview.xml | 282 ------------- ...uto___fragment_network_details_request.xml | 188 --------- ...to___fragment_network_details_response.xml | 175 -------- ...luto___fragment_network_proxy_settings.xml | 380 ------------------ ...__fragment_network_proxy_settings_list.xml | 208 ---------- .../pluto___fragment_overlay_consent.xml | 101 +++++ .../res/layout/pluto___fragment_settings.xml | 115 ++---- .../layout/pluto___fragment_shared_pref.xml | 100 ----- .../pluto___fragment_shared_pref_filter.xml | 98 ----- .../layout/pluto___item_anr_thread_state.xml | 19 - .../res/layout/pluto___item_app_state.xml | 33 -- .../main/res/layout/pluto___item_crash.xml | 58 --- .../pluto___item_crash_details_device.xml | 359 ----------------- .../pluto___item_crash_details_header.xml | 93 ----- .../pluto___item_crash_details_thread.xml | 118 ------ ...uto___item_crash_details_thread_states.xml | 38 -- .../main/res/layout/pluto___item_network.xml | 111 ----- .../pluto___item_network_proxy_settings.xml | 28 -- .../main/res/layout/pluto___item_plugin.xml | 49 +++ ...ter.xml => pluto___item_plugin_option.xml} | 27 +- .../pluto___item_settings_clear_data.xml | 41 -- .../pluto___item_settings_easy_access.xml | 4 +- ...__item_settings_easy_access_appearance.xml | 2 +- .../pluto___item_settings_link_mocklets.xml | 57 --- .../pluto___item_settings_reset_all.xml | 4 +- .../pluto___item_settings_shared_pref.xml | 56 --- .../pluto___item_shared_pref_key_value.xml | 72 ---- .../pluto___layout_easy_access_setup.xml | 94 ----- .../pluto___layout_network_call_details.xml | 200 --------- .../main/res/layout/pluto___layout_notch.xml | 72 ++++ .../pluto___layout_plugin_options_dialog.xml | 139 +++++++ .../main/res/layout/pluto___layout_popup.xml | 64 --- .../pluto___layout_shared_pref_edit.xml | 165 -------- .../res/layout/pluto___stub_copy_curl.xml | 30 -- .../res/layout/pluto___stub_crash_report.xml | 51 --- .../layout/pluto___stub_network_settings.xml | 136 ------- .../res/menu/pluto___popup_menu_crashes.xml | 7 - .../res/menu/pluto___popup_menu_network.xml | 15 - .../menu/pluto___popup_menu_shared_pref.xml | 7 - pluto/src/main/res/values/attr.xml | 9 + pluto/src/main/res/values/dimens.xml | 34 +- pluto/src/main/res/values/strings.xml | 158 +------- pluto/src/main/res/values/styles.xml | 57 ++- .../com/mocklets/pluto/ExampleUnitTest.kt | 16 - sample/build.gradle | 11 +- sample/proguard-rules.pro | 2 +- sample/src/main/AndroidManifest.xml | 4 +- .../main/java/com/sampleapp/MainActivity.kt | 58 ++- .../src/main/java/com/sampleapp/SampleApp.kt | 26 +- sample/src/main/java/com/sampleapp/Test.java | 3 +- .../java/com/sampleapp/TestingThreadANR.kt | 3 +- sample/src/main/java/com/sampleapp/Utils.kt | 19 +- .../java/com/sampleapp/network/Network.kt | 3 +- .../com/sampleapp/network/NetworkCalls.kt | 6 +- .../build/utils.gradle | 0 .../module.gradle} | 2 +- .../root.gradle} | 0 .../static-analysis}/.editorconfig | 0 .../static-analysis}/android-lint-config.xml | 0 .../static-analysis}/code-analysis.gradle | 4 +- .../static-analysis}/detekt-config.yml | 0 settings.gradle | 3 +- 346 files changed, 3341 insertions(+), 11637 deletions(-) rename {pluto-no-op => plugin-logger}/.gitignore (100%) create mode 100644 plugin-logger/build.gradle create mode 100644 plugin-logger/src/main/AndroidManifest.xml create mode 100644 plugin-logger/src/main/java/com/pluto/logger/PlutoLog.kt create mode 100644 plugin-logger/src/main/java/com/pluto/logger/PlutoLoggerPlugin.kt create mode 100644 plugin-logger/src/main/java/com/pluto/logger/PlutoTimberTree.kt rename {pluto/src/main/java/com/mocklets/pluto/modules/logging => plugin-logger/src/main/java/com/pluto/logger/internal}/DataModel.kt (77%) create mode 100644 plugin-logger/src/main/java/com/pluto/logger/internal/LogsFragment.kt create mode 100644 plugin-logger/src/main/java/com/pluto/logger/internal/LogsProcessor.kt rename {pluto/src/main/java/com/mocklets/pluto/modules/logging => plugin-logger/src/main/java/com/pluto/logger/internal}/LogsRepo.kt (75%) create mode 100644 plugin-logger/src/main/java/com/pluto/logger/internal/LogsViewModel.kt create mode 100644 plugin-logger/src/main/java/com/pluto/logger/internal/Session.kt create mode 100644 plugin-logger/src/main/java/com/pluto/logger/internal/ThrowableKtx.kt create mode 100644 plugin-logger/src/main/java/com/pluto/logger/internal/ui/DetailsFragment.kt rename pluto/src/main/java/com/mocklets/pluto/modules/logging/ui/LogsFragment.kt => plugin-logger/src/main/java/com/pluto/logger/internal/ui/ListFragment.kt (50%) rename {pluto/src/main/java/com/mocklets/pluto/modules/logging/ui => plugin-logger/src/main/java/com/pluto/logger/internal/ui/list}/LogItemHolder.kt (66%) rename {pluto/src/main/java/com/mocklets/pluto/modules/logging/ui => plugin-logger/src/main/java/com/pluto/logger/internal/ui/list}/LogsAdapter.kt (69%) create mode 100644 plugin-logger/src/main/res/drawable/pluto_logger___bg_bottom_sheet.xml rename pluto/src/main/res/drawable/pluto___bg_bottom_sheet_content.xml => plugin-logger/src/main/res/drawable/pluto_logger___bg_bottom_sheet_content.xml (78%) rename pluto/src/main/res/drawable/pluto___ic_analytics.xml => plugin-logger/src/main/res/drawable/pluto_logger___ic_analytics.xml (100%) rename pluto/src/main/res/drawable/pluto___ic_clear_all.xml => plugin-logger/src/main/res/drawable/pluto_logger___ic_clear_all.xml (100%) rename pluto/src/main/res/drawable/pluto___ic_label.xml => plugin-logger/src/main/res/drawable/pluto_logger___ic_label.xml (100%) create mode 100644 plugin-logger/src/main/res/drawable/pluto_logger___ic_logger_icon.xml rename pluto/src/main/res/layout/pluto___layout_log_details.xml => plugin-logger/src/main/res/layout/pluto_logger___fragment_details.xml (91%) create mode 100644 plugin-logger/src/main/res/layout/pluto_logger___fragment_list.xml create mode 100644 plugin-logger/src/main/res/layout/pluto_logger___fragment_logs.xml rename pluto/src/main/res/layout/pluto___item_log.xml => plugin-logger/src/main/res/layout/pluto_logger___list_item.xml (84%) rename pluto/src/main/res/menu/pluto___popup_menu_logger.xml => plugin-logger/src/main/res/menu/pluto_logger___menu_more_options.xml (54%) create mode 100644 plugin-logger/src/main/res/navigation/pluto_logger___navigation.xml create mode 100644 plugin-logger/src/main/res/values/strings.xml create mode 100644 plugin-logger/src/main/res/values/styles.xml delete mode 100644 pluto-no-op/build.gradle delete mode 100644 pluto-no-op/src/main/java/com/mocklets/pluto/Pluto.kt delete mode 100644 pluto-no-op/src/main/java/com/mocklets/pluto/PlutoInterceptor.kt delete mode 100644 pluto-no-op/src/main/java/com/mocklets/pluto/PlutoLog.kt delete mode 100644 pluto-no-op/src/main/java/com/mocklets/pluto/modules/exceptions/ANRException.kt delete mode 100644 pluto-no-op/src/main/java/com/mocklets/pluto/modules/exceptions/ANRListener.kt create mode 100644 pluto-plugin/.gitignore create mode 100644 pluto-plugin/build.gradle rename {pluto-no-op => pluto-plugin}/src/main/AndroidManifest.xml (56%) create mode 100644 pluto-plugin/src/main/java/com/pluto/plugin/DataModel.kt create mode 100644 pluto-plugin/src/main/java/com/pluto/plugin/Plugin.kt create mode 100644 pluto-plugin/src/main/java/com/pluto/plugin/PluginHelper.kt create mode 100644 pluto-plugin/src/main/java/com/pluto/plugin/PluginOptionsViewModel.kt create mode 100644 pluto-plugin/src/main/java/com/pluto/plugin/PluginUiBridge.kt create mode 100644 pluto-plugin/src/main/java/com/pluto/plugin/UiBridgeComponents.kt rename {pluto/src/main/java/com/mocklets/pluto/core/ui => pluto-plugin/src/main/java/com/pluto/plugin/utilities}/DeBounceClickListener.kt (75%) rename {pluto/src/main/java/com/mocklets/pluto/core => pluto-plugin/src/main/java/com/pluto/plugin/utilities}/DebugLog.kt (71%) rename {pluto/src/main/java/com/mocklets/pluto/core/binding => pluto-plugin/src/main/java/com/pluto/plugin/utilities}/FragmentViewBindingDelegate.kt (87%) rename {pluto/src/main/java/com/mocklets/pluto/core => pluto-plugin/src/main/java/com/pluto/plugin/utilities}/SingleLiveEvent.kt (89%) rename pluto/src/main/java/com/mocklets/pluto/core/DeviceFingerPrint.kt => pluto-plugin/src/main/java/com/pluto/plugin/utilities/device/Device.kt (54%) create mode 100644 pluto-plugin/src/main/java/com/pluto/plugin/utilities/device/RootUtil.kt rename {pluto/src/main/java/com/mocklets/pluto/core => pluto-plugin/src/main/java/com/pluto/plugin/utilities}/extensions/AnimationKtx.kt (96%) rename {pluto/src/main/java/com/mocklets/pluto/core => pluto-plugin/src/main/java/com/pluto/plugin/utilities}/extensions/ContextKtx.kt (68%) rename {pluto/src/main/java/com/mocklets/pluto/core => pluto-plugin/src/main/java/com/pluto/plugin/utilities}/extensions/DateKtx.kt (95%) rename {pluto/src/main/java/com/mocklets/pluto/core => pluto-plugin/src/main/java/com/pluto/plugin/utilities}/extensions/DimensKtx.kt (92%) rename {pluto/src/main/java/com/mocklets/pluto/core => pluto-plugin/src/main/java/com/pluto/plugin/utilities}/extensions/KeyboardKtx.kt (97%) rename {pluto/src/main/java/com/mocklets/pluto/core => pluto-plugin/src/main/java/com/pluto/plugin/utilities}/extensions/LayoutKtx.kt (97%) rename {pluto/src/main/java/com/mocklets/pluto/core => pluto-plugin/src/main/java/com/pluto/plugin/utilities}/extensions/LifecycleKtx.kt (89%) rename {pluto/src/main/java/com/mocklets/pluto/core => pluto-plugin/src/main/java/com/pluto/plugin/utilities}/extensions/LiveDataKtx.kt (84%) rename {pluto/src/main/java/com/mocklets/pluto/core => pluto-plugin/src/main/java/com/pluto/plugin/utilities}/extensions/PopupKtx.kt (83%) rename {pluto/src/main/java/com/mocklets/pluto/core => pluto-plugin/src/main/java/com/pluto/plugin/utilities}/extensions/RecyclerViewKtx.kt (86%) rename {pluto/src/main/java/com/mocklets/pluto/core => pluto-plugin/src/main/java/com/pluto/plugin/utilities}/extensions/StringsKtx.kt (80%) rename {pluto/src/main/java/com/mocklets/pluto/core/ui => pluto-plugin/src/main/java/com/pluto/plugin/utilities}/list/BaseAdapter.kt (89%) rename {pluto/src/main/java/com/mocklets/pluto/core/ui => pluto-plugin/src/main/java/com/pluto/plugin/utilities}/list/CustomItemDecorator.kt (80%) rename {pluto/src/main/java/com/mocklets/pluto/core/ui => pluto-plugin/src/main/java/com/pluto/plugin/utilities}/list/DiffAwareAdapter.kt (93%) rename {pluto/src/main/java/com/mocklets/pluto/core => pluto-plugin/src/main/java/com/pluto/plugin/utilities}/sharing/ContentShare.kt (77%) rename {pluto/src/main/java/com/mocklets/pluto/core => pluto-plugin/src/main/java/com/pluto/plugin/utilities}/sharing/ContentShareViewModel.kt (53%) rename {pluto/src/main/java/com/mocklets/pluto/core => pluto-plugin/src/main/java/com/pluto/plugin/utilities}/sharing/ShareKtx.kt (86%) rename {pluto/src/main/java/com/mocklets/pluto/core => pluto-plugin/src/main/java/com/pluto/plugin/utilities}/sharing/ShareOptionsDialog.kt (81%) rename {pluto/src/main/java/com/mocklets/pluto/core/ui => pluto-plugin/src/main/java/com/pluto/plugin/utilities}/spannable/CustomTypefaceSpan.kt (93%) rename {pluto/src/main/java/com/mocklets/pluto/core/ui => pluto-plugin/src/main/java/com/pluto/plugin/utilities}/spannable/SpanUtil.kt (92%) rename {pluto => pluto-plugin}/src/main/res/anim/pluto___fragment_default_enter.xml (100%) rename {pluto => pluto-plugin}/src/main/res/anim/pluto___fragment_default_exit.xml (100%) rename {pluto => pluto-plugin}/src/main/res/anim/pluto___fragment_default_reenter.xml (100%) rename {pluto => pluto-plugin}/src/main/res/anim/pluto___fragment_default_return.xml (100%) rename {pluto => pluto-plugin}/src/main/res/drawable/pluto___bg_bottom_sheet.xml (79%) create mode 100644 pluto-plugin/src/main/res/drawable/pluto___ic_plugin_placeholder_icon.xml rename {pluto => pluto-plugin}/src/main/res/drawable/pluto___ic_search.xml (100%) rename {pluto => pluto-plugin}/src/main/res/drawable/pluto___ic_share_as_copy.xml (100%) rename {pluto => pluto-plugin}/src/main/res/drawable/pluto___ic_share_as_file.xml (100%) rename {pluto => pluto-plugin}/src/main/res/drawable/pluto___ic_share_as_text.xml (100%) rename {pluto => pluto-plugin}/src/main/res/drawable/pluto___line_divider.xml (100%) rename {pluto => pluto-plugin}/src/main/res/font/muli.ttf (100%) rename {pluto => pluto-plugin}/src/main/res/font/muli_bold.ttf (100%) rename {pluto => pluto-plugin}/src/main/res/font/muli_light.ttf (100%) rename {pluto => pluto-plugin}/src/main/res/font/muli_semibold.ttf (100%) rename {pluto => pluto-plugin}/src/main/res/layout/pluto___layout_content_share_options.xml (99%) rename {pluto => pluto-plugin}/src/main/res/values/colors.xml (81%) create mode 100644 pluto-plugin/src/main/res/values/dimens.xml rename {pluto => pluto-plugin}/src/main/res/values/integers.xml (100%) create mode 100644 pluto-plugin/src/main/res/values/strings.xml create mode 100644 pluto-plugin/src/main/res/values/styles.xml delete mode 100644 pluto/consumer-rules.pro delete mode 100644 pluto/proguard-rules.pro delete mode 100644 pluto/src/androidTest/java/com/mocklets/pluto/ExampleInstrumentedTest.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/Pluto.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/PlutoInterceptor.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/PlutoLog.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/core/DeviceInfo.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/core/RootUtil.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/core/Session.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/core/database/DatabaseManager.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/core/database/PlutoDatabase.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/core/extensions/ConcurrencyKtx.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/core/extensions/CustomTabKtx.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/core/extensions/FragmentKtx.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/core/preferences/Preferences.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/core/ui/routing/OnBackKeyHandler.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/core/ui/routing/RouteManager.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/core/ui/routing/Router.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/core/ui/routing/Utilities.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/activities/ActivityTracker.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/appstate/AppStateFragment.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/appstate/AppStateItemAdapter.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/appstate/AppStateItemHolder.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/appstate/AppStateViewModel.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/exceptions/ANRException.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/exceptions/ANRListener.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/exceptions/DataModel.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/exceptions/ExceptionRepo.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/exceptions/anrs/AnrSupervisor.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/exceptions/anrs/AnrSupervisorCallback.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/exceptions/anrs/AnrSupervisorRunnable.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/exceptions/crashes/CrashHandler.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/exceptions/crashes/CrashNotification.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/exceptions/dao/EntityConverters.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/exceptions/dao/ExceptionDao.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/exceptions/dao/ExceptionEntity.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/exceptions/ui/CrashDetailsFragment.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/exceptions/ui/CrashesAdapter.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/exceptions/ui/CrashesFragment.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/exceptions/ui/CrashesViewModel.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/exceptions/ui/holder/CrashItemDetailsDeviceHolder.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/exceptions/ui/holder/CrashItemDetailsHeaderHolder.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/exceptions/ui/holder/CrashItemDetailsThreadHolder.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/exceptions/ui/holder/CrashItemDetailsThreadStatesHolder.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/exceptions/ui/holder/CrashItemDetailsThreadStatesItemHolder.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/exceptions/ui/holder/CrashItemHolder.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/logging/ui/LogDetailsDialog.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/logging/ui/LogsViewModel.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/network/BodyTransformer.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/network/CurlBuilder.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/network/DataModel.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/network/MediaTypeKtx.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/network/NetworkCallsRepo.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/network/interceptor/CacheDirectoryProvider.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/network/interceptor/DataConvertor.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/network/interceptor/DepletingSource.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/network/interceptor/FileFactory.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/network/interceptor/OkHttpKtx.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/network/interceptor/ReportingSink.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/network/interceptor/ResponseBodyProcessor.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/network/interceptor/TeeSource.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/network/proxy/NetworkProxyRepo.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/network/proxy/NetworkProxyViewModel.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/network/proxy/dao/NetworkProxyConverters.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/network/proxy/dao/NetworkProxyDao.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/network/proxy/dao/NetworkProxyEntity.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/network/proxy/ui/NetworkProxyItemAdapter.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/network/proxy/ui/NetworkProxyItemHolder.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/network/proxy/ui/NetworkProxySettingsFragment.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/network/proxy/ui/NetworkProxySettingsListFragment.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/network/transformers/BaseTransformer.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/network/transformers/FormEncodedTransformer.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/network/transformers/JsonBaseTransformer.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/network/transformers/XmlBaseTransformer.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/network/ui/ApiItemHolder.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/network/ui/NetworkAdapter.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/network/ui/NetworkFragment.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/network/ui/NetworkViewModel.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/network/ui/details/NetworkCallDetailsFragment.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/network/ui/details/NetworkDetailsOverviewFragment.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/network/ui/details/NetworkDetailsPagerAdapter.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/network/ui/details/NetworkDetailsRequestFragment.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/network/ui/details/NetworkDetailsResponseFragment.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/preferences/SharedPrefRepo.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/preferences/ui/DataModel.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/preferences/ui/SharedPrefAdapter.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/preferences/ui/SharedPrefEditDialog.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/preferences/ui/SharedPrefFragment.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/preferences/ui/SharedPrefKeyValueItemHolder.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/preferences/ui/SharedPrefViewModel.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/preferences/ui/filter/SharedPrefFilterFragment.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/preferences/ui/filter/SharedPrefFilterItemHolder.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/settings/DataModel.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/settings/SettingsAdapter.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/settings/SettingsFragment.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/settings/holders/SettingsClearDataHolder.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/settings/holders/SettingsEasyAccessHolder.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/settings/holders/SettingsMockletsLinkHolder.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/settings/holders/SettingsResetAllHolder.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/settings/holders/SettingsSharePreferenceHolder.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/setup/easyaccess/EasyAccessSetupDialog.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/modules/setup/easyaccess/Popup.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/ui/AboutFragment.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/ui/BaseFragment.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/ui/PlutoActivity.kt delete mode 100644 pluto/src/main/java/com/mocklets/pluto/ui/SectionsPagerAdapter.kt rename pluto/src/main/java/com/{mocklets/pluto/modules/setup/SetupNotification.kt => pluto/DebugNotification.kt} (58%) create mode 100644 pluto/src/main/java/com/pluto/Pluto.kt rename pluto/src/main/java/com/{mocklets/pluto/core => pluto}/PlutoFileProvider.kt (74%) create mode 100644 pluto/src/main/java/com/pluto/Session.kt create mode 100644 pluto/src/main/java/com/pluto/applifecycle/AppLifecycle.kt create mode 100644 pluto/src/main/java/com/pluto/applifecycle/AppState.kt create mode 100644 pluto/src/main/java/com/pluto/notch/Notch.kt rename pluto/src/main/java/com/{mocklets/pluto/modules/setup/easyaccess/PopupViewManager.kt => pluto/notch/NotchViewManager.kt} (72%) rename pluto/src/main/java/com/{mocklets/pluto/modules/setup/easyaccess/OnPopupInteractionListener.kt => pluto/notch/OnNotchInteractionListener.kt} (54%) rename pluto/src/main/java/com/{mocklets/pluto/core => pluto}/notification/NotificationUtil.kt (96%) create mode 100644 pluto/src/main/java/com/pluto/plugin/PluginManager.kt create mode 100644 pluto/src/main/java/com/pluto/plugin/PluginSelectorActivity.kt create mode 100644 pluto/src/main/java/com/pluto/plugin/PluginsViewModel.kt create mode 100644 pluto/src/main/java/com/pluto/plugin/list/PluginAdapter.kt create mode 100644 pluto/src/main/java/com/pluto/plugin/list/PluginItemHolder.kt create mode 100644 pluto/src/main/java/com/pluto/plugin/options/PluginOptionAdapter.kt create mode 100644 pluto/src/main/java/com/pluto/plugin/options/PluginOptionItemHolder.kt create mode 100644 pluto/src/main/java/com/pluto/settings/DataModel.kt create mode 100644 pluto/src/main/java/com/pluto/settings/OverConsentFragment.kt create mode 100644 pluto/src/main/java/com/pluto/settings/SettingsAdapter.kt create mode 100644 pluto/src/main/java/com/pluto/settings/SettingsFragment.kt rename pluto/src/main/java/com/{mocklets/pluto/core/extensions => pluto/settings}/SettingsKtx.kt (93%) create mode 100644 pluto/src/main/java/com/pluto/settings/SettingsPreferences.kt rename pluto/src/main/java/com/{mocklets/pluto/modules => pluto}/settings/SettingsViewModel.kt (56%) create mode 100644 pluto/src/main/java/com/pluto/settings/holders/SettingsEasyAccessHolder.kt rename pluto/src/main/java/com/{mocklets/pluto/modules => pluto}/settings/holders/SettingsEasyAccessPopupAppearanceHolder.kt (56%) create mode 100644 pluto/src/main/java/com/pluto/settings/holders/SettingsResetAllHolder.kt create mode 100644 pluto/src/main/java/com/pluto/ui/PluginOptionsDialog.kt create mode 100644 pluto/src/main/java/com/pluto/ui/PlutoActivity.kt create mode 100644 pluto/src/main/java/com/pluto/ui/RoundedFrameLayout.kt create mode 100644 pluto/src/main/res/anim/pluto___click_bounce.xml create mode 100644 pluto/src/main/res/anim/pluto___fade_in_dialog.xml create mode 100644 pluto/src/main/res/anim/pluto___fade_out_dialog.xml create mode 100644 pluto/src/main/res/anim/pluto___slide_in_bottom.xml create mode 100644 pluto/src/main/res/anim/pluto___slide_out_bottom.xml delete mode 100644 pluto/src/main/res/color/pluto___bg_strip_button_dark.xml delete mode 100644 pluto/src/main/res/drawable/pluto___bg_cta_dashed_blue.xml delete mode 100644 pluto/src/main/res/drawable/pluto___bg_section_body.xml delete mode 100644 pluto/src/main/res/drawable/pluto___bg_section_header.xml delete mode 100644 pluto/src/main/res/drawable/pluto___bg_section_light.xml delete mode 100644 pluto/src/main/res/drawable/pluto___bg_shared_pref_file_badge.xml create mode 100644 pluto/src/main/res/drawable/pluto___ic_about.xml delete mode 100644 pluto/src/main/res/drawable/pluto___ic_anr_warning.xml delete mode 100644 pluto/src/main/res/drawable/pluto___ic_arrow_back.xml rename pluto/src/main/res/drawable/{pluto___ic_check_light.xml => pluto___ic_back.xml} (58%) delete mode 100644 pluto/src/main/res/drawable/pluto___ic_chevron_right.xml delete mode 100644 pluto/src/main/res/drawable/pluto___ic_close_gray.xml delete mode 100644 pluto/src/main/res/drawable/pluto___ic_copy.xml delete mode 100644 pluto/src/main/res/drawable/pluto___ic_debugger.xml delete mode 100644 pluto/src/main/res/drawable/pluto___ic_delete.xml delete mode 100644 pluto/src/main/res/drawable/pluto___ic_done.xml delete mode 100644 pluto/src/main/res/drawable/pluto___ic_filter.xml create mode 100644 pluto/src/main/res/drawable/pluto___ic_grid.xml create mode 100644 pluto/src/main/res/drawable/pluto___ic_grid_dark.xml delete mode 100644 pluto/src/main/res/drawable/pluto___ic_key.xml rename pluto/src/main/res/drawable/{pluto___ic_more_vertical.xml => pluto___ic_more.xml} (82%) delete mode 100644 pluto/src/main/res/drawable/pluto___ic_proxy_base_request.xml delete mode 100644 pluto/src/main/res/drawable/pluto___ic_proxy_base_request_dark.xml delete mode 100644 pluto/src/main/res/drawable/pluto___ic_proxy_indicator.xml delete mode 100644 pluto/src/main/res/drawable/pluto___ic_proxy_settings.xml delete mode 100644 pluto/src/main/res/drawable/pluto___ic_proxy_settings_disabled.xml delete mode 100644 pluto/src/main/res/drawable/pluto___ic_proxy_settings_enabled.xml delete mode 100644 pluto/src/main/res/drawable/pluto___ic_share.xml delete mode 100644 pluto/src/main/res/drawable/pluto___ic_success.xml delete mode 100644 pluto/src/main/res/drawable/pluto___ic_user_properties.xml create mode 100644 pluto/src/main/res/drawable/pluto___ic_vcs.xml create mode 100644 pluto/src/main/res/drawable/pluto___ic_website.xml create mode 100644 pluto/src/main/res/layout/pluto___activity_plugin_selector.xml delete mode 100644 pluto/src/main/res/layout/pluto___fragment_about.xml delete mode 100644 pluto/src/main/res/layout/pluto___fragment_app_state.xml delete mode 100644 pluto/src/main/res/layout/pluto___fragment_base.xml delete mode 100644 pluto/src/main/res/layout/pluto___fragment_crash_details.xml delete mode 100644 pluto/src/main/res/layout/pluto___fragment_crashes.xml delete mode 100644 pluto/src/main/res/layout/pluto___fragment_logs.xml delete mode 100644 pluto/src/main/res/layout/pluto___fragment_network.xml delete mode 100644 pluto/src/main/res/layout/pluto___fragment_network_details_overview.xml delete mode 100644 pluto/src/main/res/layout/pluto___fragment_network_details_request.xml delete mode 100644 pluto/src/main/res/layout/pluto___fragment_network_details_response.xml delete mode 100644 pluto/src/main/res/layout/pluto___fragment_network_proxy_settings.xml delete mode 100644 pluto/src/main/res/layout/pluto___fragment_network_proxy_settings_list.xml create mode 100644 pluto/src/main/res/layout/pluto___fragment_overlay_consent.xml delete mode 100644 pluto/src/main/res/layout/pluto___fragment_shared_pref.xml delete mode 100644 pluto/src/main/res/layout/pluto___fragment_shared_pref_filter.xml delete mode 100644 pluto/src/main/res/layout/pluto___item_anr_thread_state.xml delete mode 100644 pluto/src/main/res/layout/pluto___item_app_state.xml delete mode 100644 pluto/src/main/res/layout/pluto___item_crash.xml delete mode 100644 pluto/src/main/res/layout/pluto___item_crash_details_device.xml delete mode 100644 pluto/src/main/res/layout/pluto___item_crash_details_header.xml delete mode 100644 pluto/src/main/res/layout/pluto___item_crash_details_thread.xml delete mode 100644 pluto/src/main/res/layout/pluto___item_crash_details_thread_states.xml delete mode 100644 pluto/src/main/res/layout/pluto___item_network.xml delete mode 100644 pluto/src/main/res/layout/pluto___item_network_proxy_settings.xml create mode 100644 pluto/src/main/res/layout/pluto___item_plugin.xml rename pluto/src/main/res/layout/{pluto___item_shared_pref_filter.xml => pluto___item_plugin_option.xml} (51%) delete mode 100644 pluto/src/main/res/layout/pluto___item_settings_clear_data.xml delete mode 100644 pluto/src/main/res/layout/pluto___item_settings_link_mocklets.xml delete mode 100644 pluto/src/main/res/layout/pluto___item_settings_shared_pref.xml delete mode 100644 pluto/src/main/res/layout/pluto___item_shared_pref_key_value.xml delete mode 100644 pluto/src/main/res/layout/pluto___layout_easy_access_setup.xml delete mode 100644 pluto/src/main/res/layout/pluto___layout_network_call_details.xml create mode 100644 pluto/src/main/res/layout/pluto___layout_notch.xml create mode 100644 pluto/src/main/res/layout/pluto___layout_plugin_options_dialog.xml delete mode 100644 pluto/src/main/res/layout/pluto___layout_popup.xml delete mode 100644 pluto/src/main/res/layout/pluto___layout_shared_pref_edit.xml delete mode 100644 pluto/src/main/res/layout/pluto___stub_copy_curl.xml delete mode 100644 pluto/src/main/res/layout/pluto___stub_crash_report.xml delete mode 100644 pluto/src/main/res/layout/pluto___stub_network_settings.xml delete mode 100644 pluto/src/main/res/menu/pluto___popup_menu_crashes.xml delete mode 100644 pluto/src/main/res/menu/pluto___popup_menu_network.xml delete mode 100644 pluto/src/main/res/menu/pluto___popup_menu_shared_pref.xml delete mode 100644 pluto/src/test/java/com/mocklets/pluto/ExampleUnitTest.kt rename build-utils.gradle => scripts/build/utils.gradle (100%) rename scripts/{publish-module.gradle => publish/module.gradle} (98%) rename scripts/{publish-root.gradle => publish/root.gradle} (100%) rename {static-analysis => scripts/static-analysis}/.editorconfig (100%) rename {static-analysis => scripts/static-analysis}/android-lint-config.xml (100%) rename {static-analysis => scripts/static-analysis}/code-analysis.gradle (84%) rename {static-analysis => scripts/static-analysis}/detekt-config.yml (100%) diff --git a/build.gradle b/build.gradle index 04d5de07..0bff117c 100644 --- a/build.gradle +++ b/build.gradle @@ -12,8 +12,8 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:4.2.2' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath 'com.android.tools.build:gradle:7.0.4' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10" classpath "com.github.dcendents:android-maven-gradle-plugin:2.1" // maven central dependencies @@ -21,7 +21,7 @@ buildscript { classpath 'org.jetbrains.dokka:dokka-gradle-plugin:1.4.30' // ktlint & detekt dependencies - classpath "org.jlleitschuh.gradle:ktlint-gradle:9.4.0" + classpath "org.jlleitschuh.gradle:ktlint-gradle:10.2.0" classpath "io.gitlab.arturbosch.detekt:detekt-gradle-plugin:$detekt_version" } } @@ -34,22 +34,21 @@ allprojects { } subprojects { - if (project.name != "pluto-no-op") { +// if (project.name != "pluto-no-op") { pluginManager.withPlugin('kotlin-android') { - apply from: "$rootDir/static-analysis/code-analysis.gradle" + apply from: "$rootDir/scripts/static-analysis/code-analysis.gradle" } - } +// } } ext { - minSdkVersion = 19 - targetSdkVersion = 29 - compileSdkVersion = 29 - buildToolsVersion = '29.0.3' + minSdkVersion = 21 + targetSdkVersion = 32 + compileSdkVersion = 32 + buildToolsVersion = '32.0.0' - androidXCoreVersion = '1.5.0' - okhttpVersion = '3.14.4' - roomsVersion = '2.3.0' + androidXCoreVersion = '1.7.0' + moshiVersion = '1.13.0' } task installGitHook(type: Copy) { @@ -72,5 +71,5 @@ task prCheck { dependsOn ':pluto:validateChanges' } -apply from: "${rootDir}/scripts/publish-root.gradle" -apply from: "${rootDir}/scripts/project-dependancy-graph.gradle" \ No newline at end of file +apply from: "$rootDir/scripts/publish/root.gradle" +apply from: "$rootDir/scripts/project-dependancy-graph.gradle" \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index c99d7347..fb67fa79 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Fri Dec 25 12:26:30 IST 2020 +#Tue Feb 08 21:06:21 IST 2022 distributionBase=GRADLE_USER_HOME +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip +zipStoreBase=GRADLE_USER_HOME diff --git a/pluto-no-op/.gitignore b/plugin-logger/.gitignore similarity index 100% rename from pluto-no-op/.gitignore rename to plugin-logger/.gitignore diff --git a/plugin-logger/build.gradle b/plugin-logger/build.gradle new file mode 100644 index 00000000..c7a5673c --- /dev/null +++ b/plugin-logger/build.gradle @@ -0,0 +1,70 @@ +plugins { + id 'com.android.library' + id 'kotlin-android' + id 'kotlin-kapt' +} + +apply from: "$rootDir/scripts/build/utils.gradle" +apply from: "$rootDir/scripts/publish/module.gradle" + +def verCode, verName, verBuild, verNameShort, verPublish +(verCode, verName, verBuild, verNameShort, verPublish) = genVersion() + +ext { + PUBLISH_GROUP_ID = "com.plutolib.plugins" + PUBLISH_VERSION = verPublish + PUBLISH_ARTIFACT_ID = 'logger' +} + +android { + compileSdkVersion rootProject.compileSdkVersion + buildToolsVersion rootProject.buildToolsVersion + + buildFeatures { + viewBinding true + } + + lintOptions { + abortOnError false + } + + defaultConfig { + minSdkVersion rootProject.minSdkVersion + targetSdkVersion rootProject.targetSdkVersion + versionCode verCode + versionName verName + } + + buildTypes { + release { + debuggable true + minifyEnabled false + shrinkResources false + } + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = JavaVersion.VERSION_1_8.toString() + } + + resourcePrefix 'pluto_logger___' +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation project(path: ':pluto-plugin') + implementation 'androidx.navigation:navigation-ui-ktx:2.4.0' + api 'com.jakewharton.timber:timber:5.0.1' + implementation "com.squareup.moshi:moshi:$moshiVersion" + kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshiVersion" +} + +task validateChanges { + dependsOn 'ktlintFormat' + dependsOn 'detekt' +} \ No newline at end of file diff --git a/plugin-logger/src/main/AndroidManifest.xml b/plugin-logger/src/main/AndroidManifest.xml new file mode 100644 index 00000000..7e2e2b31 --- /dev/null +++ b/plugin-logger/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/plugin-logger/src/main/java/com/pluto/logger/PlutoLog.kt b/plugin-logger/src/main/java/com/pluto/logger/PlutoLog.kt new file mode 100644 index 00000000..ae70b605 --- /dev/null +++ b/plugin-logger/src/main/java/com/pluto/logger/PlutoLog.kt @@ -0,0 +1,57 @@ +package com.pluto.logger + +import android.util.Log +import androidx.annotation.Keep +import com.pluto.logger.internal.LogsProcessor.Companion.process +import com.pluto.logger.internal.LogsProcessor.Companion.processEvent +import com.pluto.logger.internal.LogsProcessor.Companion.stackTraceElement + +@Keep +class PlutoLog private constructor() { + + companion object { + + @JvmStatic + fun v(tag: String, message: String, tr: Throwable? = null) { + process(Log.VERBOSE, tag, message, tr, Thread.currentThread().getStackTraceElement()) + } + + @JvmStatic + fun d(tag: String, message: String, tr: Throwable? = null) { + process(Log.DEBUG, tag, message, tr, Thread.currentThread().getStackTraceElement()) + } + + @JvmStatic + fun i(tag: String, message: String, tr: Throwable? = null) { + process(Log.INFO, tag, message, tr, Thread.currentThread().getStackTraceElement()) + } + + @JvmStatic + fun w(tag: String, message: String, tr: Throwable? = null) { + process(Log.WARN, tag, message, tr, Thread.currentThread().getStackTraceElement()) + } + + @JvmStatic + fun e(tag: String, message: String, tr: Throwable? = null) { + process(Log.ERROR, tag, message, tr, Thread.currentThread().getStackTraceElement()) + } + + @JvmStatic + fun wtf(tag: String, message: String, tr: Throwable? = null) { + process(Log.ASSERT, tag, message, tr, Thread.currentThread().getStackTraceElement()) + } + + @JvmStatic + fun event(tag: String, event: String, attributes: HashMap?) { + processEvent(tag, event, attributes, Thread.currentThread().getStackTraceElement()) + } + + private fun Thread.getStackTraceElement(): StackTraceElement { + val index = if (name == "main") MAIN_THREAD_INDEX else DAEMON_THREAD_INDEX + return stackTraceElement(index) + } + + private const val MAIN_THREAD_INDEX = 6 + private const val DAEMON_THREAD_INDEX = 5 + } +} diff --git a/plugin-logger/src/main/java/com/pluto/logger/PlutoLoggerPlugin.kt b/plugin-logger/src/main/java/com/pluto/logger/PlutoLoggerPlugin.kt new file mode 100644 index 00000000..6e9ed915 --- /dev/null +++ b/plugin-logger/src/main/java/com/pluto/logger/PlutoLoggerPlugin.kt @@ -0,0 +1,32 @@ +package com.pluto.logger + +import androidx.fragment.app.Fragment +import com.pluto.logger.internal.LogsFragment +import com.pluto.logger.internal.LogsRepo +import com.pluto.plugin.DeveloperDetails +import com.pluto.plugin.Plugin +import com.pluto.plugin.PluginConfiguration + +class PlutoLoggerPlugin(identifier: String) : Plugin(identifier) { + + override fun getConfig(): PluginConfiguration = PluginConfiguration( + name = context.getString(R.string.pluto_logger___plugin_name), + icon = R.drawable.pluto_logger___ic_logger_icon + ) + + override fun getView(): Fragment = LogsFragment() + + override fun getDeveloperDetails(): DeveloperDetails { + return DeveloperDetails( + website = "https://plutolib.com", + vcsLink = "https://github.com/plutolib/plugin-logger" + ) + } + + override fun onPluginInstalled() { + } + + override fun onPluginDataCleared() { + LogsRepo.deleteAll() + } +} diff --git a/plugin-logger/src/main/java/com/pluto/logger/PlutoTimberTree.kt b/plugin-logger/src/main/java/com/pluto/logger/PlutoTimberTree.kt new file mode 100644 index 00000000..ce8fa6e2 --- /dev/null +++ b/plugin-logger/src/main/java/com/pluto/logger/PlutoTimberTree.kt @@ -0,0 +1,77 @@ +package com.pluto.logger + +import com.pluto.logger.internal.LogsProcessor +import com.pluto.logger.internal.LogsProcessor.Companion.LOG_EVENT_PRIORITY +import com.pluto.logger.internal.LogsProcessor.Companion.stackTraceElement +import com.squareup.moshi.JsonAdapter +import com.squareup.moshi.Moshi +import com.squareup.moshi.Types +import java.math.BigDecimal +import timber.log.Timber + +class PlutoTimberTree : Timber.Tree() { + + override fun log(priority: Int, tag: String?, message: String, t: Throwable?) { + if (priority == LOG_EVENT_PRIORITY) { + val eventData = eventExtractor(message) + LogsProcessor.processEvent(tag ?: "pluto_timber", eventData.first, eventData.second, Thread.currentThread().getStackTraceElement()) + } else { + LogsProcessor.process(priority, tag ?: "pluto_timber", messageExtractor(message), t, Thread.currentThread().getStackTraceElement()) + } + } + + @SuppressWarnings("NestedBlockDepth") + private fun eventExtractor(message: String): Pair?> { + val length = message.length + var newline = message.indexOf('\t', 0) + newline = if (newline != -1) newline else length + val end = newline.coerceAtMost(MAX_LOG_LENGTH) + + val event = message.substring(0, end) + val attrString = if (end < length) { + val moshi = Moshi.Builder().build() + val moshiAdapter: JsonAdapter?> = moshi.adapter(Types.newParameterizedType(Map::class.java, String::class.java, Any::class.java)) + val map = moshiAdapter.fromJson(message.substring(end + 1, length)) + val hashMap = HashMap() + if (!map.isNullOrEmpty()) { + map.entries.forEach { + hashMap[it.key] = when (it.value) { + is Double -> BigDecimal(it.value.toString()).longValueExact() + else -> it.value + } + } + hashMap + } else { + null + } + } else { + null + } + return Pair(event, attrString) + } + + private fun messageExtractor(message: String): String { + val length = message.length + var newline = message.indexOf('\n', 0) + newline = if (newline != -1) newline else length + val end = newline.coerceAtMost(MAX_LOG_LENGTH) + return message.substring(0, end) + } + + private fun Thread.getStackTraceElement(): StackTraceElement { + val index = if (name == "main") MAIN_THREAD_INDEX else DAEMON_THREAD_INDEX + return stackTraceElement(index) + } + + companion object { + private const val MAX_LOG_LENGTH = 4000 + private const val MAIN_THREAD_INDEX = 9 + private const val DAEMON_THREAD_INDEX = 8 + } +} + +fun Timber.Tree.event(event: String, attr: HashMap?) { + val moshi = Moshi.Builder().build() + val moshiAdapter: JsonAdapter?> = moshi.adapter(Types.newParameterizedType(Map::class.java, String::class.java, Any::class.java)) + log(LOG_EVENT_PRIORITY, "$event\t${moshiAdapter.toJson(attr)}") +} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/logging/DataModel.kt b/plugin-logger/src/main/java/com/pluto/logger/internal/DataModel.kt similarity index 77% rename from pluto/src/main/java/com/mocklets/pluto/modules/logging/DataModel.kt rename to plugin-logger/src/main/java/com/pluto/logger/internal/DataModel.kt index de5d6952..a0094a08 100644 --- a/pluto/src/main/java/com/mocklets/pluto/modules/logging/DataModel.kt +++ b/plugin-logger/src/main/java/com/pluto/logger/internal/DataModel.kt @@ -1,8 +1,9 @@ -package com.mocklets.pluto.modules.logging +package com.pluto.logger.internal import androidx.annotation.DrawableRes -import com.mocklets.pluto.R -import com.mocklets.pluto.core.ui.list.ListItem +import androidx.annotation.Keep +import com.pluto.logger.R +import com.pluto.plugin.utilities.list.ListItem internal sealed class Level( val label: String, @@ -14,10 +15,12 @@ internal sealed class Level( object Debug : Level("debug") object Info : Level("info") object Warning : Level("warning") + object WTF : Level("wtf") object Error : Level("error", R.color.pluto___red_05, R.color.pluto___red_80) - object Event : Level(label = "event", iconRes = R.drawable.pluto___ic_analytics, textColor = R.color.pluto___blue) + object Event : Level(label = "event", iconRes = R.drawable.pluto_logger___ic_analytics, textColor = R.color.pluto___blue) } +@Keep internal data class LogData( val level: Level, val tag: String, diff --git a/plugin-logger/src/main/java/com/pluto/logger/internal/LogsFragment.kt b/plugin-logger/src/main/java/com/pluto/logger/internal/LogsFragment.kt new file mode 100644 index 00000000..8916de06 --- /dev/null +++ b/plugin-logger/src/main/java/com/pluto/logger/internal/LogsFragment.kt @@ -0,0 +1,6 @@ +package com.pluto.logger.internal + +import androidx.fragment.app.Fragment +import com.pluto.logger.R + +internal class LogsFragment : Fragment(R.layout.pluto_logger___fragment_logs) diff --git a/plugin-logger/src/main/java/com/pluto/logger/internal/LogsProcessor.kt b/plugin-logger/src/main/java/com/pluto/logger/internal/LogsProcessor.kt new file mode 100644 index 00000000..0e4a793c --- /dev/null +++ b/plugin-logger/src/main/java/com/pluto/logger/internal/LogsProcessor.kt @@ -0,0 +1,71 @@ +package com.pluto.logger.internal + +import android.util.Log +import androidx.annotation.Keep +import com.pluto.logger.BuildConfig +import com.squareup.moshi.JsonAdapter +import com.squareup.moshi.Moshi +import com.squareup.moshi.Types + +internal class LogsProcessor private constructor() { + + companion object { + + fun process(priority: Int, tag: String, message: String, tr: Throwable?, stackTrace: StackTraceElement) { + LogsRepo.save(priority2Level(priority), tag, message, tr, stackTrace) + consolePrint(priority2Level(priority), tag, message, tr, stackTrace) + } + + fun processEvent(tag: String, event: String, attr: HashMap?, stackTrace: StackTraceElement) { + val moshi = Moshi.Builder().build() + val moshiAdapter: JsonAdapter?> = moshi.adapter(Types.newParameterizedType(Map::class.java, String::class.java, Any::class.java)) + LogsRepo.saveEvent(Level.Event, tag, event, attr, stackTrace) + consolePrint(Level.Event, tag, "$event => ${moshiAdapter.toJson(attr)}", null, stackTrace) + } + + @SuppressWarnings("ComplexCondition") + fun Thread.stackTraceElement(index: Int): StackTraceElement { + stackTrace.forEach { + if (!it.className.startsWith(BuildConfig.LIBRARY_PACKAGE_NAME) && + !it.className.startsWith("java.lang.") && + !it.className.startsWith("dalvik.system.") && + !it.fileName.startsWith("Timber.kt") + ) { + return it + } + } + return stackTrace[index] + } + + private fun priority2Level(priority: Int): Level { + return when (priority) { + Log.DEBUG -> Level.Debug + Log.ERROR -> Level.Error + Log.INFO -> Level.Info + Log.VERBOSE -> Level.Verbose + Log.WARN -> Level.Warning + Log.ASSERT -> Level.WTF + LOG_EVENT_PRIORITY -> Level.Event + else -> Level.Debug + } + } + + private fun consolePrint(level: Level, tag: String, message: String, tr: Throwable?, trace: StackTraceElement) { + val logTag = "${trace.formattedStack()} | $tag" + when (level) { + is Level.Debug -> Log.v(logTag, message, tr) + is Level.Error -> Log.e(logTag, message, tr) + is Level.Info -> Log.i(logTag, message, tr) + is Level.Warning -> Log.w(logTag, message, tr) + is Level.Verbose -> Log.v(logTag, message, tr) + is Level.WTF -> Log.wtf(logTag, message, tr) + is Level.Event -> Log.d(logTag, message) + } + } + + @Keep + fun StackTraceElement.formattedStack(): String = "$methodName($fileName:$lineNumber)" + + const val LOG_EVENT_PRIORITY = 101 + } +} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/logging/LogsRepo.kt b/plugin-logger/src/main/java/com/pluto/logger/internal/LogsRepo.kt similarity index 75% rename from pluto/src/main/java/com/mocklets/pluto/modules/logging/LogsRepo.kt rename to plugin-logger/src/main/java/com/pluto/logger/internal/LogsRepo.kt index d7ecad0d..dc130f27 100644 --- a/pluto/src/main/java/com/mocklets/pluto/modules/logging/LogsRepo.kt +++ b/plugin-logger/src/main/java/com/pluto/logger/internal/LogsRepo.kt @@ -1,10 +1,13 @@ -package com.mocklets.pluto.modules.logging +package com.pluto.logger.internal +import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData internal object LogsRepo { - private val logs = MutableLiveData>() + internal val logs: LiveData> + get() = _logs + private val _logs = MutableLiveData>() private val logsList = arrayListOf() private const val MAX_LIMIT = 256 @@ -14,7 +17,7 @@ internal object LogsRepo { val temp = logsList.take(MAX_LIMIT) logsList.clear() logsList.addAll(temp) - logs.postValue(logsList) + _logs.postValue(logsList) } } @@ -29,13 +32,11 @@ internal object LogsRepo { val temp = logsList.take(MAX_LIMIT) logsList.clear() logsList.addAll(temp) - logs.postValue(logsList) + _logs.postValue(logsList) } - fun getLogsLD() = logs - fun deleteAll() { logsList.clear() - logs.postValue(logsList) + _logs.postValue(logsList) } } diff --git a/plugin-logger/src/main/java/com/pluto/logger/internal/LogsViewModel.kt b/plugin-logger/src/main/java/com/pluto/logger/internal/LogsViewModel.kt new file mode 100644 index 00000000..446a8797 --- /dev/null +++ b/plugin-logger/src/main/java/com/pluto/logger/internal/LogsViewModel.kt @@ -0,0 +1,18 @@ +package com.pluto.logger.internal + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel + +internal class LogsViewModel : ViewModel() { + val logs: LiveData> + get() = LogsRepo.logs + + val current: LiveData + get() = _current + private val _current = MutableLiveData() + + internal fun updateCurrentLog(data: LogData) { + _current.postValue(data) + } +} diff --git a/plugin-logger/src/main/java/com/pluto/logger/internal/Session.kt b/plugin-logger/src/main/java/com/pluto/logger/internal/Session.kt new file mode 100644 index 00000000..555c92cf --- /dev/null +++ b/plugin-logger/src/main/java/com/pluto/logger/internal/Session.kt @@ -0,0 +1,5 @@ +package com.pluto.logger.internal + +internal object Session { + var loggerSearchText: String? = null +} diff --git a/plugin-logger/src/main/java/com/pluto/logger/internal/ThrowableKtx.kt b/plugin-logger/src/main/java/com/pluto/logger/internal/ThrowableKtx.kt new file mode 100644 index 00000000..3d51bcf2 --- /dev/null +++ b/plugin-logger/src/main/java/com/pluto/logger/internal/ThrowableKtx.kt @@ -0,0 +1,56 @@ +package com.pluto.logger.internal + +import android.content.Context +import androidx.annotation.Keep +import com.pluto.logger.R +import com.pluto.plugin.utilities.extensions.color +import com.pluto.plugin.utilities.list.ListItem +import com.pluto.plugin.utilities.spannable.createSpan + +internal fun Throwable.asExceptionData(isANR: Boolean = false): ExceptionData { + return ExceptionData( + name = this.toString().replace(": $message", "", true), + message = message, + stackTrace = stackTrace.asStringArray(), + file = stackTrace.getOrNull(0)?.fileName, + lineNumber = stackTrace.getOrNull(0)?.lineNumber ?: Int.MIN_VALUE, + isANRException = isANR + ) +} + +@Keep +internal data class ExceptionData( + val message: String?, + val name: String?, + val file: String?, + val lineNumber: Int, + val stackTrace: ArrayList, + val timeStamp: Long = System.currentTimeMillis(), + val isANRException: Boolean = false +) : ListItem() + +internal fun Array.asStringArray(): ArrayList { + val array = arrayListOf() + forEach { + if (it.isNativeMethod) { + array.add("${it.className}.${it.methodName}(Native Method)") + } else { + array.add("${it.className}.${it.methodName}(${it.fileName}:${it.lineNumber})") + } + } + return array +} + +internal fun Context?.beautifyAttributes(data: Map): CharSequence? { + return this?.createSpan { + data.forEach { + append("${it.key} : ") + if (it.value != null) { + append(fontColor(semiBold("${it.value}"), context.color(R.color.pluto___text_dark_80))) + } else { + append(fontColor(light(italic("null")), context.color(R.color.pluto___text_dark_40))) + } + append("\n") + } + }?.trim() +} diff --git a/plugin-logger/src/main/java/com/pluto/logger/internal/ui/DetailsFragment.kt b/plugin-logger/src/main/java/com/pluto/logger/internal/ui/DetailsFragment.kt new file mode 100644 index 00000000..6314e497 --- /dev/null +++ b/plugin-logger/src/main/java/com/pluto/logger/internal/ui/DetailsFragment.kt @@ -0,0 +1,137 @@ +package com.pluto.logger.internal.ui + +import android.content.Context +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.activity.OnBackPressedCallback +import androidx.fragment.app.activityViewModels +import androidx.lifecycle.Observer +import androidx.navigation.fragment.findNavController +import com.google.android.material.bottomsheet.BottomSheetDialogFragment +import com.pluto.logger.R +import com.pluto.logger.databinding.PlutoLoggerFragmentDetailsBinding +import com.pluto.logger.internal.LogData +import com.pluto.logger.internal.LogsViewModel +import com.pluto.logger.internal.asExceptionData +import com.pluto.logger.internal.beautifyAttributes +import com.pluto.logger.internal.ui.DetailsFragment.Companion.MAX_STACK_TRACE_LINES +import com.pluto.plugin.utilities.extensions.color +import com.pluto.plugin.utilities.setDebounceClickListener +import com.pluto.plugin.utilities.sharing.Shareable +import com.pluto.plugin.utilities.sharing.lazyContentSharer +import com.pluto.plugin.utilities.spannable.setSpan +import com.pluto.plugin.utilities.viewBinding + +internal class DetailsFragment : BottomSheetDialogFragment() { + + private val binding by viewBinding(PlutoLoggerFragmentDetailsBinding::bind) + private val viewModel: LogsViewModel by activityViewModels() + private val contentSharer by lazyContentSharer() + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = + inflater.inflate(R.layout.pluto_logger___fragment_details, container, false) + + override fun getTheme(): Int = R.style.PlutoLoggerBottomSheetDialog + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + requireActivity().onBackPressedDispatcher.addCallback( + viewLifecycleOwner, + object : OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + findNavController().navigateUp() + } + } + ) + +// binding.text.setDebounceClickListener { +// findNavController().popBackStack() +// } + + viewModel.current.removeObserver(detailsObserver) + viewModel.current.observe(viewLifecycleOwner, detailsObserver) + } + + private val detailsObserver = Observer { data -> + binding.title.setSpan { + append("${context.getString(R.string.pluto_logger___log_details)} ") + append(italic(fontColor(data.level.label.uppercase(), context.color(data.level.textColor)))) + } + + binding.cta.setDebounceClickListener { + context?.let { + contentSharer.share(Shareable(title = "Share Log details", content = data.toShareText(it))) + } + } + + binding.tag.text = data.tag + binding.filename.setSpan { + append(fontColor("called from\n", context.color(R.color.pluto___text_dark_40))) + append(data.stackTraceElement.methodName) + append(fontColor(" (", context.color(R.color.pluto___text_dark_40))) + append(data.stackTraceElement.fileName) + append(fontColor(", line:", context.color(R.color.pluto___text_dark_60))) + append(fontColor("${data.stackTraceElement.lineNumber}", context.color(R.color.pluto___text_dark_80))) + append(fontColor(")", context.color(R.color.pluto___text_dark_40))) + } + binding.message.text = data.message + binding.stackTraceContainer.visibility = View.GONE + data.tr?.asExceptionData()?.let { + binding.stackTraceContainer.visibility = View.VISIBLE + binding.stackTrace.setSpan { + append(fontColor("${it.name}: ${it.message}", context.color(R.color.pluto___text_dark_80))) + it.stackTrace.take(MAX_STACK_TRACE_LINES).forEach { + append("\n\t\t\t") + append(fontColor(" at ", context.color(R.color.pluto___text_dark_40))) + append(it) + } + val extraTrace = it.stackTrace.size - MAX_STACK_TRACE_LINES + if (extraTrace > 0) { + append( + fontColor( + "\n\t\t\t + $extraTrace more lines", context.color(R.color.pluto___text_dark_40) + ) + ) + } + } + } + + if (!data.eventAttributes.isNullOrEmpty()) { + binding.stackTraceContainer.visibility = View.VISIBLE + binding.stackTraceTitle.setSpan { + append(context.getString(R.string.pluto_logger___event_attributes)) + append(fontColor(" (${data.eventAttributes.size})", context.color(R.color.pluto___text_dark_40))) + } + binding.stackTrace.text = context.beautifyAttributes(data.eventAttributes) + } + } + + companion object { + const val MAX_STACK_TRACE_LINES = 15 + } +} + +private fun LogData.toShareText(context: Context): String { + val text = StringBuilder() + text.append("$tag : $message\n") + + tr?.asExceptionData()?.let { + text.append("\n${it.name}: ${it.message}\n") + it.stackTrace.take(MAX_STACK_TRACE_LINES).forEach { trace -> + text.append("\t at $trace\n") + } + if (it.stackTrace.size - MAX_STACK_TRACE_LINES > 0) { + text.append("\t + ${it.stackTrace.size - MAX_STACK_TRACE_LINES} more lines\n\n") + } + } + + if (!eventAttributes.isNullOrEmpty()) { + text.append("\n${context.getString(R.string.pluto_logger___event_attributes).lowercase()} - ") + eventAttributes.entries.forEach { + text.append("\n\t ${it.key} : ${it.value}") + } + } + return text.toString() +} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/logging/ui/LogsFragment.kt b/plugin-logger/src/main/java/com/pluto/logger/internal/ui/ListFragment.kt similarity index 50% rename from pluto/src/main/java/com/mocklets/pluto/modules/logging/ui/LogsFragment.kt rename to plugin-logger/src/main/java/com/pluto/logger/internal/ui/ListFragment.kt index 7448de9d..e6a3eb65 100644 --- a/pluto/src/main/java/com/mocklets/pluto/modules/logging/ui/LogsFragment.kt +++ b/plugin-logger/src/main/java/com/pluto/logger/internal/ui/ListFragment.kt @@ -1,32 +1,35 @@ -package com.mocklets.pluto.modules.logging.ui +package com.pluto.logger.internal.ui import android.os.Bundle import android.view.View -import androidx.core.widget.addTextChangedListener +import androidx.core.widget.doOnTextChanged import androidx.fragment.app.Fragment -import androidx.fragment.app.viewModels +import androidx.fragment.app.activityViewModels import androidx.lifecycle.Observer import androidx.lifecycle.lifecycleScope -import com.mocklets.pluto.Pluto -import com.mocklets.pluto.R -import com.mocklets.pluto.core.binding.viewBinding -import com.mocklets.pluto.core.extensions.hideKeyboard -import com.mocklets.pluto.core.extensions.linearLayoutManager -import com.mocklets.pluto.core.extensions.showMoreOptions -import com.mocklets.pluto.core.ui.list.BaseAdapter -import com.mocklets.pluto.core.ui.list.CustomItemDecorator -import com.mocklets.pluto.core.ui.list.DiffAwareAdapter -import com.mocklets.pluto.core.ui.list.DiffAwareHolder -import com.mocklets.pluto.core.ui.list.ListItem -import com.mocklets.pluto.core.ui.setDebounceClickListener -import com.mocklets.pluto.databinding.PlutoFragmentLogsBinding -import com.mocklets.pluto.modules.logging.LogData -import com.mocklets.pluto.modules.logging.LogsRepo +import androidx.navigation.fragment.findNavController +import com.pluto.logger.R +import com.pluto.logger.databinding.PlutoLoggerFragmentListBinding +import com.pluto.logger.internal.LogData +import com.pluto.logger.internal.LogsRepo +import com.pluto.logger.internal.LogsViewModel +import com.pluto.logger.internal.Session +import com.pluto.logger.internal.ui.list.LogsAdapter +import com.pluto.plugin.utilities.extensions.hideKeyboard +import com.pluto.plugin.utilities.extensions.linearLayoutManager +import com.pluto.plugin.utilities.extensions.showMoreOptions +import com.pluto.plugin.utilities.list.BaseAdapter +import com.pluto.plugin.utilities.list.CustomItemDecorator +import com.pluto.plugin.utilities.list.DiffAwareAdapter +import com.pluto.plugin.utilities.list.DiffAwareHolder +import com.pluto.plugin.utilities.list.ListItem +import com.pluto.plugin.utilities.setDebounceClickListener +import com.pluto.plugin.utilities.viewBinding -internal class LogsFragment : Fragment(R.layout.pluto___fragment_logs) { +internal class ListFragment : Fragment(R.layout.pluto_logger___fragment_list) { - private val binding by viewBinding(PlutoFragmentLogsBinding::bind) - private val viewModel: LogsViewModel by viewModels() + private val binding by viewBinding(PlutoLoggerFragmentListBinding::bind) + private val viewModel: LogsViewModel by activityViewModels() private val logsAdapter: BaseAdapter by lazy { LogsAdapter(onActionListener) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -35,10 +38,10 @@ internal class LogsFragment : Fragment(R.layout.pluto___fragment_logs) { adapter = logsAdapter addItemDecoration(CustomItemDecorator(requireContext())) } - binding.search.addTextChangedListener { editable -> - lifecycleScope.launchWhenResumed { - editable?.toString()?.let { - Pluto.session.loggerSearchText = it + binding.search.doOnTextChanged { text, _, _, _ -> + viewLifecycleOwner.lifecycleScope.launchWhenResumed { + text?.toString()?.let { + Session.loggerSearchText = it logsAdapter.list = filteredLogs(it) if (it.isEmpty()) { binding.logList.linearLayoutManager()?.scrollToPositionWithOffset(0, 0) @@ -46,16 +49,20 @@ internal class LogsFragment : Fragment(R.layout.pluto___fragment_logs) { } } } - binding.more.setDebounceClickListener { - requireContext().showMoreOptions(it, R.menu.pluto___popup_menu_logger) { item -> + binding.search.setText(Session.loggerSearchText) + viewModel.logs.removeObserver(logsObserver) + viewModel.logs.observe(viewLifecycleOwner, logsObserver) + + binding.close.setDebounceClickListener { + activity?.finish() + } + binding.options.setDebounceClickListener { + context?.showMoreOptions(it, R.menu.pluto_logger___menu_more_options) { item -> when (item.itemId) { R.id.clear -> LogsRepo.deleteAll() } } } - binding.search.setText(Pluto.session.loggerSearchText) - viewModel.logs.removeObserver(logsObserver) - viewModel.logs.observe(viewLifecycleOwner, logsObserver) } private fun filteredLogs(search: String): List { @@ -69,9 +76,9 @@ internal class LogsFragment : Fragment(R.layout.pluto___fragment_logs) { } binding.noItemText.text = getString( if (search.isNotEmpty()) { - R.string.pluto___no_search_result + R.string.pluto_logger___no_search_result } else { - R.string.pluto___no_logs_text + R.string.pluto_logger___no_logs_text } ) binding.noItemText.visibility = if (list.isEmpty()) View.VISIBLE else View.GONE @@ -86,8 +93,9 @@ internal class LogsFragment : Fragment(R.layout.pluto___fragment_logs) { override fun onAction(action: String, data: ListItem, holder: DiffAwareHolder?) { if (data is LogData) { activity?.let { - it.hideKeyboard(lifecycleScope) { - LogDetailsDialog(it, data).show() + it.hideKeyboard(viewLifecycleOwner.lifecycleScope) { + viewModel.updateCurrentLog(data) + findNavController().navigate(R.id.openDetails) } } } diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/logging/ui/LogItemHolder.kt b/plugin-logger/src/main/java/com/pluto/logger/internal/ui/list/LogItemHolder.kt similarity index 66% rename from pluto/src/main/java/com/mocklets/pluto/modules/logging/ui/LogItemHolder.kt rename to plugin-logger/src/main/java/com/pluto/logger/internal/ui/list/LogItemHolder.kt index d2a4bb39..0a57bda5 100644 --- a/pluto/src/main/java/com/mocklets/pluto/modules/logging/ui/LogItemHolder.kt +++ b/plugin-logger/src/main/java/com/pluto/logger/internal/ui/list/LogItemHolder.kt @@ -1,23 +1,23 @@ -package com.mocklets.pluto.modules.logging.ui +package com.pluto.logger.internal.ui.list import android.view.ViewGroup -import com.mocklets.pluto.R -import com.mocklets.pluto.core.extensions.asTimeElapsed -import com.mocklets.pluto.core.extensions.color -import com.mocklets.pluto.core.extensions.dp -import com.mocklets.pluto.core.extensions.inflate -import com.mocklets.pluto.core.ui.list.DiffAwareAdapter -import com.mocklets.pluto.core.ui.list.DiffAwareHolder -import com.mocklets.pluto.core.ui.list.ListItem -import com.mocklets.pluto.core.ui.setDebounceClickListener -import com.mocklets.pluto.core.ui.spannable.setSpan -import com.mocklets.pluto.databinding.PlutoItemLogBinding -import com.mocklets.pluto.modules.logging.LogData +import com.pluto.logger.R +import com.pluto.logger.databinding.PlutoLoggerListItemBinding +import com.pluto.logger.internal.LogData +import com.pluto.plugin.utilities.extensions.asTimeElapsed +import com.pluto.plugin.utilities.extensions.color +import com.pluto.plugin.utilities.extensions.dp +import com.pluto.plugin.utilities.extensions.inflate +import com.pluto.plugin.utilities.list.DiffAwareAdapter +import com.pluto.plugin.utilities.list.DiffAwareHolder +import com.pluto.plugin.utilities.list.ListItem +import com.pluto.plugin.utilities.setDebounceClickListener +import com.pluto.plugin.utilities.spannable.setSpan internal class LogItemHolder(parent: ViewGroup, actionListener: DiffAwareAdapter.OnActionListener) : - DiffAwareHolder(parent.inflate(R.layout.pluto___item_log), actionListener) { + DiffAwareHolder(parent.inflate(R.layout.pluto_logger___list_item), actionListener) { - private val binding = PlutoItemLogBinding.bind(itemView) + private val binding = PlutoLoggerListItemBinding.bind(itemView) private val logTag = binding.logtag private val message = binding.message private val timestamp = binding.timestamp diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/logging/ui/LogsAdapter.kt b/plugin-logger/src/main/java/com/pluto/logger/internal/ui/list/LogsAdapter.kt similarity index 69% rename from pluto/src/main/java/com/mocklets/pluto/modules/logging/ui/LogsAdapter.kt rename to plugin-logger/src/main/java/com/pluto/logger/internal/ui/list/LogsAdapter.kt index d332a5b2..946ea531 100644 --- a/pluto/src/main/java/com/mocklets/pluto/modules/logging/ui/LogsAdapter.kt +++ b/plugin-logger/src/main/java/com/pluto/logger/internal/ui/list/LogsAdapter.kt @@ -1,10 +1,10 @@ -package com.mocklets.pluto.modules.logging.ui +package com.pluto.logger.internal.ui.list import android.view.ViewGroup -import com.mocklets.pluto.core.ui.list.BaseAdapter -import com.mocklets.pluto.core.ui.list.DiffAwareHolder -import com.mocklets.pluto.core.ui.list.ListItem -import com.mocklets.pluto.modules.logging.LogData +import com.pluto.logger.internal.LogData +import com.pluto.plugin.utilities.list.BaseAdapter +import com.pluto.plugin.utilities.list.DiffAwareHolder +import com.pluto.plugin.utilities.list.ListItem internal class LogsAdapter(private val listener: OnActionListener) : BaseAdapter() { override fun getItemViewType(item: ListItem): Int? { diff --git a/plugin-logger/src/main/res/drawable/pluto_logger___bg_bottom_sheet.xml b/plugin-logger/src/main/res/drawable/pluto_logger___bg_bottom_sheet.xml new file mode 100644 index 00000000..48e20428 --- /dev/null +++ b/plugin-logger/src/main/res/drawable/pluto_logger___bg_bottom_sheet.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pluto/src/main/res/drawable/pluto___bg_bottom_sheet_content.xml b/plugin-logger/src/main/res/drawable/pluto_logger___bg_bottom_sheet_content.xml similarity index 78% rename from pluto/src/main/res/drawable/pluto___bg_bottom_sheet_content.xml rename to plugin-logger/src/main/res/drawable/pluto_logger___bg_bottom_sheet_content.xml index 1c754b7b..387102e1 100644 --- a/pluto/src/main/res/drawable/pluto___bg_bottom_sheet_content.xml +++ b/plugin-logger/src/main/res/drawable/pluto_logger___bg_bottom_sheet_content.xml @@ -10,8 +10,8 @@ + android:topLeftRadius="@dimen/pluto___margin_medium" + android:topRightRadius="@dimen/pluto___margin_medium" /> diff --git a/pluto/src/main/res/drawable/pluto___ic_analytics.xml b/plugin-logger/src/main/res/drawable/pluto_logger___ic_analytics.xml similarity index 100% rename from pluto/src/main/res/drawable/pluto___ic_analytics.xml rename to plugin-logger/src/main/res/drawable/pluto_logger___ic_analytics.xml diff --git a/pluto/src/main/res/drawable/pluto___ic_clear_all.xml b/plugin-logger/src/main/res/drawable/pluto_logger___ic_clear_all.xml similarity index 100% rename from pluto/src/main/res/drawable/pluto___ic_clear_all.xml rename to plugin-logger/src/main/res/drawable/pluto_logger___ic_clear_all.xml diff --git a/pluto/src/main/res/drawable/pluto___ic_label.xml b/plugin-logger/src/main/res/drawable/pluto_logger___ic_label.xml similarity index 100% rename from pluto/src/main/res/drawable/pluto___ic_label.xml rename to plugin-logger/src/main/res/drawable/pluto_logger___ic_label.xml diff --git a/plugin-logger/src/main/res/drawable/pluto_logger___ic_logger_icon.xml b/plugin-logger/src/main/res/drawable/pluto_logger___ic_logger_icon.xml new file mode 100644 index 00000000..de4e9724 --- /dev/null +++ b/plugin-logger/src/main/res/drawable/pluto_logger___ic_logger_icon.xml @@ -0,0 +1,10 @@ + + + diff --git a/pluto/src/main/res/layout/pluto___layout_log_details.xml b/plugin-logger/src/main/res/layout/pluto_logger___fragment_details.xml similarity index 91% rename from pluto/src/main/res/layout/pluto___layout_log_details.xml rename to plugin-logger/src/main/res/layout/pluto_logger___fragment_details.xml index b535dec2..0a0ffcf0 100644 --- a/pluto/src/main/res/layout/pluto___layout_log_details.xml +++ b/plugin-logger/src/main/res/layout/pluto_logger___fragment_details.xml @@ -4,21 +4,21 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@drawable/pluto___bg_bottom_sheet"> + android:background="@drawable/pluto_logger___bg_bottom_sheet"> + app:layout_constraintTop_toBottomOf="@+id/title" + app:layout_constraintVertical_bias="0"> + android:textColorHint="@color/pluto___text_dark_40" + android:textIsSelectable="true" /> diff --git a/plugin-logger/src/main/res/layout/pluto_logger___fragment_list.xml b/plugin-logger/src/main/res/layout/pluto_logger___fragment_list.xml new file mode 100644 index 00000000..f0943e90 --- /dev/null +++ b/plugin-logger/src/main/res/layout/pluto_logger___fragment_list.xml @@ -0,0 +1,140 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugin-logger/src/main/res/layout/pluto_logger___fragment_logs.xml b/plugin-logger/src/main/res/layout/pluto_logger___fragment_logs.xml new file mode 100644 index 00000000..bdd3af91 --- /dev/null +++ b/plugin-logger/src/main/res/layout/pluto_logger___fragment_logs.xml @@ -0,0 +1,15 @@ + + + + + + \ No newline at end of file diff --git a/pluto/src/main/res/layout/pluto___item_log.xml b/plugin-logger/src/main/res/layout/pluto_logger___list_item.xml similarity index 84% rename from pluto/src/main/res/layout/pluto___item_log.xml rename to plugin-logger/src/main/res/layout/pluto_logger___list_item.xml index 847088fa..d9d34656 100644 --- a/pluto/src/main/res/layout/pluto___item_log.xml +++ b/plugin-logger/src/main/res/layout/pluto_logger___list_item.xml @@ -11,9 +11,8 @@ android:id="@+id/logtag" android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_marginStart="@dimen/pluto___margin_small" + android:layout_marginHorizontal="@dimen/pluto___margin_small" android:layout_marginTop="@dimen/pluto___margin_medium" - android:layout_marginEnd="@dimen/pluto___margin_small" android:fontFamily="@font/muli_light" android:textColor="@color/pluto___text_dark_80" android:textSize="@dimen/pluto___text_xsmall" @@ -26,26 +25,24 @@ android:id="@+id/timestamp" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginEnd="@dimen/pluto___margin_small" + android:layout_marginHorizontal="@dimen/pluto___margin_small" android:fontFamily="@font/muli" android:textColor="@color/pluto___text_dark_40" android:textSize="@dimen/pluto___text_xxsmall" android:textStyle="italic" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="@+id/logtag" - android:layout_marginRight="@dimen/pluto___margin_small" tools:text="12:45:34" /> + android:icon="@drawable/pluto_logger___ic_clear_all" + android:title="@string/pluto_logger___delete_console_logs" /> \ No newline at end of file diff --git a/plugin-logger/src/main/res/navigation/pluto_logger___navigation.xml b/plugin-logger/src/main/res/navigation/pluto_logger___navigation.xml new file mode 100644 index 00000000..8479f345 --- /dev/null +++ b/plugin-logger/src/main/res/navigation/pluto_logger___navigation.xml @@ -0,0 +1,21 @@ + + + + + + + + + \ No newline at end of file diff --git a/plugin-logger/src/main/res/values/strings.xml b/plugin-logger/src/main/res/values/strings.xml new file mode 100644 index 00000000..b9d7c7fe --- /dev/null +++ b/plugin-logger/src/main/res/values/strings.xml @@ -0,0 +1,15 @@ + + Pluto Logger + No Logs printed. + Search logs + open logs menu + Exit logs debugger + Logger + Show more options + No search result found. + Log Details + Share + Exception + Event Attributes + Clear All logs + \ No newline at end of file diff --git a/plugin-logger/src/main/res/values/styles.xml b/plugin-logger/src/main/res/values/styles.xml new file mode 100644 index 00000000..ab6b4938 --- /dev/null +++ b/plugin-logger/src/main/res/values/styles.xml @@ -0,0 +1,21 @@ + + + + + + + \ No newline at end of file diff --git a/pluto-no-op/build.gradle b/pluto-no-op/build.gradle deleted file mode 100644 index 4682a1c0..00000000 --- a/pluto-no-op/build.gradle +++ /dev/null @@ -1,51 +0,0 @@ -plugins { - id 'com.android.library' - id 'kotlin-android' -} - -apply from: "$project.rootDir/build-utils.gradle" -apply from: "${rootDir}/scripts/publish-module.gradle" - -def verCode, verName, verBuild, verNameShort, verPublish -(verCode, verName, verBuild, verNameShort, verPublish) = genVersion() - -ext { - PUBLISH_GROUP_ID = "com.mocklets" - PUBLISH_VERSION = verPublish - PUBLISH_ARTIFACT_ID = 'pluto-no-op' -} - -android { - compileSdkVersion rootProject.compileSdkVersion - buildToolsVersion rootProject.buildToolsVersion - - defaultConfig { - minSdkVersion rootProject.minSdkVersion - targetSdkVersion rootProject.targetSdkVersion - versionCode verCode - versionName verName - } - - buildTypes { - release { - minifyEnabled false - } - } - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - kotlinOptions { - jvmTarget = '1.6' - } -} - -dependencies { - implementation "androidx.core:core-ktx:$androidXCoreVersion" - implementation "com.squareup.okhttp3:okhttp:$okhttpVersion" -} - -task buildAndUpload { - dependsOn 'assemble' - dependsOn 'bintrayUpload' -} \ No newline at end of file diff --git a/pluto-no-op/src/main/java/com/mocklets/pluto/Pluto.kt b/pluto-no-op/src/main/java/com/mocklets/pluto/Pluto.kt deleted file mode 100644 index 2649f165..00000000 --- a/pluto-no-op/src/main/java/com/mocklets/pluto/Pluto.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.mocklets.pluto - -import android.content.Context -import androidx.annotation.Keep -import com.mocklets.pluto.modules.exceptions.ANRListener - -@Keep -object Pluto { - - @JvmOverloads - fun initialize(context: Context, shouldShowIntroToast: Boolean = true) {} - - fun setAppProperties(properties: HashMap) {} - - fun setExceptionHandler(uncaughtExceptionHandler: Thread.UncaughtExceptionHandler) {} - - fun setANRListener(listener: ANRListener) {} - - fun showUi() {} -} diff --git a/pluto-no-op/src/main/java/com/mocklets/pluto/PlutoInterceptor.kt b/pluto-no-op/src/main/java/com/mocklets/pluto/PlutoInterceptor.kt deleted file mode 100644 index e136065b..00000000 --- a/pluto-no-op/src/main/java/com/mocklets/pluto/PlutoInterceptor.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.mocklets.pluto - -import androidx.annotation.Keep -import okhttp3.Interceptor -import okhttp3.Response - -@Keep -class PlutoInterceptor : Interceptor { - - override fun intercept(chain: Interceptor.Chain): Response { - val request = chain.request() - return chain.proceed(request) - } -} diff --git a/pluto-no-op/src/main/java/com/mocklets/pluto/PlutoLog.kt b/pluto-no-op/src/main/java/com/mocklets/pluto/PlutoLog.kt deleted file mode 100644 index 3caf7d03..00000000 --- a/pluto-no-op/src/main/java/com/mocklets/pluto/PlutoLog.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.mocklets.pluto - -import androidx.annotation.Keep - -@Keep -class PlutoLog private constructor() { - - companion object { - @JvmStatic - fun v(tag: String, message: String?, tr: Throwable? = null) {} - - @JvmStatic - fun d(tag: String, message: String?, tr: Throwable? = null) {} - - @JvmStatic - fun i(tag: String, message: String?, tr: Throwable? = null) {} - - @JvmStatic - fun w(tag: String, message: String?, tr: Throwable? = null) {} - - @JvmStatic - fun e(tag: String, message: String?, tr: Throwable? = null) {} - - @JvmStatic - fun event(tag: String, event: String, attributes: HashMap?) {} - } -} diff --git a/pluto-no-op/src/main/java/com/mocklets/pluto/modules/exceptions/ANRException.kt b/pluto-no-op/src/main/java/com/mocklets/pluto/modules/exceptions/ANRException.kt deleted file mode 100644 index ec6f9227..00000000 --- a/pluto-no-op/src/main/java/com/mocklets/pluto/modules/exceptions/ANRException.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.mocklets.pluto.modules.exceptions - -import androidx.annotation.Keep - -@Keep -class ANRException(thread: Thread) : Exception("ANR detected in Pluto-No-Op") { - - val threadStateMap: String - - init { - stackTrace = thread.stackTrace - threadStateMap = "" - } -} diff --git a/pluto-no-op/src/main/java/com/mocklets/pluto/modules/exceptions/ANRListener.kt b/pluto-no-op/src/main/java/com/mocklets/pluto/modules/exceptions/ANRListener.kt deleted file mode 100644 index 30c4d19d..00000000 --- a/pluto-no-op/src/main/java/com/mocklets/pluto/modules/exceptions/ANRListener.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.mocklets.pluto.modules.exceptions - -import androidx.annotation.Keep - -@Keep -interface ANRListener { - fun onAppNotResponding(exception: ANRException) -} diff --git a/pluto-plugin/.gitignore b/pluto-plugin/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/pluto-plugin/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/pluto-plugin/build.gradle b/pluto-plugin/build.gradle new file mode 100644 index 00000000..7f98dc31 --- /dev/null +++ b/pluto-plugin/build.gradle @@ -0,0 +1,69 @@ +plugins { + id 'com.android.library' + id 'kotlin-android' +} + +apply from: "$rootDir/scripts/build/utils.gradle" +apply from: "$rootDir/scripts/publish/module.gradle" + +def verCode, verName, verBuild, verNameShort, verPublish +(verCode, verName, verBuild, verNameShort, verPublish) = genVersion() + +ext { + PUBLISH_GROUP_ID = "com.plutolib" + PUBLISH_VERSION = verPublish + PUBLISH_ARTIFACT_ID = 'plugin' +} + +android { + compileSdkVersion rootProject.compileSdkVersion + buildToolsVersion rootProject.buildToolsVersion + + buildFeatures { + viewBinding true + } + + lintOptions { + abortOnError false + } + + defaultConfig { + minSdkVersion rootProject.minSdkVersion + targetSdkVersion rootProject.targetSdkVersion + versionCode verCode + versionName verName + } + + buildTypes { + release { + debuggable true + minifyEnabled false + shrinkResources false + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = '1.8' + } + + resourcePrefix 'pluto___' +} + +dependencies { + api "androidx.core:core-ktx:$androidXCoreVersion" + api 'androidx.appcompat:appcompat:1.4.0' + + api 'androidx.cardview:cardview:1.0.0' + api 'androidx.constraintlayout:constraintlayout:2.1.2' + api 'androidx.navigation:navigation-fragment-ktx:2.3.5' + api 'androidx.recyclerview:recyclerview:1.2.1' + + api 'com.google.android.material:material:1.4.0' + + implementation 'androidx.lifecycle:lifecycle-common-java8:2.4.0' + implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.0' + implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' +} \ No newline at end of file diff --git a/pluto-no-op/src/main/AndroidManifest.xml b/pluto-plugin/src/main/AndroidManifest.xml similarity index 56% rename from pluto-no-op/src/main/AndroidManifest.xml rename to pluto-plugin/src/main/AndroidManifest.xml index 324c9d04..246e8562 100644 --- a/pluto-no-op/src/main/AndroidManifest.xml +++ b/pluto-plugin/src/main/AndroidManifest.xml @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/pluto-plugin/src/main/java/com/pluto/plugin/DataModel.kt b/pluto-plugin/src/main/java/com/pluto/plugin/DataModel.kt new file mode 100644 index 00000000..7eb800c8 --- /dev/null +++ b/pluto-plugin/src/main/java/com/pluto/plugin/DataModel.kt @@ -0,0 +1,34 @@ +package com.pluto.plugin + +import androidx.annotation.DrawableRes +import com.pluto.plugin.utilities.list.ListItem + +data class DeveloperDetails( + val vcsLink: String? = null, + val website: String? = null, +) + +data class PluginConfiguration( + val name: String, + @DrawableRes val icon: Int = R.drawable.pluto___ic_plugin_placeholder_icon +) : ListItem() { + val identifier = name.lowercase().replace(" ", "_", true) + + override fun equals(other: Any?): Boolean { + return other is PluginConfiguration && identifier == other.identifier + } + + override fun hashCode(): Int { + return identifier.hashCode() + } +} + +data class PluginOption( + val id: String, + val label: String, + @DrawableRes val icon: Int = R.drawable.pluto___ic_plugin_placeholder_icon +) : ListItem() { + override fun equals(other: Any?): Boolean { + return other is PluginOption && id == other.id + } +} diff --git a/pluto-plugin/src/main/java/com/pluto/plugin/Plugin.kt b/pluto-plugin/src/main/java/com/pluto/plugin/Plugin.kt new file mode 100644 index 00000000..bd627eb6 --- /dev/null +++ b/pluto-plugin/src/main/java/com/pluto/plugin/Plugin.kt @@ -0,0 +1,54 @@ +package com.pluto.plugin + +import android.app.Application +import android.content.Context +import android.os.Bundle +import androidx.annotation.Keep +import androidx.fragment.app.Fragment +import com.pluto.plugin.utilities.DebugLog +import com.pluto.plugin.utilities.extensions.toast +import com.pluto.plugin.utilities.list.ListItem + +@Keep +abstract class Plugin(val devIdentifier: String) : ListItem() { + + val context: Context + get() = returnContext() + private var _application: Application? = null + private fun returnContext(): Context { + _application?.let { + return it.applicationContext + } + throw IllegalStateException("${this.javaClass.name} plugin is not installed yet.") + } + + var savedInstance: Bundle = Bundle() + private set + + fun install(application: Application) { + this._application = application + onPluginInstalled() + } + + abstract fun getConfig(): PluginConfiguration + abstract fun getView(): Fragment + open fun getDeveloperDetails(): DeveloperDetails? = null + open fun shouldInstallPlugin(): Boolean = true + + /** + * plugin lifecycle methods + */ + abstract fun onPluginInstalled() + abstract fun onPluginDataCleared() + + fun onPluginViewCreated(savedInstanceState: Bundle?) { + DebugLog.d("pluto_plugin", "view switched :: ${getConfig().name} : $savedInstanceState") + context.toast("View switched to ${getConfig().name}") + } + + @Deprecated("global level plugin options are no longer supported") + fun getOptions(): List = emptyList() + + override fun equals(other: Any?): Boolean = other is Plugin && devIdentifier == other.devIdentifier + override fun hashCode(): Int = devIdentifier.hashCode() +} diff --git a/pluto-plugin/src/main/java/com/pluto/plugin/PluginHelper.kt b/pluto-plugin/src/main/java/com/pluto/plugin/PluginHelper.kt new file mode 100644 index 00000000..2a7aa5ba --- /dev/null +++ b/pluto-plugin/src/main/java/com/pluto/plugin/PluginHelper.kt @@ -0,0 +1,27 @@ +package com.pluto.plugin + +import android.app.PendingIntent +import android.content.Context +import android.content.Intent +import android.os.Build +import android.os.Bundle + +class PluginHelper private constructor() { + + companion object { + fun generatePendingIntent(context: Context, identifier: String, bundle: Bundle? = null): PendingIntent { + val notificationIntent = Intent(context, PluginUiBridge.get.bridgeComponents.activityClass) + .putExtra(ID_LABEL, identifier) + bundle?.let { notificationIntent.putExtra(BUNDLE_LABEL, it) } + notificationIntent.action = System.currentTimeMillis().toString() + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + PendingIntent.getActivity(context, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT) + } else { + PendingIntent.getActivity(context, 0, notificationIntent, 0) + } + } + + const val ID_LABEL = "pluginIdentifier" + const val BUNDLE_LABEL = "pluginBundle" + } +} diff --git a/pluto-plugin/src/main/java/com/pluto/plugin/PluginOptionsViewModel.kt b/pluto-plugin/src/main/java/com/pluto/plugin/PluginOptionsViewModel.kt new file mode 100644 index 00000000..a71739e6 --- /dev/null +++ b/pluto-plugin/src/main/java/com/pluto/plugin/PluginOptionsViewModel.kt @@ -0,0 +1,18 @@ +package com.pluto.plugin + +import android.app.Application +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.LiveData +import com.pluto.plugin.utilities.SingleLiveEvent + +@Deprecated("global level plugin options are no longer supported") +class PluginOptionsViewModel(application: Application) : AndroidViewModel(application) { + + val currentOption: LiveData + get() = _currentOption + private val _currentOption = SingleLiveEvent() + + fun select(id: String) { + _currentOption.postValue(id) + } +} diff --git a/pluto-plugin/src/main/java/com/pluto/plugin/PluginUiBridge.kt b/pluto-plugin/src/main/java/com/pluto/plugin/PluginUiBridge.kt new file mode 100644 index 00000000..406e5d7c --- /dev/null +++ b/pluto-plugin/src/main/java/com/pluto/plugin/PluginUiBridge.kt @@ -0,0 +1,28 @@ +package com.pluto.plugin + +import java.lang.IllegalStateException + +class PluginUiBridge private constructor(val bridgeComponents: UiBridgeComponents) { + companion object { + val get: PluginUiBridge + get() { + if (instance != null) { + return instance!! + } + throw IllegalStateException("PluginUiBridge not initialised yet") + } + + private var instance: PluginUiBridge? = null + + fun create(bridgeComponents: UiBridgeComponents): PluginUiBridge? { + if (instance == null) { + synchronized(PluginUiBridge::class.java) { + if (instance == null) { + instance = PluginUiBridge(bridgeComponents) + } + } + } + return instance + } + } +} diff --git a/pluto-plugin/src/main/java/com/pluto/plugin/UiBridgeComponents.kt b/pluto-plugin/src/main/java/com/pluto/plugin/UiBridgeComponents.kt new file mode 100644 index 00000000..ccb147d9 --- /dev/null +++ b/pluto-plugin/src/main/java/com/pluto/plugin/UiBridgeComponents.kt @@ -0,0 +1,5 @@ +package com.pluto.plugin + +data class UiBridgeComponents( + val activityClass: Class<*> +) diff --git a/pluto/src/main/java/com/mocklets/pluto/core/ui/DeBounceClickListener.kt b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/DeBounceClickListener.kt similarity index 75% rename from pluto/src/main/java/com/mocklets/pluto/core/ui/DeBounceClickListener.kt rename to pluto-plugin/src/main/java/com/pluto/plugin/utilities/DeBounceClickListener.kt index aa2d569a..869ab3d6 100644 --- a/pluto/src/main/java/com/mocklets/pluto/core/ui/DeBounceClickListener.kt +++ b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/DeBounceClickListener.kt @@ -1,22 +1,22 @@ -package com.mocklets.pluto.core.ui +package com.pluto.plugin.utilities import android.view.HapticFeedbackConstants import android.view.SoundEffectConstants import android.view.View -import com.mocklets.pluto.core.ui.ClickUtils.Companion.DEBOUNCE_DELAY -import com.mocklets.pluto.core.ui.ClickUtils.Companion.enabled +import com.pluto.plugin.utilities.ClickUtils.Companion.DEBOUNCE_DELAY +import com.pluto.plugin.utilities.ClickUtils.Companion.enabled -internal fun View.setDebounceClickListener( +fun View.setDebounceClickListener( delay: Long = DEBOUNCE_DELAY, haptic: Boolean = false, action: ((View) -> Unit)? ) { - action?.let { action -> + action?.let { setOnClickListener { view -> view.onDebounce(delay) { view.hapticFeedback(haptic) view.soundFeedback() - action.invoke(view) + it.invoke(view) } } return @@ -32,7 +32,7 @@ private inline fun View.onDebounce(delay: Long, next: () -> Unit?) { } } -internal fun View.hapticFeedback(isGlobal: Boolean) { +fun View.hapticFeedback(isGlobal: Boolean) { isHapticFeedbackEnabled = true performHapticFeedback( HapticFeedbackConstants.VIRTUAL_KEY, @@ -40,7 +40,7 @@ internal fun View.hapticFeedback(isGlobal: Boolean) { ) } -internal fun View.soundFeedback() { +fun View.soundFeedback() { isSoundEffectsEnabled = true playSoundEffect(SoundEffectConstants.CLICK) } diff --git a/pluto/src/main/java/com/mocklets/pluto/core/DebugLog.kt b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/DebugLog.kt similarity index 71% rename from pluto/src/main/java/com/mocklets/pluto/core/DebugLog.kt rename to pluto-plugin/src/main/java/com/pluto/plugin/utilities/DebugLog.kt index 5254ee59..9c484230 100644 --- a/pluto/src/main/java/com/mocklets/pluto/core/DebugLog.kt +++ b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/DebugLog.kt @@ -1,37 +1,38 @@ -package com.mocklets.pluto.core +package com.pluto.plugin.utilities import android.util.Log -import com.mocklets.pluto.BuildConfig -internal class DebugLog private constructor() { +class DebugLog private constructor() { companion object { + var enabled: Boolean = true + fun v(tag: String, message: String?, tr: Throwable? = null) { - if (BuildConfig.DEBUG) { + if (enabled) { Log.v(tag, message.toString(), tr) } } fun d(tag: String, message: String?, tr: Throwable? = null) { - if (BuildConfig.DEBUG) { + if (enabled) { Log.d(tag, message.toString(), tr) } } fun i(tag: String, message: String?, tr: Throwable? = null) { - if (BuildConfig.DEBUG) { + if (enabled) { Log.i(tag, message.toString(), tr) } } fun w(tag: String, message: String?, tr: Throwable? = null) { - if (BuildConfig.DEBUG) { + if (enabled) { Log.w(tag, message.toString(), tr) } } fun e(tag: String, message: String?, tr: Throwable? = null) { - if (BuildConfig.DEBUG) { + if (enabled) { Log.e(tag, message, tr) } } diff --git a/pluto/src/main/java/com/mocklets/pluto/core/binding/FragmentViewBindingDelegate.kt b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/FragmentViewBindingDelegate.kt similarity index 87% rename from pluto/src/main/java/com/mocklets/pluto/core/binding/FragmentViewBindingDelegate.kt rename to pluto-plugin/src/main/java/com/pluto/plugin/utilities/FragmentViewBindingDelegate.kt index f4e28cfa..272f44b3 100644 --- a/pluto/src/main/java/com/mocklets/pluto/core/binding/FragmentViewBindingDelegate.kt +++ b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/FragmentViewBindingDelegate.kt @@ -3,7 +3,7 @@ */ // https://github.com/Zhuinden/fragmentviewbindingdelegate-kt -package com.mocklets.pluto.core.binding +package com.pluto.plugin.utilities import android.view.View import androidx.fragment.app.Fragment @@ -15,10 +15,7 @@ import androidx.viewbinding.ViewBinding import kotlin.properties.ReadOnlyProperty import kotlin.reflect.KProperty -internal class FragmentViewBindingDelegate( - val fragment: Fragment, - val viewBindingFactory: (View) -> T -) : ReadOnlyProperty { +class FragmentViewBindingDelegate(val fragment: Fragment, val viewBindingFactory: (View) -> T) : ReadOnlyProperty { private var binding: T? = null init { @@ -59,5 +56,5 @@ internal class FragmentViewBindingDelegate( } } -internal fun Fragment.viewBinding(viewBindingFactory: (View) -> T) = +fun Fragment.viewBinding(viewBindingFactory: (View) -> T) = FragmentViewBindingDelegate(this, viewBindingFactory) diff --git a/pluto/src/main/java/com/mocklets/pluto/core/SingleLiveEvent.kt b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/SingleLiveEvent.kt similarity index 89% rename from pluto/src/main/java/com/mocklets/pluto/core/SingleLiveEvent.kt rename to pluto-plugin/src/main/java/com/pluto/plugin/utilities/SingleLiveEvent.kt index b9bdf061..be33a12f 100644 --- a/pluto/src/main/java/com/mocklets/pluto/core/SingleLiveEvent.kt +++ b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/SingleLiveEvent.kt @@ -1,4 +1,4 @@ -package com.mocklets.pluto.core +package com.pluto.plugin.utilities /* * Copyright 2017 Google Inc. @@ -44,14 +44,11 @@ class SingleLiveEvent : MutableLiveData() { Log.w(TAG, "Multiple observers registered but only one will be notified of changes.") } // Observe the internal MutableLiveData - super.observe( - owner, - Observer { t -> - if (pending.compareAndSet(true, false)) { - observer.onChanged(t) - } + super.observe(owner) { + if (pending.compareAndSet(true, false)) { + observer.onChanged(it) } - ) + } } @MainThread diff --git a/pluto/src/main/java/com/mocklets/pluto/core/DeviceFingerPrint.kt b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/device/Device.kt similarity index 54% rename from pluto/src/main/java/com/mocklets/pluto/core/DeviceFingerPrint.kt rename to pluto-plugin/src/main/java/com/pluto/plugin/utilities/device/Device.kt index 1ca16761..bfe000a7 100644 --- a/pluto/src/main/java/com/mocklets/pluto/core/DeviceFingerPrint.kt +++ b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/device/Device.kt @@ -1,4 +1,4 @@ -package com.mocklets.pluto.core +package com.pluto.plugin.utilities.device import android.content.Context import android.content.pm.PackageInfo @@ -6,18 +6,15 @@ import android.content.pm.PackageManager import android.os.Build import android.provider.Settings import androidx.annotation.Keep -import com.mocklets.pluto.core.ui.list.ListItem import kotlin.math.ceil import kotlin.math.pow import kotlin.math.sqrt -@Keep -internal class DeviceFingerPrint(context: Context) : ListItem() { - val build = BuildData() - val screen = context.getScreen() - val software = context.getSoftwareData() - val isRooted = RootUtil.isDeviceRooted - val gaId: String? = null +data class Device(val context: Context) { + val build: BuildData = BuildData() + val screen: ScreenData = context.getScreen() + val software: SoftwareData = context.getSoftwareData() + val app: AppData = context.getAppData() } private fun Context.getSoftwareData(): SoftwareData { @@ -25,13 +22,12 @@ private fun Context.getSoftwareData(): SoftwareData { androidId = Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID), androidAPILevel = Build.VERSION.SDK_INT.toString(), androidOs = Build.VERSION.RELEASE, - appVersion = getVersion(), - orientation = getOrientation() + isRooted = RootUtil.isDeviceRooted ) } private const val DENSITY_MULTIPLIER = 160f -private fun Context.getScreen(): Screen { +private fun Context.getScreen(): ScreenData { val dm = resources.displayMetrics val height = dm.heightPixels val width = dm.widthPixels @@ -41,11 +37,12 @@ private fun Context.getScreen(): Screen { val rounded = ceil(screenInches) val densityDpi = (dm.density * DENSITY_MULTIPLIER).toInt() - return Screen( - width = "${width}px", - height = "${height}px", - size = "$rounded inches", - density = "$densityDpi dpi" + return ScreenData( + widthPx = width, + heightPx = height, + sizeInches = rounded, + density = densityDpi, + orientation = getOrientation() ) } @@ -57,44 +54,53 @@ private fun Context.getOrientation(): String { } } -private fun Context.getVersion(): VersionData? { - try { - val info: PackageInfo = packageManager.getPackageInfo(packageName, 0) - return VersionData( - info.versionName, - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) info.longVersionCode else info.versionCode.toLong() +private fun Context.getAppData(): AppData { + packageManager.getApplicationLabel( + packageManager.getApplicationInfo(packageName, PackageManager.GET_META_DATA) + ) as String + val info: PackageInfo = packageManager.getPackageInfo(packageName, 0) + return AppData( + name = packageManager.getApplicationLabel(packageManager.getApplicationInfo(packageName, PackageManager.GET_META_DATA)) as String, + packageName = info.packageName, + version = VersionData( + name = info.versionName, + code = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) info.longVersionCode else info.versionCode.toLong() ) - } catch (e: PackageManager.NameNotFoundException) { - DebugLog.e("device-fingerprint", "error while fetching version info", e) - } - return null + ) } @Keep -internal data class VersionData( +data class VersionData( val name: String, val code: Long ) @Keep -internal data class Screen( - val width: String, - val height: String, - val size: String, - val density: String +data class AppData( + val name: String, + val packageName: String, + val version: VersionData ) @Keep -internal data class SoftwareData( +data class ScreenData( + val widthPx: Int, + val heightPx: Int, + val sizeInches: Double, + val density: Int, + val orientation: String +) + +@Keep +data class SoftwareData( val androidId: String?, val androidAPILevel: String?, val androidOs: String?, - val appVersion: VersionData?, - val orientation: String + val isRooted: Boolean ) @Keep -internal data class BuildData( +data class BuildData( val user: String? = Build.USER, val host: String? = Build.HOST, val id: String? = Build.ID, @@ -104,5 +110,6 @@ internal data class BuildData( val board: String = Build.BOARD, val brand: String? = Build.BRAND, val bootloader: String? = Build.BOOTLOADER, - val model: String? = Build.MODEL + val model: String? = Build.MODEL, + val gaId: String? = null ) diff --git a/pluto-plugin/src/main/java/com/pluto/plugin/utilities/device/RootUtil.kt b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/device/RootUtil.kt new file mode 100644 index 00000000..af900f2f --- /dev/null +++ b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/device/RootUtil.kt @@ -0,0 +1,56 @@ +package com.pluto.plugin.utilities.device + +import android.os.Build +import java.io.BufferedReader +import java.io.File +import java.io.InputStreamReader + +/** Credits: https://stackoverflow.com/questions/1101380/determine-if-running-on-a-rooted-device/8097801 + * + * @author Kevin Kowalewski + */ +internal class RootUtil private constructor() { + companion object { + val isDeviceRooted: Boolean + get() = checkRootMethod1() || checkRootMethod2() || checkRootMethod3() + + private fun checkRootMethod1(): Boolean { + val buildTags = Build.TAGS + return buildTags != null && buildTags.contains("test-keys") + } + + private fun checkRootMethod2(): Boolean { + val paths = arrayOf( + "/system/app/Superuser.apk", + "/sbin/su", + "/system/bin/su", + "/system/xbin/su", + "/data/local/xbin/su", + "/data/local/bin/su", + "/system/sd/xbin/su", + "/system/bin/failsafe/su", + "/data/local/su", + "/su/bin/su" + ) + for (path in paths) { + if (File(path).exists()) return true + } + return false + } + + @Suppress("TooGenericExceptionCaught", "SwallowedException") + private fun checkRootMethod3(): Boolean { + var process: Process? = null + return try { + process = Runtime.getRuntime().exec(arrayOf("/system/xbin/which", "su")) + val stream = BufferedReader(InputStreamReader(process.inputStream)) + stream.readLine() != null + } catch (t: Throwable) { +// DebugLog.e("root-utils", "exception occurred", t) + false + } finally { + process?.destroy() + } + } + } +} diff --git a/pluto/src/main/java/com/mocklets/pluto/core/extensions/AnimationKtx.kt b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/extensions/AnimationKtx.kt similarity index 96% rename from pluto/src/main/java/com/mocklets/pluto/core/extensions/AnimationKtx.kt rename to pluto-plugin/src/main/java/com/pluto/plugin/utilities/extensions/AnimationKtx.kt index cade819e..e2ed922f 100644 --- a/pluto/src/main/java/com/mocklets/pluto/core/extensions/AnimationKtx.kt +++ b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/extensions/AnimationKtx.kt @@ -1,4 +1,4 @@ -package com.mocklets.pluto.core.extensions +package com.pluto.plugin.utilities.extensions import android.view.animation.Animation diff --git a/pluto/src/main/java/com/mocklets/pluto/core/extensions/ContextKtx.kt b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/extensions/ContextKtx.kt similarity index 68% rename from pluto/src/main/java/com/mocklets/pluto/core/extensions/ContextKtx.kt rename to pluto-plugin/src/main/java/com/pluto/plugin/utilities/extensions/ContextKtx.kt index f98721d8..35e2245b 100644 --- a/pluto/src/main/java/com/mocklets/pluto/core/extensions/ContextKtx.kt +++ b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/extensions/ContextKtx.kt @@ -1,4 +1,4 @@ -package com.mocklets.pluto.core.extensions +package com.pluto.plugin.utilities.extensions import android.content.Context import android.content.Intent @@ -14,13 +14,13 @@ import androidx.core.content.ContextCompat import androidx.core.content.res.ResourcesCompat @ColorInt -internal fun Context.color(@ColorRes id: Int) = ContextCompat.getColor(this, id) +fun Context.color(@ColorRes id: Int) = ContextCompat.getColor(this, id) -internal fun Context.font(@FontRes id: Int) = ResourcesCompat.getFont(this, id) +fun Context.font(@FontRes id: Int) = ResourcesCompat.getFont(this, id) -internal fun Context.drawable(@DrawableRes id: Int) = ContextCompat.getDrawable(this, id) +fun Context.drawable(@DrawableRes id: Int) = ContextCompat.getDrawable(this, id) -internal fun Context.toast(message: String, isLong: Boolean = false) { +fun Context.toast(message: String, isLong: Boolean = false) { Toast.makeText(this, message, if (isLong) LENGTH_LONG else LENGTH_SHORT).show() } diff --git a/pluto/src/main/java/com/mocklets/pluto/core/extensions/DateKtx.kt b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/extensions/DateKtx.kt similarity index 95% rename from pluto/src/main/java/com/mocklets/pluto/core/extensions/DateKtx.kt rename to pluto-plugin/src/main/java/com/pluto/plugin/utilities/extensions/DateKtx.kt index 51cdc2ea..f3dcb929 100644 --- a/pluto/src/main/java/com/mocklets/pluto/core/extensions/DateKtx.kt +++ b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/extensions/DateKtx.kt @@ -1,4 +1,4 @@ -package com.mocklets.pluto.core.extensions +package com.pluto.plugin.utilities.extensions import java.text.SimpleDateFormat import java.util.Calendar diff --git a/pluto/src/main/java/com/mocklets/pluto/core/extensions/DimensKtx.kt b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/extensions/DimensKtx.kt similarity index 92% rename from pluto/src/main/java/com/mocklets/pluto/core/extensions/DimensKtx.kt rename to pluto-plugin/src/main/java/com/pluto/plugin/utilities/extensions/DimensKtx.kt index 144c9980..69bb6caa 100644 --- a/pluto/src/main/java/com/mocklets/pluto/core/extensions/DimensKtx.kt +++ b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/extensions/DimensKtx.kt @@ -1,4 +1,4 @@ -package com.mocklets.pluto.core.extensions +package com.pluto.plugin.utilities.extensions import android.content.res.Resources import android.util.TypedValue diff --git a/pluto/src/main/java/com/mocklets/pluto/core/extensions/KeyboardKtx.kt b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/extensions/KeyboardKtx.kt similarity index 97% rename from pluto/src/main/java/com/mocklets/pluto/core/extensions/KeyboardKtx.kt rename to pluto-plugin/src/main/java/com/pluto/plugin/utilities/extensions/KeyboardKtx.kt index f1670e26..0dfec736 100644 --- a/pluto/src/main/java/com/mocklets/pluto/core/extensions/KeyboardKtx.kt +++ b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/extensions/KeyboardKtx.kt @@ -1,4 +1,4 @@ -package com.mocklets.pluto.core.extensions +package com.pluto.plugin.utilities.extensions import android.content.Context import android.view.View diff --git a/pluto/src/main/java/com/mocklets/pluto/core/extensions/LayoutKtx.kt b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/extensions/LayoutKtx.kt similarity index 97% rename from pluto/src/main/java/com/mocklets/pluto/core/extensions/LayoutKtx.kt rename to pluto-plugin/src/main/java/com/pluto/plugin/utilities/extensions/LayoutKtx.kt index c088e6d4..9fbedd29 100644 --- a/pluto/src/main/java/com/mocklets/pluto/core/extensions/LayoutKtx.kt +++ b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/extensions/LayoutKtx.kt @@ -1,4 +1,4 @@ -package com.mocklets.pluto.core.extensions +package com.pluto.plugin.utilities.extensions import android.animation.Animator import android.animation.AnimatorListenerAdapter diff --git a/pluto/src/main/java/com/mocklets/pluto/core/extensions/LifecycleKtx.kt b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/extensions/LifecycleKtx.kt similarity index 89% rename from pluto/src/main/java/com/mocklets/pluto/core/extensions/LifecycleKtx.kt rename to pluto-plugin/src/main/java/com/pluto/plugin/utilities/extensions/LifecycleKtx.kt index fda597eb..b93bd8b9 100644 --- a/pluto/src/main/java/com/mocklets/pluto/core/extensions/LifecycleKtx.kt +++ b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/extensions/LifecycleKtx.kt @@ -1,4 +1,4 @@ -package com.mocklets.pluto.core.extensions +package com.pluto.plugin.utilities.extensions import androidx.lifecycle.LifecycleCoroutineScope import kotlinx.coroutines.CoroutineScope diff --git a/pluto/src/main/java/com/mocklets/pluto/core/extensions/LiveDataKtx.kt b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/extensions/LiveDataKtx.kt similarity index 84% rename from pluto/src/main/java/com/mocklets/pluto/core/extensions/LiveDataKtx.kt rename to pluto-plugin/src/main/java/com/pluto/plugin/utilities/extensions/LiveDataKtx.kt index b8bb111f..2958f92a 100644 --- a/pluto/src/main/java/com/mocklets/pluto/core/extensions/LiveDataKtx.kt +++ b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/extensions/LiveDataKtx.kt @@ -1,4 +1,4 @@ -package com.mocklets.pluto.core.extensions +package com.pluto.plugin.utilities.extensions import androidx.lifecycle.MutableLiveData diff --git a/pluto/src/main/java/com/mocklets/pluto/core/extensions/PopupKtx.kt b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/extensions/PopupKtx.kt similarity index 83% rename from pluto/src/main/java/com/mocklets/pluto/core/extensions/PopupKtx.kt rename to pluto-plugin/src/main/java/com/pluto/plugin/utilities/extensions/PopupKtx.kt index a23740b8..7321d64b 100644 --- a/pluto/src/main/java/com/mocklets/pluto/core/extensions/PopupKtx.kt +++ b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/extensions/PopupKtx.kt @@ -1,4 +1,4 @@ -package com.mocklets.pluto.core.extensions +package com.pluto.plugin.utilities.extensions import android.annotation.SuppressLint import android.content.Context @@ -10,11 +10,11 @@ import androidx.annotation.MenuRes import androidx.appcompat.view.menu.MenuBuilder import androidx.appcompat.view.menu.MenuPopupHelper import androidx.appcompat.widget.PopupMenu -import com.mocklets.pluto.R -import com.mocklets.pluto.core.ui.spannable.createSpan +import com.pluto.plugin.R +import com.pluto.plugin.utilities.spannable.createSpan @SuppressLint("RestrictedApi") -internal fun Context.showMoreOptions(view: View, @MenuRes menu: Int, listener: (MenuItem) -> Unit) { +fun Context.showMoreOptions(view: View, @MenuRes menu: Int, listener: (MenuItem) -> Unit) { val popup = PopupMenu(this, view, Gravity.END).apply { menuInflater.inflate(menu, this.menu) applyFontToMenu(this.menu) @@ -47,4 +47,4 @@ private fun Context.applyFontToMenuItem(mi: MenuItem) { } } -private const val MENU_FONT_SIZE = 16 +private const val MENU_FONT_SIZE = 15 diff --git a/pluto/src/main/java/com/mocklets/pluto/core/extensions/RecyclerViewKtx.kt b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/extensions/RecyclerViewKtx.kt similarity index 86% rename from pluto/src/main/java/com/mocklets/pluto/core/extensions/RecyclerViewKtx.kt rename to pluto-plugin/src/main/java/com/pluto/plugin/utilities/extensions/RecyclerViewKtx.kt index a3a328ca..b2f8be59 100644 --- a/pluto/src/main/java/com/mocklets/pluto/core/extensions/RecyclerViewKtx.kt +++ b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/extensions/RecyclerViewKtx.kt @@ -1,4 +1,4 @@ -package com.mocklets.pluto.core.extensions +package com.pluto.plugin.utilities.extensions import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView diff --git a/pluto/src/main/java/com/mocklets/pluto/core/extensions/StringsKtx.kt b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/extensions/StringsKtx.kt similarity index 80% rename from pluto/src/main/java/com/mocklets/pluto/core/extensions/StringsKtx.kt rename to pluto-plugin/src/main/java/com/pluto/plugin/utilities/extensions/StringsKtx.kt index d8f97f7a..08897bb5 100644 --- a/pluto/src/main/java/com/mocklets/pluto/core/extensions/StringsKtx.kt +++ b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/extensions/StringsKtx.kt @@ -1,4 +1,4 @@ -package com.mocklets.pluto.core.extensions +package com.pluto.plugin.utilities.extensions import java.util.Locale diff --git a/pluto/src/main/java/com/mocklets/pluto/core/ui/list/BaseAdapter.kt b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/list/BaseAdapter.kt similarity index 89% rename from pluto/src/main/java/com/mocklets/pluto/core/ui/list/BaseAdapter.kt rename to pluto-plugin/src/main/java/com/pluto/plugin/utilities/list/BaseAdapter.kt index cdb9725f..4aecdb0b 100644 --- a/pluto/src/main/java/com/mocklets/pluto/core/ui/list/BaseAdapter.kt +++ b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/list/BaseAdapter.kt @@ -1,11 +1,11 @@ -package com.mocklets.pluto.core.ui.list +package com.pluto.plugin.utilities.list import android.content.Context import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView -internal abstract class BaseAdapter : DiffAwareAdapter() { +abstract class BaseAdapter : DiffAwareAdapter() { abstract fun onViewHolderCreated(parent: ViewGroup, viewType: Int): DiffAwareHolder? abstract fun getItemViewType(item: ListItem): Int? @@ -24,7 +24,7 @@ internal abstract class BaseAdapter : DiffAwareAdapter() { } } -internal abstract class DiffAwareHolder( +abstract class DiffAwareHolder( view: View, private val listener: DiffAwareAdapter.OnActionListener? ) : RecyclerView.ViewHolder(view) { diff --git a/pluto/src/main/java/com/mocklets/pluto/core/ui/list/CustomItemDecorator.kt b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/list/CustomItemDecorator.kt similarity index 80% rename from pluto/src/main/java/com/mocklets/pluto/core/ui/list/CustomItemDecorator.kt rename to pluto-plugin/src/main/java/com/pluto/plugin/utilities/list/CustomItemDecorator.kt index c35370c8..2ac49a08 100644 --- a/pluto/src/main/java/com/mocklets/pluto/core/ui/list/CustomItemDecorator.kt +++ b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/list/CustomItemDecorator.kt @@ -1,14 +1,14 @@ -package com.mocklets.pluto.core.ui.list +package com.pluto.plugin.utilities.list import android.content.Context import android.graphics.Canvas import android.view.View import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView.ItemDecoration -import com.mocklets.pluto.R -import com.mocklets.pluto.core.extensions.drawable +import com.pluto.plugin.R +import com.pluto.plugin.utilities.extensions.drawable -internal class CustomItemDecorator(context: Context, private val edge: Int = 0) : ItemDecoration() { +class CustomItemDecorator(context: Context, private val edge: Int = 0) : ItemDecoration() { private val divider = context.drawable(R.drawable.pluto___line_divider) diff --git a/pluto/src/main/java/com/mocklets/pluto/core/ui/list/DiffAwareAdapter.kt b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/list/DiffAwareAdapter.kt similarity index 93% rename from pluto/src/main/java/com/mocklets/pluto/core/ui/list/DiffAwareAdapter.kt rename to pluto-plugin/src/main/java/com/pluto/plugin/utilities/list/DiffAwareAdapter.kt index 34b05b01..624d6196 100644 --- a/pluto/src/main/java/com/mocklets/pluto/core/ui/list/DiffAwareAdapter.kt +++ b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/list/DiffAwareAdapter.kt @@ -1,11 +1,11 @@ -package com.mocklets.pluto.core.ui.list +package com.pluto.plugin.utilities.list import android.view.ViewGroup import androidx.recyclerview.widget.AsyncListDiffer import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.RecyclerView -internal abstract class DiffAwareAdapter : RecyclerView.Adapter() { +abstract class DiffAwareAdapter : RecyclerView.Adapter() { private val asyncListDiffer by lazy { AsyncListDiffer( diff --git a/pluto/src/main/java/com/mocklets/pluto/core/sharing/ContentShare.kt b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/sharing/ContentShare.kt similarity index 77% rename from pluto/src/main/java/com/mocklets/pluto/core/sharing/ContentShare.kt rename to pluto-plugin/src/main/java/com/pluto/plugin/utilities/sharing/ContentShare.kt index c6863078..e19d1f4e 100644 --- a/pluto/src/main/java/com/mocklets/pluto/core/sharing/ContentShare.kt +++ b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/sharing/ContentShare.kt @@ -1,13 +1,13 @@ -package com.mocklets.pluto.core.sharing +package com.pluto.plugin.utilities.sharing import androidx.activity.ComponentActivity import androidx.activity.viewModels import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentActivity import androidx.fragment.app.activityViewModels -import com.mocklets.pluto.core.extensions.toast +import com.pluto.plugin.utilities.extensions.toast -internal class ContentShare(activity: FragmentActivity) { +class ContentShare(activity: FragmentActivity) { private val sharer: ContentShareViewModel by activity.lazyContentSharer() private val dialog = ShareOptionsDialog(activity) { when (it) { @@ -30,22 +30,22 @@ internal class ContentShare(activity: FragmentActivity) { } } -internal fun Fragment.lazyContentSharer(): Lazy = activityViewModels() +fun Fragment.lazyContentSharer(): Lazy = activityViewModels() -internal fun ComponentActivity.lazyContentSharer(): Lazy = viewModels() +fun ComponentActivity.lazyContentSharer(): Lazy = viewModels() -internal data class Shareable( +data class Shareable( val title: String, val content: String, val fileName: String = "File shared from Pluto" ) { val processedContent: String = StringBuilder().apply { append(content) - append("\n\n==================\nreport generated by Pluto (https://pluto.mocklets.com)") + append("\n\n==================\nreport generated by Pluto (https://plutolib.com)") }.toString() } -internal sealed class ShareAction { +sealed class ShareAction { data class ShareAsText(val shareable: Shareable) : ShareAction() data class ShareAsFile(val shareable: Shareable) : ShareAction() data class ShareAsCopy(val shareable: Shareable) : ShareAction() diff --git a/pluto/src/main/java/com/mocklets/pluto/core/sharing/ContentShareViewModel.kt b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/sharing/ContentShareViewModel.kt similarity index 53% rename from pluto/src/main/java/com/mocklets/pluto/core/sharing/ContentShareViewModel.kt rename to pluto-plugin/src/main/java/com/pluto/plugin/utilities/sharing/ContentShareViewModel.kt index e89d40a9..88523f0c 100644 --- a/pluto/src/main/java/com/mocklets/pluto/core/sharing/ContentShareViewModel.kt +++ b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/sharing/ContentShareViewModel.kt @@ -1,9 +1,9 @@ -package com.mocklets.pluto.core.sharing +package com.pluto.plugin.utilities.sharing import androidx.lifecycle.ViewModel -import com.mocklets.pluto.core.SingleLiveEvent +import com.pluto.plugin.utilities.SingleLiveEvent -internal class ContentShareViewModel : ViewModel() { +class ContentShareViewModel : ViewModel() { val data = SingleLiveEvent() diff --git a/pluto/src/main/java/com/mocklets/pluto/core/sharing/ShareKtx.kt b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/sharing/ShareKtx.kt similarity index 86% rename from pluto/src/main/java/com/mocklets/pluto/core/sharing/ShareKtx.kt rename to pluto-plugin/src/main/java/com/pluto/plugin/utilities/sharing/ShareKtx.kt index ac096583..4108e0e1 100644 --- a/pluto/src/main/java/com/mocklets/pluto/core/sharing/ShareKtx.kt +++ b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/sharing/ShareKtx.kt @@ -1,4 +1,4 @@ -package com.mocklets.pluto.core.sharing +package com.pluto.plugin.utilities.sharing import android.content.ClipData import android.content.ClipboardManager @@ -6,11 +6,11 @@ import android.content.Context import android.content.Intent import android.os.Environment import androidx.core.content.FileProvider -import com.mocklets.pluto.core.DebugLog +import com.pluto.plugin.utilities.DebugLog import java.io.File import java.io.FileOutputStream -internal fun Context.share(message: String, title: String? = null, subject: String? = null) { +fun Context.share(message: String, title: String? = null, subject: String? = null) { val intent = Intent().apply { type = "text/plain" putExtra(Intent.EXTRA_SUBJECT, subject ?: "") @@ -20,7 +20,7 @@ internal fun Context.share(message: String, title: String? = null, subject: Stri startActivity(Intent.createChooser(intent, title ?: "Share via...")) } -internal fun Context.shareFile(message: String, title: String? = null, fileName: String) { +fun Context.shareFile(message: String, title: String? = null, fileName: String) { val dir = getDirectoryName() val file = generateFile(message, dir) val uri = FileProvider.getUriForFile(applicationContext, "pluto___${applicationContext.packageName}.provider", file) @@ -35,7 +35,7 @@ internal fun Context.shareFile(message: String, title: String? = null, fileName: startActivity(Intent.createChooser(intent, title ?: "Share via...")) } -internal fun Context.copyToClipboard(data: String, label: String) { +fun Context.copyToClipboard(data: String, label: String) { val clipboard: ClipboardManager? = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager? val clip = ClipData.newPlainText(label, data) clipboard?.setPrimaryClip(clip) diff --git a/pluto/src/main/java/com/mocklets/pluto/core/sharing/ShareOptionsDialog.kt b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/sharing/ShareOptionsDialog.kt similarity index 81% rename from pluto/src/main/java/com/mocklets/pluto/core/sharing/ShareOptionsDialog.kt rename to pluto-plugin/src/main/java/com/pluto/plugin/utilities/sharing/ShareOptionsDialog.kt index d21249c6..78684395 100644 --- a/pluto/src/main/java/com/mocklets/pluto/core/sharing/ShareOptionsDialog.kt +++ b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/sharing/ShareOptionsDialog.kt @@ -1,4 +1,4 @@ -package com.mocklets.pluto.core.sharing +package com.pluto.plugin.utilities.sharing import android.content.Context import android.graphics.drawable.ColorDrawable @@ -7,20 +7,20 @@ import android.widget.FrameLayout import androidx.core.content.ContextCompat import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialog -import com.mocklets.pluto.R -import com.mocklets.pluto.core.DeviceInfo -import com.mocklets.pluto.core.extensions.inflate -import com.mocklets.pluto.core.ui.setDebounceClickListener -import com.mocklets.pluto.databinding.PlutoLayoutContentShareOptionsBinding +import com.pluto.plugin.R +import com.pluto.plugin.databinding.PlutoLayoutContentShareOptionsBinding +import com.pluto.plugin.utilities.device.Device +import com.pluto.plugin.utilities.extensions.inflate +import com.pluto.plugin.utilities.setDebounceClickListener -internal class ShareOptionsDialog( +class ShareOptionsDialog( context: Context, private val onAction: (ShareAction) -> Unit ) : BottomSheetDialog(context, R.style.PlutoBottomSheetDialogTheme) { private val sheetView: View = context.inflate(R.layout.pluto___layout_content_share_options) private val binding = PlutoLayoutContentShareOptionsBinding.bind(sheetView) - private val deviceInfo = DeviceInfo(context) + private val device = Device(context) init { setContentView(sheetView) @@ -35,7 +35,7 @@ internal class ShareOptionsDialog( state = BottomSheetBehavior.STATE_EXPANDED isHideable = false skipCollapsed = true - peekHeight = deviceInfo.height + peekHeight = device.screen.heightPx } } } diff --git a/pluto/src/main/java/com/mocklets/pluto/core/ui/spannable/CustomTypefaceSpan.kt b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/spannable/CustomTypefaceSpan.kt similarity index 93% rename from pluto/src/main/java/com/mocklets/pluto/core/ui/spannable/CustomTypefaceSpan.kt rename to pluto-plugin/src/main/java/com/pluto/plugin/utilities/spannable/CustomTypefaceSpan.kt index ce80ad6f..9ba20c04 100644 --- a/pluto/src/main/java/com/mocklets/pluto/core/ui/spannable/CustomTypefaceSpan.kt +++ b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/spannable/CustomTypefaceSpan.kt @@ -1,4 +1,4 @@ -package com.mocklets.pluto.core.ui.spannable +package com.pluto.plugin.utilities.spannable import android.graphics.Typeface import android.text.TextPaint diff --git a/pluto/src/main/java/com/mocklets/pluto/core/ui/spannable/SpanUtil.kt b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/spannable/SpanUtil.kt similarity index 92% rename from pluto/src/main/java/com/mocklets/pluto/core/ui/spannable/SpanUtil.kt rename to pluto-plugin/src/main/java/com/pluto/plugin/utilities/spannable/SpanUtil.kt index 014fc1cf..2b565798 100644 --- a/pluto/src/main/java/com/mocklets/pluto/core/ui/spannable/SpanUtil.kt +++ b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/spannable/SpanUtil.kt @@ -1,4 +1,4 @@ -package com.mocklets.pluto.core.ui.spannable +package com.pluto.plugin.utilities.spannable import android.content.Context import android.graphics.Typeface @@ -12,12 +12,12 @@ import android.text.style.ForegroundColorSpan import android.text.style.StyleSpan import android.text.style.UnderlineSpan import android.widget.TextView -import com.mocklets.pluto.R -import com.mocklets.pluto.core.extensions.color -import com.mocklets.pluto.core.extensions.font +import com.pluto.plugin.R +import com.pluto.plugin.utilities.extensions.color +import com.pluto.plugin.utilities.extensions.font import java.text.Normalizer -internal inline fun TextView.setSpan( +inline fun TextView.setSpan( bufferType: TextView.BufferType? = null, spanBuilder: Builder.() -> Unit ) { @@ -30,13 +30,13 @@ internal inline fun TextView.setSpan( } } -internal inline fun Context.createSpan(spanBuilder: Builder.() -> Unit): CharSequence { +inline fun Context.createSpan(spanBuilder: Builder.() -> Unit): CharSequence { val builder = Builder(this) builder.spanBuilder() return builder.build() } -internal class Builder(val context: Context) { +class Builder(val context: Context) { private val spanBuilder = SpannableStringBuilder() fun append(text: String) { diff --git a/pluto/src/main/res/anim/pluto___fragment_default_enter.xml b/pluto-plugin/src/main/res/anim/pluto___fragment_default_enter.xml similarity index 100% rename from pluto/src/main/res/anim/pluto___fragment_default_enter.xml rename to pluto-plugin/src/main/res/anim/pluto___fragment_default_enter.xml diff --git a/pluto/src/main/res/anim/pluto___fragment_default_exit.xml b/pluto-plugin/src/main/res/anim/pluto___fragment_default_exit.xml similarity index 100% rename from pluto/src/main/res/anim/pluto___fragment_default_exit.xml rename to pluto-plugin/src/main/res/anim/pluto___fragment_default_exit.xml diff --git a/pluto/src/main/res/anim/pluto___fragment_default_reenter.xml b/pluto-plugin/src/main/res/anim/pluto___fragment_default_reenter.xml similarity index 100% rename from pluto/src/main/res/anim/pluto___fragment_default_reenter.xml rename to pluto-plugin/src/main/res/anim/pluto___fragment_default_reenter.xml diff --git a/pluto/src/main/res/anim/pluto___fragment_default_return.xml b/pluto-plugin/src/main/res/anim/pluto___fragment_default_return.xml similarity index 100% rename from pluto/src/main/res/anim/pluto___fragment_default_return.xml rename to pluto-plugin/src/main/res/anim/pluto___fragment_default_return.xml diff --git a/pluto/src/main/res/drawable/pluto___bg_bottom_sheet.xml b/pluto-plugin/src/main/res/drawable/pluto___bg_bottom_sheet.xml similarity index 79% rename from pluto/src/main/res/drawable/pluto___bg_bottom_sheet.xml rename to pluto-plugin/src/main/res/drawable/pluto___bg_bottom_sheet.xml index cb6074d5..48e20428 100644 --- a/pluto/src/main/res/drawable/pluto___bg_bottom_sheet.xml +++ b/pluto-plugin/src/main/res/drawable/pluto___bg_bottom_sheet.xml @@ -10,8 +10,8 @@ + android:topLeftRadius="@dimen/pluto___margin_medium" + android:topRightRadius="@dimen/pluto___margin_medium" /> diff --git a/pluto-plugin/src/main/res/drawable/pluto___ic_plugin_placeholder_icon.xml b/pluto-plugin/src/main/res/drawable/pluto___ic_plugin_placeholder_icon.xml new file mode 100644 index 00000000..44649aa6 --- /dev/null +++ b/pluto-plugin/src/main/res/drawable/pluto___ic_plugin_placeholder_icon.xml @@ -0,0 +1,9 @@ + + + diff --git a/pluto/src/main/res/drawable/pluto___ic_search.xml b/pluto-plugin/src/main/res/drawable/pluto___ic_search.xml similarity index 100% rename from pluto/src/main/res/drawable/pluto___ic_search.xml rename to pluto-plugin/src/main/res/drawable/pluto___ic_search.xml diff --git a/pluto/src/main/res/drawable/pluto___ic_share_as_copy.xml b/pluto-plugin/src/main/res/drawable/pluto___ic_share_as_copy.xml similarity index 100% rename from pluto/src/main/res/drawable/pluto___ic_share_as_copy.xml rename to pluto-plugin/src/main/res/drawable/pluto___ic_share_as_copy.xml diff --git a/pluto/src/main/res/drawable/pluto___ic_share_as_file.xml b/pluto-plugin/src/main/res/drawable/pluto___ic_share_as_file.xml similarity index 100% rename from pluto/src/main/res/drawable/pluto___ic_share_as_file.xml rename to pluto-plugin/src/main/res/drawable/pluto___ic_share_as_file.xml diff --git a/pluto/src/main/res/drawable/pluto___ic_share_as_text.xml b/pluto-plugin/src/main/res/drawable/pluto___ic_share_as_text.xml similarity index 100% rename from pluto/src/main/res/drawable/pluto___ic_share_as_text.xml rename to pluto-plugin/src/main/res/drawable/pluto___ic_share_as_text.xml diff --git a/pluto/src/main/res/drawable/pluto___line_divider.xml b/pluto-plugin/src/main/res/drawable/pluto___line_divider.xml similarity index 100% rename from pluto/src/main/res/drawable/pluto___line_divider.xml rename to pluto-plugin/src/main/res/drawable/pluto___line_divider.xml diff --git a/pluto/src/main/res/font/muli.ttf b/pluto-plugin/src/main/res/font/muli.ttf similarity index 100% rename from pluto/src/main/res/font/muli.ttf rename to pluto-plugin/src/main/res/font/muli.ttf diff --git a/pluto/src/main/res/font/muli_bold.ttf b/pluto-plugin/src/main/res/font/muli_bold.ttf similarity index 100% rename from pluto/src/main/res/font/muli_bold.ttf rename to pluto-plugin/src/main/res/font/muli_bold.ttf diff --git a/pluto/src/main/res/font/muli_light.ttf b/pluto-plugin/src/main/res/font/muli_light.ttf similarity index 100% rename from pluto/src/main/res/font/muli_light.ttf rename to pluto-plugin/src/main/res/font/muli_light.ttf diff --git a/pluto/src/main/res/font/muli_semibold.ttf b/pluto-plugin/src/main/res/font/muli_semibold.ttf similarity index 100% rename from pluto/src/main/res/font/muli_semibold.ttf rename to pluto-plugin/src/main/res/font/muli_semibold.ttf diff --git a/pluto/src/main/res/layout/pluto___layout_content_share_options.xml b/pluto-plugin/src/main/res/layout/pluto___layout_content_share_options.xml similarity index 99% rename from pluto/src/main/res/layout/pluto___layout_content_share_options.xml rename to pluto-plugin/src/main/res/layout/pluto___layout_content_share_options.xml index a1f915a4..198e0018 100644 --- a/pluto/src/main/res/layout/pluto___layout_content_share_options.xml +++ b/pluto-plugin/src/main/res/layout/pluto___layout_content_share_options.xml @@ -82,7 +82,6 @@ android:drawableLeft="@drawable/pluto___ic_share_as_file" android:drawablePadding="@dimen/pluto___margin_xsmall" android:fontFamily="@font/muli_semibold" - android:gravity="center_vertical" android:paddingHorizontal="@dimen/pluto___margin_medium" android:paddingTop="@dimen/pluto___margin_small" diff --git a/pluto/src/main/res/values/colors.xml b/pluto-plugin/src/main/res/values/colors.xml similarity index 81% rename from pluto/src/main/res/values/colors.xml rename to pluto-plugin/src/main/res/values/colors.xml index f6e8bc0f..690c13d8 100644 --- a/pluto/src/main/res/values/colors.xml +++ b/pluto-plugin/src/main/res/values/colors.xml @@ -18,6 +18,10 @@ #99231f40 #cc231f40 @color/pluto___dark + #0Dffffff + #33ffffff + #66ffffff + #99ffffff #ccffffff #e50914 #ff5733 @@ -40,5 +44,10 @@ #66366BD1 #aaff9100 - #0c192b + + #eeecec + #ff5733 + + #11141c + #ff9100 diff --git a/pluto-plugin/src/main/res/values/dimens.xml b/pluto-plugin/src/main/res/values/dimens.xml new file mode 100644 index 00000000..3bf37244 --- /dev/null +++ b/pluto-plugin/src/main/res/values/dimens.xml @@ -0,0 +1,36 @@ + + + 70dp + 8dp + 60dp + 44dp + + 1dp + + 2dp + 4dp + 8dp + 12dp + 16dp + 20dp + 24dp + 28dp + 32dp + 36dp + 40dp + 72dp + + 10sp + 11sp + 12sp + 13sp + 14sp + 15sp + 16sp + 18sp + 20sp + 24sp + 30sp + + 48dp + \ No newline at end of file diff --git a/pluto/src/main/res/values/integers.xml b/pluto-plugin/src/main/res/values/integers.xml similarity index 100% rename from pluto/src/main/res/values/integers.xml rename to pluto-plugin/src/main/res/values/integers.xml diff --git a/pluto-plugin/src/main/res/values/strings.xml b/pluto-plugin/src/main/res/values/strings.xml new file mode 100644 index 00000000..0fe918dd --- /dev/null +++ b/pluto-plugin/src/main/res/values/strings.xml @@ -0,0 +1,6 @@ + + Share as Text + Share as File + Copy to Clipboard + Prefer this option while sharing on Slack. + diff --git a/pluto-plugin/src/main/res/values/styles.xml b/pluto-plugin/src/main/res/values/styles.xml new file mode 100644 index 00000000..a508a531 --- /dev/null +++ b/pluto-plugin/src/main/res/values/styles.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pluto-version.properties b/pluto-version.properties index 94a952e2..bc1a37a8 100644 --- a/pluto-version.properties +++ b/pluto-version.properties @@ -1,5 +1,5 @@ major=1 -minor=1 -patch=3 +minor=2 +patch=0 build=0 channel=release diff --git a/pluto/.gitignore b/pluto/.gitignore index 796b96d1..42afabfd 100644 --- a/pluto/.gitignore +++ b/pluto/.gitignore @@ -1 +1 @@ -/build +/build \ No newline at end of file diff --git a/pluto/build.gradle b/pluto/build.gradle index d2e75fe8..f2efeb5f 100644 --- a/pluto/build.gradle +++ b/pluto/build.gradle @@ -1,18 +1,16 @@ plugins { id 'com.android.library' id 'kotlin-android' - id 'kotlin-parcelize' - id 'kotlin-kapt' } -apply from: "$project.rootDir/build-utils.gradle" -apply from: "${rootDir}/scripts/publish-module.gradle" +apply from: "$rootDir/scripts/build/utils.gradle" +apply from: "$rootDir/scripts/publish/module.gradle" def verCode, verName, verBuild, verNameShort, verPublish (verCode, verName, verBuild, verNameShort, verPublish) = genVersion() ext { - PUBLISH_GROUP_ID = "com.mocklets" + PUBLISH_GROUP_ID = "com.plutolib" PUBLISH_VERSION = verPublish PUBLISH_ARTIFACT_ID = 'pluto' } @@ -38,83 +36,26 @@ android { buildConfigField "String", "VERSION_NAME", "\"${verPublish}\"" buildConfigField ("long", "VERSION_CODE", "${verCode}") buildConfigField "String", "GIT_SHA", "\"${gitSha()}\"" - - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - consumerProguardFiles 'consumer-rules.pro' } buildTypes { - debug { - debuggable true - minifyEnabled false - shrinkResources false -// minifyEnabled true -// shrinkResources false -// zipAlignEnabled true -// multiDexEnabled false -// debuggable true -// proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } release { debuggable true minifyEnabled false shrinkResources false -// minifyEnabled true -// shrinkResources false -// zipAlignEnabled true -// multiDexEnabled false -// debuggable false -// proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } - compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } - kotlinOptions { - jvmTarget = JavaVersion.VERSION_1_8.toString() + jvmTarget = '1.8' } resourcePrefix 'pluto___' } dependencies { - implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation 'androidx.appcompat:appcompat:1.3.0' - implementation "androidx.core:core-ktx:$androidXCoreVersion" - implementation 'androidx.constraintlayout:constraintlayout:2.0.4' - implementation 'com.google.android.material:material:1.4.0' - - implementation 'androidx.lifecycle:lifecycle-common-java8:2.3.1' - implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1' - implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.1' - implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.0-alpha02' - implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' - - implementation 'androidx.navigation:navigation-fragment-ktx:2.3.5' - implementation 'androidx.preference:preference-ktx:1.1.1' - implementation 'androidx.viewpager2:viewpager2:1.1.0-alpha01' - implementation 'androidx.databinding:viewbinding:4.1.2' - - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0" - - implementation 'com.google.code.gson:gson:2.8.6' - implementation "com.squareup.okhttp3:okhttp:$okhttpVersion" - implementation 'com.squareup.okio:okio:2.4.3' - implementation 'androidx.legacy:legacy-support-v4:1.0.0' - implementation 'androidx.browser:browser:1.3.0' - - testImplementation 'junit:junit:4.13.2' - androidTestImplementation 'androidx.test.ext:junit:1.1.3' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' - - implementation "androidx.room:room-ktx:$roomsVersion" - kapt "androidx.room:room-compiler:$roomsVersion" -} - -task validateChanges { - dependsOn 'ktlintFormat' - dependsOn 'detekt' + api project(path: ':pluto-plugin') } \ No newline at end of file diff --git a/pluto/consumer-rules.pro b/pluto/consumer-rules.pro deleted file mode 100644 index 8be7fe7f..00000000 --- a/pluto/consumer-rules.pro +++ /dev/null @@ -1,20 +0,0 @@ -# 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 - --keep class kotlin.** { *; } --keep class kotlin.Metadata { *; } --dontwarn kotlin.** --keepclassmembers class **$WhenMappings { - ; -} --keepclassmembers class kotlin.Metadata { - public ; -} --assumenosideeffects class kotlin.jvm.internal.Intrinsics { - static void checkParameterIsNotNull(java.lang.Object, java.lang.String); -} \ No newline at end of file diff --git a/pluto/proguard-rules.pro b/pluto/proguard-rules.pro deleted file mode 100644 index 202acce9..00000000 --- a/pluto/proguard-rules.pro +++ /dev/null @@ -1,34 +0,0 @@ -# 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 - --keep class kotlin.** { *; } --keep class kotlin.Metadata { *; } --dontwarn kotlin.** --keepclassmembers class **$WhenMappings { - ; -} --keepclassmembers class kotlin.Metadata { - public ; -} --assumenosideeffects class kotlin.jvm.internal.Intrinsics { - static void checkParameterIsNotNull(java.lang.Object, java.lang.String); -} \ No newline at end of file diff --git a/pluto/src/androidTest/java/com/mocklets/pluto/ExampleInstrumentedTest.kt b/pluto/src/androidTest/java/com/mocklets/pluto/ExampleInstrumentedTest.kt deleted file mode 100644 index 57812670..00000000 --- a/pluto/src/androidTest/java/com/mocklets/pluto/ExampleInstrumentedTest.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.mocklets.pluto - -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("com.mocklets.pluto.test", appContext.packageName) - } -} diff --git a/pluto/src/main/AndroidManifest.xml b/pluto/src/main/AndroidManifest.xml index 8c22a129..1dfa26c6 100644 --- a/pluto/src/main/AndroidManifest.xml +++ b/pluto/src/main/AndroidManifest.xml @@ -1,19 +1,24 @@ + package="com.pluto"> + @@ -21,7 +26,6 @@ android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/pluto___file_provider_paths" /> - \ No newline at end of file diff --git a/pluto/src/main/java/com/mocklets/pluto/Pluto.kt b/pluto/src/main/java/com/mocklets/pluto/Pluto.kt deleted file mode 100644 index 2c3aa7f3..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/Pluto.kt +++ /dev/null @@ -1,72 +0,0 @@ -package com.mocklets.pluto - -import android.app.Application -import android.content.Context -import android.content.Intent -import androidx.annotation.Keep -import com.mocklets.pluto.core.Session -import com.mocklets.pluto.core.preferences.Preferences -import com.mocklets.pluto.modules.activities.ActivityTracker -import com.mocklets.pluto.modules.exceptions.ANRListener -import com.mocklets.pluto.modules.exceptions.ExceptionRepo -import com.mocklets.pluto.modules.exceptions.anrs.AnrSupervisor -import com.mocklets.pluto.modules.exceptions.crashes.CrashHandler -import com.mocklets.pluto.modules.network.proxy.NetworkProxyRepo -import com.mocklets.pluto.ui.PlutoActivity - -@Keep -object Pluto { - internal var appContext: Context? = null - internal lateinit var activity: ActivityTracker - internal lateinit var exceptionRepo: ExceptionRepo - internal lateinit var preferences: Preferences - private lateinit var anrSupervisor: AnrSupervisor - internal lateinit var session: Session - internal var appProperties: HashMap = hashMapOf() - private var crashHandler: CrashHandler? = null - - @JvmOverloads - fun initialize(context: Context, shouldShowIntroToast: Boolean = true) { - if (appContext != null) { - return - } - - session = Session() - appContext = context.applicationContext - preferences = Preferences(context) - NetworkProxyRepo.init(context) - crashHandler = CrashHandler(context) - Thread.setDefaultUncaughtExceptionHandler(crashHandler) - exceptionRepo = ExceptionRepo(context) - activity = ActivityTracker(context.applicationContext as Application, shouldShowIntroToast) - anrSupervisor = AnrSupervisor() - - anrSupervisor.start() - } - - fun setAppProperties(properties: HashMap) { - this.appProperties.putAll(properties) - } - - fun setExceptionHandler(uncaughtExceptionHandler: Thread.UncaughtExceptionHandler) { - this.crashHandler?.let { - it.setExceptionHandler(uncaughtExceptionHandler) - return - } - throw IllegalStateException("UncaughtExceptionHandler cannot be set as Pluto is not initialised.") - } - - fun setANRListener(listener: ANRListener) { - anrSupervisor.setListener(listener) - } - - fun showUi() { - val context = - appContext ?: throw IllegalStateException("cannot open Ui as Pluto is not initialised.") - val intent = Intent(context, PlutoActivity::class.java).apply { - addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - } - - context.startActivity(intent) - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/PlutoInterceptor.kt b/pluto/src/main/java/com/mocklets/pluto/PlutoInterceptor.kt deleted file mode 100644 index 401e52fd..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/PlutoInterceptor.kt +++ /dev/null @@ -1,64 +0,0 @@ -package com.mocklets.pluto - -import android.util.Log -import androidx.annotation.Keep -import com.mocklets.pluto.core.DebugLog -import com.mocklets.pluto.modules.exceptions.asExceptionData -import com.mocklets.pluto.modules.network.ApiCallData -import com.mocklets.pluto.modules.network.NetworkCallsRepo -import com.mocklets.pluto.modules.network.ProxyConfig -import com.mocklets.pluto.modules.network.interceptor.ResponseBodyProcessor -import com.mocklets.pluto.modules.network.interceptor.convert -import com.mocklets.pluto.modules.network.proxy.NetworkProxyRepo -import com.mocklets.pluto.modules.setup.SetupNotification -import java.io.IOException -import java.util.UUID -import okhttp3.Interceptor -import okhttp3.Request -import okhttp3.Response - -@Keep -class PlutoInterceptor : Interceptor { - private var setupNotification: SetupNotification? = null - - init { - Pluto.appContext?.let { context -> - setupNotification = SetupNotification(context) - } - } - - override fun intercept(chain: Interceptor.Chain): Response { - val request = chain.request() - - Pluto.appContext?.let { context -> - setupNotification?.add() - val responseBodyProcessor = ResponseBodyProcessor(context) - val id = UUID.nameUUIDFromBytes("${System.currentTimeMillis()}::${request.url()}".toByteArray()).toString() - DebugLog.d("interceptor : ot", "$id ${request.url()}") - val requestData = request.convert() - val apiCallData = ApiCallData(id = id, request = requestData) - NetworkCallsRepo.set(apiCallData) - - var proxyRequest: Request? = null - val proxyUrl = NetworkProxyRepo.get(request.url(), request.method()) - proxyUrl?.let { - val builder = request.newBuilder().url(it) - proxyRequest = builder.build() - apiCallData.proxy = ProxyConfig(proxyUrl) - NetworkCallsRepo.set(apiCallData) - } - - val response: Response = try { - chain.proceed(proxyRequest ?: request) - } catch (e: IOException) { - DebugLog.e("interceptor : ex", "network_crash", e) - apiCallData.exception = e.asExceptionData() - NetworkCallsRepo.set(apiCallData) - throw e - } - return responseBodyProcessor.processBody(response, apiCallData) - } - Log.e("pluto", "API call not intercepted as Pluto is not initialised.") - return chain.proceed(request) - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/PlutoLog.kt b/pluto/src/main/java/com/mocklets/pluto/PlutoLog.kt deleted file mode 100644 index b55e8a56..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/PlutoLog.kt +++ /dev/null @@ -1,97 +0,0 @@ -package com.mocklets.pluto - -import android.util.Log -import androidx.annotation.Keep -import com.mocklets.pluto.modules.logging.Level -import com.mocklets.pluto.modules.logging.LogsRepo - -@Suppress("StringLiteralDuplication") -@Keep -class PlutoLog private constructor() { - - companion object { - - @JvmStatic - fun v(tag: String, message: String?, tr: Throwable? = null) { - preProcessLog(Level.Verbose, tag, message, tr) { trace -> - Log.v("$trace | $tag", "$message", tr) - } - } - - @JvmStatic - fun d(tag: String, message: String?, tr: Throwable? = null) { - preProcessLog(Level.Debug, tag, message, tr) { trace -> - Log.d("$trace | $tag", "$message", tr) - } - } - - @JvmStatic - fun i(tag: String, message: String?, tr: Throwable? = null) { - preProcessLog(Level.Info, tag, message, tr) { trace -> - Log.i("$trace | $tag", "$message", tr) - } - } - - @JvmStatic - fun w(tag: String, message: String?, tr: Throwable? = null) { - preProcessLog(Level.Warning, tag, message, tr) { trace -> - Log.w("$trace | $tag", "$message", tr) - } - } - - @JvmStatic - fun e(tag: String, message: String?, tr: Throwable? = null) { - preProcessLog(Level.Error, tag, message, tr) { trace -> - Log.e("$trace | $tag", "$message", tr) - } - } - - @JvmStatic - fun event(tag: String, event: String, attributes: HashMap?) { - if (!isValidInitialisation()) return - val stackTrace = Thread.currentThread().stackTraceElement() - LogsRepo.saveEvent(Level.Event, tag, event, attributes, stackTrace) - Log.d("${stackTrace.formattedStack()} | $tag", "$event => $attributes") - } - - private fun preProcessLog( - level: Level, - tag: String, - message: String?, - tr: Throwable? = null, - next: ((String) -> Unit) - ) { - if (isValidInitialisation()) { - val stackTrace = Thread.currentThread().stackTraceElement() - LogsRepo.save(level, tag, message, tr, stackTrace) - next.invoke(stackTrace.formattedStack()) - } - } - - private fun isValidInitialisation(): Boolean { - Pluto.appContext?.let { - return true - } - Log.e("pluto", "PlutoLog not printing as Pluto is not initialised.") - return false - } - } -} - -private fun Thread.stackTraceElement(): StackTraceElement { - stackTrace.forEach { - if (!it.className.startsWith(BuildConfig.LIBRARY_PACKAGE_NAME) && - !it.className.startsWith("java.lang.") && - !it.className.startsWith("dalvik.system.") - ) { - return it - } - } - return stackTrace[if (name == "main") MAIN_THREAD_INDEX else DAEMON_THREAD_INDEX] -} - -private const val MAIN_THREAD_INDEX = 6 -private const val DAEMON_THREAD_INDEX = 5 - -@Keep -private fun StackTraceElement.formattedStack(): String = "$methodName($fileName:$lineNumber)" diff --git a/pluto/src/main/java/com/mocklets/pluto/core/DeviceInfo.kt b/pluto/src/main/java/com/mocklets/pluto/core/DeviceInfo.kt deleted file mode 100644 index a770839a..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/core/DeviceInfo.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.mocklets.pluto.core - -import android.content.Context -import android.graphics.Point -import android.view.WindowManager - -internal class DeviceInfo(context: Context) { - - private val screenDimension: ScreenDimension by lazy { - val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager - val display = windowManager.defaultDisplay - val size = Point() - display.getSize(size) - ScreenDimension(size.x, size.y) - } - - val height = screenDimension.height - val width = screenDimension.width - - private data class ScreenDimension(val width: Int, val height: Int) -} diff --git a/pluto/src/main/java/com/mocklets/pluto/core/RootUtil.kt b/pluto/src/main/java/com/mocklets/pluto/core/RootUtil.kt deleted file mode 100644 index daf81d16..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/core/RootUtil.kt +++ /dev/null @@ -1,54 +0,0 @@ -package com.mocklets.pluto.core - -import android.os.Build -import java.io.BufferedReader -import java.io.File -import java.io.InputStreamReader - -/** Credits: https://stackoverflow.com/questions/1101380/determine-if-running-on-a-rooted-device/8097801 - * - * @author Kevin Kowalewski - */ -internal object RootUtil { - val isDeviceRooted: Boolean - get() = checkRootMethod1() || checkRootMethod2() || checkRootMethod3() - - private fun checkRootMethod1(): Boolean { - val buildTags = Build.TAGS - return buildTags != null && buildTags.contains("test-keys") - } - - private fun checkRootMethod2(): Boolean { - val paths = arrayOf( - "/system/app/Superuser.apk", - "/sbin/su", - "/system/bin/su", - "/system/xbin/su", - "/data/local/xbin/su", - "/data/local/bin/su", - "/system/sd/xbin/su", - "/system/bin/failsafe/su", - "/data/local/su", - "/su/bin/su" - ) - for (path in paths) { - if (File(path).exists()) return true - } - return false - } - - @Suppress("TooGenericExceptionCaught") - private fun checkRootMethod3(): Boolean { - var process: Process? = null - return try { - process = Runtime.getRuntime().exec(arrayOf("/system/xbin/which", "su")) - val stream = BufferedReader(InputStreamReader(process.inputStream)) - stream.readLine() != null - } catch (t: Throwable) { - DebugLog.e("root-utils", "exception occurred", t) - false - } finally { - process?.destroy() - } - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/core/Session.kt b/pluto/src/main/java/com/mocklets/pluto/core/Session.kt deleted file mode 100644 index b4f2ae8b..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/core/Session.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.mocklets.pluto.core - -internal class Session { - - var networkSearchText: String = "" - - var loggerSearchText: String = "" - - var exceptionSearchText: String = "" - - var preferencesSearchText: String = "" - - var selectTabIndex: Int = 0 -} diff --git a/pluto/src/main/java/com/mocklets/pluto/core/database/DatabaseManager.kt b/pluto/src/main/java/com/mocklets/pluto/core/database/DatabaseManager.kt deleted file mode 100644 index 30a07f9d..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/core/database/DatabaseManager.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.mocklets.pluto.core.database - -import android.content.Context -import androidx.room.Room - -internal class DatabaseManager(context: Context) { - - val db by lazy { - Room.databaseBuilder(context, PlutoDatabase::class.java, DATABASE_NAME) - .addMigrations() - .fallbackToDestructiveMigration() - .build() - } - - companion object { - private const val DATABASE_NAME = "_pluto_database" - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/core/database/PlutoDatabase.kt b/pluto/src/main/java/com/mocklets/pluto/core/database/PlutoDatabase.kt deleted file mode 100644 index f5e6465b..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/core/database/PlutoDatabase.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.mocklets.pluto.core.database - -import androidx.room.RoomDatabase -import com.mocklets.pluto.modules.exceptions.dao.ExceptionDao -import com.mocklets.pluto.modules.exceptions.dao.ExceptionEntity -import com.mocklets.pluto.modules.network.proxy.dao.NetworkProxyDao -import com.mocklets.pluto.modules.network.proxy.dao.NetworkProxyEntity - -@androidx.room.Database( - entities = [ - ExceptionEntity::class, - NetworkProxyEntity::class - ], - version = 2, - exportSchema = false -) -internal abstract class PlutoDatabase : RoomDatabase() { - abstract fun exceptionDao(): ExceptionDao - abstract fun networkProxyDao(): NetworkProxyDao -} diff --git a/pluto/src/main/java/com/mocklets/pluto/core/extensions/ConcurrencyKtx.kt b/pluto/src/main/java/com/mocklets/pluto/core/extensions/ConcurrencyKtx.kt deleted file mode 100644 index e1dd839d..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/core/extensions/ConcurrencyKtx.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.mocklets.pluto.core.extensions - -@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") -internal fun Any.wait(timeout: Long = 0) = (this as Object).wait(timeout) - -@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") -internal fun Any.notifyAll() = (this as Object).notifyAll() diff --git a/pluto/src/main/java/com/mocklets/pluto/core/extensions/CustomTabKtx.kt b/pluto/src/main/java/com/mocklets/pluto/core/extensions/CustomTabKtx.kt deleted file mode 100644 index 1e631262..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/core/extensions/CustomTabKtx.kt +++ /dev/null @@ -1,64 +0,0 @@ -package com.mocklets.pluto.core.extensions - -import android.app.Activity -import android.content.ActivityNotFoundException -import android.content.ComponentName -import android.content.Context -import android.net.Uri -import androidx.browser.customtabs.CustomTabColorSchemeParams -import androidx.browser.customtabs.CustomTabsCallback -import androidx.browser.customtabs.CustomTabsClient -import androidx.browser.customtabs.CustomTabsIntent -import androidx.browser.customtabs.CustomTabsIntent.SHARE_STATE_OFF -import androidx.browser.customtabs.CustomTabsServiceConnection -import com.mocklets.pluto.Pluto -import com.mocklets.pluto.R -import com.mocklets.pluto.core.DebugLog -import com.mocklets.pluto.modules.network.proxy.ui.NetworkProxySettingsFragment.Companion.IN_APP_BROWSER_RESULT_CODE - -internal const val CHROME_PACKAGE_NAME = "com.android.chrome" - -internal fun Activity.customTab(uri: Uri) { - val params = CustomTabColorSchemeParams.Builder() - .setNavigationBarColor(color(R.color.pluto___mocklets_title)) - .setToolbarColor(color(R.color.pluto___mocklets_title)) - .setSecondaryToolbarColor(color(R.color.pluto___mocklets_title)) - .build() - - val customIntentBuilder: CustomTabsIntent.Builder = CustomTabsIntent.Builder() - customIntentBuilder.setColorSchemeParams(CustomTabsIntent.COLOR_SCHEME_DARK, params) - customIntentBuilder.setShareState(SHARE_STATE_OFF) - customIntentBuilder.setUrlBarHidingEnabled(false) - customIntentBuilder.setShowTitle(false) - - val customTabIntent = customIntentBuilder.build() - customTabIntent.intent.setPackage(CHROME_PACKAGE_NAME) - - try { - customTabIntent.intent.data = uri - Pluto.activity.customTabOpened() - this.startActivityForResult(customTabIntent.intent, IN_APP_BROWSER_RESULT_CODE) - } catch (e: ActivityNotFoundException) { - DebugLog.e("chrome-tab", "not able to handle request", e) - checkAndOpenSupportedApp(uri) - } -} - -internal fun Context.bindCustomTab(uri: Uri?) { - val serviceConnection = object : CustomTabsServiceConnection() { - override fun onCustomTabsServiceConnected(name: ComponentName, mClient: CustomTabsClient) { - DebugLog.d("Service", "Connected") - mClient.warmup(0L) - val callback = object : CustomTabsCallback() {} - val session = mClient.newSession(callback)!! - uri?.let { session.mayLaunchUrl(it, null, null) } - val builder = CustomTabsIntent.Builder() - builder.setSession(session) - } - - override fun onServiceDisconnected(name: ComponentName?) { - DebugLog.d("Service", "Disconnected") - } - } - CustomTabsClient.bindCustomTabsService(this, CHROME_PACKAGE_NAME, serviceConnection) -} diff --git a/pluto/src/main/java/com/mocklets/pluto/core/extensions/FragmentKtx.kt b/pluto/src/main/java/com/mocklets/pluto/core/extensions/FragmentKtx.kt deleted file mode 100644 index 0443e7e6..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/core/extensions/FragmentKtx.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.mocklets.pluto.core.extensions - -import android.os.Bundle -import android.os.Parcelable -import androidx.fragment.app.Fragment - -internal const val KEY_EXTRA = "extra" - -fun Fragment.setParcelExtra(parcelable: Parcelable?, key: String = KEY_EXTRA) { - arguments = (arguments ?: Bundle()).apply { - putParcelable(key, parcelable) - } -} - -fun Fragment.setStringExtra(value: String?, key: String = KEY_EXTRA) { - arguments = (arguments ?: Bundle()).apply { - putString(key, value) - } -} - -fun Fragment.getParcelExtra(key: String = KEY_EXTRA): T? = arguments?.getParcelable(key) - -internal inline fun Fragment.lazyParcelExtra(key: String = KEY_EXTRA): Lazy = - lazy { this.getParcelExtra(key) } diff --git a/pluto/src/main/java/com/mocklets/pluto/core/preferences/Preferences.kt b/pluto/src/main/java/com/mocklets/pluto/core/preferences/Preferences.kt deleted file mode 100644 index 081aec2c..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/core/preferences/Preferences.kt +++ /dev/null @@ -1,51 +0,0 @@ -package com.mocklets.pluto.core.preferences - -import android.annotation.SuppressLint -import android.content.Context - -internal class Preferences(context: Context) { - - private val statePrefs by lazy { context.preferences("states") } - - private val settingsPrefs by lazy { context.preferences("settings") } - - internal var lastSessionCrash: String? - get() = statePrefs.getString(LAST_SESSION_CRASH, null) - @SuppressLint("ApplySharedPref") - /* added commit, as apply() is getting missed */ - set(value) { - statePrefs.edit().putString(LAST_SESSION_CRASH, value).commit() - } - - internal var selectedPreferenceFiles: String? - get() = settingsPrefs.getString(SELECTED_PREF_FILE, null) - set(value) = settingsPrefs.edit().putString(SELECTED_PREF_FILE, value).apply() - - internal var isDarkAccessPopup: Boolean - get() = settingsPrefs.getBoolean(IS_DARK_ACCESS_POPUP, true) - set(value) = settingsPrefs.edit().putBoolean(IS_DARK_ACCESS_POPUP, value).apply() - - internal var isRightHandedAccessPopup: Boolean - get() = settingsPrefs.getBoolean(IS_RIGHT_HANDED_ACCESS_POPUP, true) - set(value) = settingsPrefs.edit().putBoolean(IS_RIGHT_HANDED_ACCESS_POPUP, value).apply() - - internal var isEasyAccessSetupDialogShown: Boolean - get() = settingsPrefs.getBoolean(IS_EASY_ACCESS_SETUP_DIALOG_SHOWN, false) - set(value) = settingsPrefs.edit().putBoolean(IS_EASY_ACCESS_SETUP_DIALOG_SHOWN, value).apply() - - internal companion object { - fun isPlutoPref(it: String): Boolean { - return it.startsWith("_pluto_pref", true) - } - - const val DEFAULT = "Default" - - const val LAST_SESSION_CRASH = "last_session_crash" - const val SELECTED_PREF_FILE = "selected_pref_file" - const val IS_DARK_ACCESS_POPUP = "is_dark_access_popup" - const val IS_RIGHT_HANDED_ACCESS_POPUP = "is_right_handed_access_popup" - const val IS_EASY_ACCESS_SETUP_DIALOG_SHOWN = "is_easy_access_setup_dialog_shown" - } -} - -private fun Context.preferences(name: String, mode: Int = Context.MODE_PRIVATE) = getSharedPreferences(name, mode) diff --git a/pluto/src/main/java/com/mocklets/pluto/core/ui/routing/OnBackKeyHandler.kt b/pluto/src/main/java/com/mocklets/pluto/core/ui/routing/OnBackKeyHandler.kt deleted file mode 100644 index 7f9aa66a..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/core/ui/routing/OnBackKeyHandler.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.mocklets.pluto.core.ui.routing - -interface OnBackKeyHandler { - fun onBackPressed(): Boolean -} diff --git a/pluto/src/main/java/com/mocklets/pluto/core/ui/routing/RouteManager.kt b/pluto/src/main/java/com/mocklets/pluto/core/ui/routing/RouteManager.kt deleted file mode 100644 index 10b3dc85..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/core/ui/routing/RouteManager.kt +++ /dev/null @@ -1,130 +0,0 @@ -package com.mocklets.pluto.core.ui.routing - -import androidx.fragment.app.FragmentActivity -import androidx.fragment.app.FragmentManager -import androidx.fragment.app.FragmentTransaction -import com.mocklets.pluto.R -import com.mocklets.pluto.core.extensions.hideKeyboard -import com.mocklets.pluto.core.extensions.setParcelExtra -import com.mocklets.pluto.modules.appstate.AppStateFragment -import com.mocklets.pluto.modules.exceptions.ui.CrashDetailsFragment -import com.mocklets.pluto.modules.network.proxy.ui.NetworkProxySettingsFragment -import com.mocklets.pluto.modules.network.proxy.ui.NetworkProxySettingsListFragment -import com.mocklets.pluto.modules.network.ui.details.NetworkCallDetailsFragment -import com.mocklets.pluto.modules.preferences.ui.filter.SharedPrefFilterFragment -import com.mocklets.pluto.modules.settings.SettingsFragment -import com.mocklets.pluto.ui.AboutFragment - -internal class RouteManager(private val activity: FragmentActivity, private val containerId: Int) { - - private val router by activity.lazyRouter() - private val fragmentManager - get() = if (!activity.supportFragmentManager.isDestroyed) { - activity.supportFragmentManager - } else null - private val currentFragment - get() = fragmentManager?.findFragmentById(containerId) - - init { - router.routerData.observe( - activity, - { - if (!activity.supportFragmentManager.isStateSaved) { - handleRoutingData(it) - } else { - router.perform(it) - } - } - ) - } - - private fun handleRoutingData(data: RouterAction) { - when (data) { - is Screens -> executeAction(generateAction(data)) - is RouterAction.PopBackStack -> executeAction(Action.PopStack(data.popTag, data.inclusive)) - is RouterAction.BackToApp -> activity.finish() - } - } - - private fun executeAction(action: Action) { - when (action) { - is Action.Switch -> { - fragmentManager?.beginTransaction()?.apply { - animate() - if (action.replace) { - replace(containerId, action.fragment, action.fragmentTag) - } else { - add(containerId, action.fragment, action.fragmentTag) - } - addToBackStack(action.fragmentTag) - }?.commit() - activity.hideKeyboard() - } - - is Action.ShowDialog -> fragmentManager?.beginTransaction()?.apply { - if (action.addToBackStack) { - addToBackStack(action.fragmentTag) - } - }?.let { action.fragment.show(it, action.fragmentTag) } - - is Action.PopStack -> fragmentManager?.popBackStack( - action.tag, - if (action.inclusive) FragmentManager.POP_BACK_STACK_INCLUSIVE else 0 - ) - } - } - - private fun generateAction(screen: Screens): Action { - return when (screen) { - is Screens.Settings -> Action.Switch(screen.tag, SettingsFragment()) - is Screens.AppState -> Action.Switch(screen.tag, AppStateFragment()) - is Screens.About -> Action.Switch(screen.tag, AboutFragment(), false) - is Screens.NetworkCallDetails -> Action.Switch( - screen.tag, - NetworkCallDetailsFragment().apply { - setParcelExtra(screen.data) - } - ) - is Screens.NetworkProxySettings -> Action.Switch( - screen.tag, - NetworkProxySettingsFragment().apply { - setParcelExtra(screen.data) - } - ) - is Screens.NetworkProxySettingsList -> Action.Switch(screen.tag, NetworkProxySettingsListFragment()) - is Screens.CrashDetails -> Action.Switch( - screen.tag, - CrashDetailsFragment().apply { - setParcelExtra(screen.data) - } - ) - is Screens.SharedPrefFilter -> Action.Switch(screen.tag, SharedPrefFilterFragment()) - } - } - - fun onBackPressed(): Boolean { - var handled = - if (currentFragment is OnBackKeyHandler) { - (currentFragment as OnBackKeyHandler).onBackPressed() - } else { - false - } - - if (!handled) { - if (fragmentManager?.backStackEntryCount ?: 0 > 0) { - handled = true - fragmentManager?.popBackStack() - } - } - return handled - } -} - -private fun FragmentTransaction.animate() { - setCustomAnimations( - R.anim.pluto___fragment_default_enter, - R.anim.pluto___fragment_default_exit, - R.anim.pluto___fragment_default_reenter, - R.anim.pluto___fragment_default_return - ) -} diff --git a/pluto/src/main/java/com/mocklets/pluto/core/ui/routing/Router.kt b/pluto/src/main/java/com/mocklets/pluto/core/ui/routing/Router.kt deleted file mode 100644 index f9b5c6eb..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/core/ui/routing/Router.kt +++ /dev/null @@ -1,28 +0,0 @@ -package com.mocklets.pluto.core.ui.routing - -import android.app.Application -import androidx.activity.ComponentActivity -import androidx.activity.viewModels -import androidx.fragment.app.Fragment -import androidx.fragment.app.activityViewModels -import androidx.lifecycle.AndroidViewModel -import com.mocklets.pluto.core.SingleLiveEvent - -internal class Router(application: Application) : AndroidViewModel(application) { - - private val routeLiveData = SingleLiveEvent() - val routerData: SingleLiveEvent - get() = routeLiveData - - fun navigate(screen: Screens) { - perform(screen) - } - - fun perform(action: RouterAction) { - routeLiveData.postValue(action) - } -} - -internal fun Fragment.lazyRouter(): Lazy = activityViewModels() - -internal fun ComponentActivity.lazyRouter(): Lazy = viewModels() diff --git a/pluto/src/main/java/com/mocklets/pluto/core/ui/routing/Utilities.kt b/pluto/src/main/java/com/mocklets/pluto/core/ui/routing/Utilities.kt deleted file mode 100644 index bce5f162..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/core/ui/routing/Utilities.kt +++ /dev/null @@ -1,45 +0,0 @@ -package com.mocklets.pluto.core.ui.routing - -import androidx.fragment.app.DialogFragment -import androidx.fragment.app.Fragment -import com.mocklets.pluto.modules.exceptions.ui.CrashDetailsFragment -import com.mocklets.pluto.modules.network.proxy.ui.NetworkProxySettingsFragment -import com.mocklets.pluto.modules.network.ui.details.NetworkCallDetailsFragment - -internal sealed class RouterAction { - class PopBackStack(val popTag: String? = null, val inclusive: Boolean = false) : RouterAction() - class BackToApp(val trigger: String) : RouterAction() -} - -internal sealed class Screens : RouterAction() { - - val tag = this.javaClass.simpleName - - object Settings : Screens() - object About : Screens() - class NetworkCallDetails(val data: NetworkCallDetailsFragment.Data) : Screens() - object NetworkProxySettingsList : Screens() - class NetworkProxySettings(val data: NetworkProxySettingsFragment.Data? = null) : Screens() - class CrashDetails(val data: CrashDetailsFragment.Data) : Screens() - object SharedPrefFilter : Screens() - object AppState : Screens() -} - -internal sealed class Action { - class Switch( - val fragmentTag: String, - val fragment: Fragment, - val replace: Boolean = true - ) : Action() - - class ShowDialog( - val fragmentTag: String, - val fragment: DialogFragment, - val addToBackStack: Boolean = true - ) : Action() - - class PopStack( - val tag: String? = null, - val inclusive: Boolean = false - ) : Action() -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/activities/ActivityTracker.kt b/pluto/src/main/java/com/mocklets/pluto/modules/activities/ActivityTracker.kt deleted file mode 100644 index ce1707d6..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/activities/ActivityTracker.kt +++ /dev/null @@ -1,166 +0,0 @@ -package com.mocklets.pluto.modules.activities - -import android.app.Activity -import android.app.Application -import android.app.Application.ActivityLifecycleCallbacks -import android.content.Context -import android.os.Bundle -import android.view.View -import androidx.appcompat.app.AppCompatActivity -import androidx.fragment.app.Fragment -import androidx.fragment.app.FragmentActivity -import androidx.fragment.app.FragmentManager -import com.mocklets.pluto.core.DebugLog -import com.mocklets.pluto.modules.setup.SetupNotification -import com.mocklets.pluto.modules.setup.easyaccess.Popup -import com.mocklets.pluto.ui.PlutoActivity - -internal class ActivityTracker @JvmOverloads constructor( - application: Application, - shouldShowIntroToast: Boolean = true -) { - - private var isCustomTabOpened: Boolean = false - private var activityCount = 0 - private val popup: Popup = Popup(application.applicationContext, shouldShowIntroToast) - private val setupNotification = SetupNotification(application.applicationContext) - - private val fragmentLifecycleCallbacks = object : FragmentManager.FragmentLifecycleCallbacks() { - override fun onFragmentCreated(manager: FragmentManager, fragment: Fragment, savedInstanceState: Bundle?) { - DebugLog.d(LOG_TAG, ">> created ${fragment.javaClass.simpleName}") - } - - override fun onFragmentAttached(manager: FragmentManager, fragment: Fragment, context: Context) { - DebugLog.d(LOG_TAG, ">> attached ${fragment.javaClass.simpleName}") - } - - override fun onFragmentStarted(manager: FragmentManager, fragment: Fragment) { - DebugLog.d(LOG_TAG, ">> started ${fragment.javaClass.simpleName}") - } - - override fun onFragmentResumed(manager: FragmentManager, fragment: Fragment) { - DebugLog.d(LOG_TAG, ">> resumed ${fragment.javaClass.simpleName}") - } - - override fun onFragmentPaused(manager: FragmentManager, fragment: Fragment) { - DebugLog.d(LOG_TAG, ">> paused ${fragment.javaClass.simpleName}") - } - - override fun onFragmentStopped(manager: FragmentManager, fragment: Fragment) { - DebugLog.d(LOG_TAG, ">> stopped ${fragment.javaClass.simpleName}") - } - - override fun onFragmentViewCreated(manager: FragmentManager, fragment: Fragment, v: View, state: Bundle?) { - DebugLog.d(LOG_TAG, ">> view_created ${fragment.javaClass.simpleName}") - } - - override fun onFragmentViewDestroyed(manager: FragmentManager, fragment: Fragment) { - DebugLog.d(LOG_TAG, ">> view_destroyed ${fragment.javaClass.simpleName}") - } - - override fun onFragmentDetached(manager: FragmentManager, fragment: Fragment) { - DebugLog.d(LOG_TAG, ">> detached ${fragment.javaClass.simpleName}") - } - - override fun onFragmentDestroyed(manager: FragmentManager, fragment: Fragment) { - DebugLog.d(LOG_TAG, ">> destroyed ${fragment.javaClass.simpleName}") - } - } - - private val lifecycleCallbacks = object : ActivityLifecycleCallbacks { - override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) { - DebugLog.d(LOG_TAG, "create ${activity.javaClass.simpleName}") - activity.registerFragmentLifecycle(fragmentLifecycleCallbacks) - } - - override fun onActivityStarted(activity: Activity) { - DebugLog.d(LOG_TAG, "start ${activity.javaClass.simpleName}") - activityCount++ - if (activityCount == 1) { - onAppForeground(activity) - setupNotification.add() - } - } - - override fun onActivityResumed(activity: Activity) { - DebugLog.d(LOG_TAG, "resume ${activity.javaClass.simpleName}") - if (activity is PlutoActivity) { - popup.remove() - } else { - popup.add(activity) - } - } - - override fun onActivityPaused(activity: Activity) { - DebugLog.d(LOG_TAG, "pause ${activity.javaClass.simpleName}") - } - - override fun onActivityStopped(activity: Activity) { - DebugLog.d(LOG_TAG, "stop ${activity.javaClass.simpleName}") - activityCount-- - if (activityCount == 0) { - if (!isCustomTabOpened && activity is PlutoActivity) { - activity.finish() - } - onAppBackground() - } - } - - override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {} - override fun onActivityDestroyed(activity: Activity) { - if (activity is AppCompatActivity) { - activity.unregisterFragmentLifecycle(fragmentLifecycleCallbacks) - } - if (activity !is PlutoActivity && activityCount == 0) { - popup.remove() - } - DebugLog.d(LOG_TAG, "destroy ${activity.javaClass.simpleName}") - } - } - - private fun onAppBackground() { - popup.remove() - } - - private fun onAppForeground(activity: Activity) { - DebugLog.d(LOG_TAG, "app_foreground ${activity.javaClass.simpleName}") - } - - init { - registerActivityLifecycle(application) - } - - private fun registerActivityLifecycle(application: Application) { - application.registerActivityLifecycleCallbacks(lifecycleCallbacks) - } - - fun customTabOpened() { - isCustomTabOpened = true - } - - fun customTabClosed() { - isCustomTabOpened = false - } - - companion object { - const val LOG_TAG = "activity_tracker" - } -} - -private fun Activity.registerFragmentLifecycle(callback: FragmentManager.FragmentLifecycleCallbacks) { - if (this is FragmentActivity) { - supportFragmentManager.registerFragmentLifecycleCallbacks(callback, true) - } - if (this is AppCompatActivity) { - supportFragmentManager.registerFragmentLifecycleCallbacks(callback, true) - } -} - -private fun Activity.unregisterFragmentLifecycle(callback: FragmentManager.FragmentLifecycleCallbacks) { - if (this is FragmentActivity) { - supportFragmentManager.unregisterFragmentLifecycleCallbacks(callback) - } - if (this is AppCompatActivity) { - supportFragmentManager.unregisterFragmentLifecycleCallbacks(callback) - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/appstate/AppStateFragment.kt b/pluto/src/main/java/com/mocklets/pluto/modules/appstate/AppStateFragment.kt deleted file mode 100644 index 027d79c0..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/appstate/AppStateFragment.kt +++ /dev/null @@ -1,154 +0,0 @@ -package com.mocklets.pluto.modules.appstate - -import android.content.Intent -import android.net.Uri -import android.os.Bundle -import android.view.View -import android.view.View.GONE -import android.view.View.VISIBLE -import androidx.core.view.isVisible -import androidx.core.widget.addTextChangedListener -import androidx.fragment.app.Fragment -import androidx.fragment.app.activityViewModels -import androidx.lifecycle.Observer -import androidx.lifecycle.lifecycleScope -import com.mocklets.pluto.Pluto.appProperties -import com.mocklets.pluto.R -import com.mocklets.pluto.core.binding.viewBinding -import com.mocklets.pluto.core.extensions.dp -import com.mocklets.pluto.core.extensions.hideKeyboard -import com.mocklets.pluto.core.extensions.showKeyboard -import com.mocklets.pluto.core.extensions.toast -import com.mocklets.pluto.core.sharing.Shareable -import com.mocklets.pluto.core.sharing.copyToClipboard -import com.mocklets.pluto.core.sharing.lazyContentSharer -import com.mocklets.pluto.core.ui.list.BaseAdapter -import com.mocklets.pluto.core.ui.list.CustomItemDecorator -import com.mocklets.pluto.core.ui.list.DiffAwareAdapter -import com.mocklets.pluto.core.ui.list.DiffAwareHolder -import com.mocklets.pluto.core.ui.list.ListItem -import com.mocklets.pluto.core.ui.routing.OnBackKeyHandler -import com.mocklets.pluto.core.ui.setDebounceClickListener -import com.mocklets.pluto.databinding.PlutoFragmentAppStateBinding - -internal class AppStateFragment : Fragment(R.layout.pluto___fragment_app_state), OnBackKeyHandler { - - private val binding by viewBinding(PlutoFragmentAppStateBinding::bind) - private val appStateAdapter: BaseAdapter by lazy { AppStateItemAdapter(onActionListener) } - private val viewModel: AppStateViewModel by activityViewModels() - private val contentSharer by lazyContentSharer() - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - binding.list.apply { - adapter = appStateAdapter - addItemDecoration(CustomItemDecorator(context, DECORATOR_DIVIDER_PADDING)) - } - if (appProperties.isNotEmpty()) { - binding.noAppPropertiesGroup.visibility = GONE - binding.note.visibility = VISIBLE - } else { - binding.noAppPropertiesGroup.visibility = VISIBLE - binding.note.visibility = GONE - } - - binding.share.setDebounceClickListener { - viewModel.properties.value?.let { - contentSharer.share( - Shareable( - title = "Share App Properties", - content = it.toShareText(), - fileName = "App Properties from Pluto" - ) - ) - } - } - - binding.close.setDebounceClickListener { - activity?.onBackPressed() - } - binding.noAppPropertiesCta.setDebounceClickListener { - val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse("https://pluto.mocklets.com/#doc_set_properties")) - startActivity(browserIntent) - } - binding.search.setDebounceClickListener { - binding.searchView.visibility = VISIBLE - binding.searchView.requestFocus() - } - binding.closeSearch.setDebounceClickListener { - exitSearch() - } - binding.clearSearch.setDebounceClickListener { - binding.editSearch.text = null - } - binding.editSearch.setOnFocusChangeListener { v, hasFocus -> - if (hasFocus) { - v.showKeyboard() - } else { - v.hideKeyboard() - } - } - binding.editSearch.addTextChangedListener { editable -> - lifecycleScope.launchWhenResumed { - editable?.toString()?.let { - viewModel.filter(it) - } - } - } - - viewModel.properties.removeObserver(appPropertiesObserver) - viewModel.properties.observe(viewLifecycleOwner, appPropertiesObserver) - viewModel.filter() - } - - private val appPropertiesObserver = Observer> { - if (it.isNotEmpty()) { - binding.share.visibility = VISIBLE - binding.divider.visibility = VISIBLE - } - appStateAdapter.list = it - } - - private val onActionListener = object : DiffAwareAdapter.OnActionListener { - override fun onAction(action: String, data: ListItem, holder: DiffAwareHolder?) { - when (data) { - is AppStateItem -> { - context?.copyToClipboard("${data.key} : ${data.value}", "app_state") - context?.toast("${data.key} copied!") - } - } - } - } - - override fun onBackPressed(): Boolean { - if (binding.searchView.isVisible) { - exitSearch() - return true - } - return false - } - - private fun exitSearch() { - binding.editSearch.text = null - binding.searchView.visibility = GONE - binding.editSearch.clearFocus() - } - - private companion object { - val DECORATOR_DIVIDER_PADDING = 16f.dp.toInt() - } -} - -private fun List.toShareText(): String { - val text = StringBuilder() - text.append("App Properties : \n\n") - this.forEach { - text.append("${it.key} : ${it.value}\n") - } - return text.toString() -} - -data class AppStateItem( - val key: String, - val value: String? -) : ListItem() diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/appstate/AppStateItemAdapter.kt b/pluto/src/main/java/com/mocklets/pluto/modules/appstate/AppStateItemAdapter.kt deleted file mode 100644 index 1637062f..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/appstate/AppStateItemAdapter.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.mocklets.pluto.modules.appstate - -import android.view.ViewGroup -import com.mocklets.pluto.core.ui.list.BaseAdapter -import com.mocklets.pluto.core.ui.list.DiffAwareHolder -import com.mocklets.pluto.core.ui.list.ListItem - -internal class AppStateItemAdapter(private val listener: OnActionListener) : BaseAdapter() { - override fun getItemViewType(item: ListItem): Int? { - return when (item) { - is AppStateItem -> ITEM_TYPE_APP_STATE - else -> null - } - } - - override fun onViewHolderCreated(parent: ViewGroup, viewType: Int): DiffAwareHolder? { - return when (viewType) { - ITEM_TYPE_APP_STATE -> AppStateItemHolder(parent, listener) - else -> null - } - } - - companion object { - const val ITEM_TYPE_APP_STATE = 1000 - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/appstate/AppStateItemHolder.kt b/pluto/src/main/java/com/mocklets/pluto/modules/appstate/AppStateItemHolder.kt deleted file mode 100644 index d26cbc4c..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/appstate/AppStateItemHolder.kt +++ /dev/null @@ -1,31 +0,0 @@ -package com.mocklets.pluto.modules.appstate - -import android.view.ViewGroup -import com.mocklets.pluto.R -import com.mocklets.pluto.core.extensions.inflate -import com.mocklets.pluto.core.ui.list.DiffAwareAdapter -import com.mocklets.pluto.core.ui.list.DiffAwareHolder -import com.mocklets.pluto.core.ui.list.ListItem -import com.mocklets.pluto.core.ui.setDebounceClickListener -import com.mocklets.pluto.databinding.PlutoItemAppStateBinding - -internal class AppStateItemHolder( - parent: ViewGroup, - listener: DiffAwareAdapter.OnActionListener -) : DiffAwareHolder(parent.inflate(R.layout.pluto___item_app_state), listener) { - - private val binding = PlutoItemAppStateBinding.bind(itemView) - - private val key = binding.key - private val value = binding.value - - override fun onBind(item: ListItem) { - if (item is AppStateItem) { - key.text = "- ${item.key}" - value.text = item.value - itemView.setDebounceClickListener { - onAction("click") - } - } - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/appstate/AppStateViewModel.kt b/pluto/src/main/java/com/mocklets/pluto/modules/appstate/AppStateViewModel.kt deleted file mode 100644 index 219f66c6..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/appstate/AppStateViewModel.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.mocklets.pluto.modules.appstate - -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel -import com.mocklets.pluto.Pluto.appProperties - -internal class AppStateViewModel : ViewModel() { - - val properties: LiveData> - get() = _properties - private val _properties = MutableLiveData>() - - fun filter(search: String = "") { - _properties.postValue(appProperties.convert(search)) - } - - private fun HashMap.convert(search: String): List { - val list = arrayListOf() - forEach { (key, value) -> - if (key.contains(search, true)) { - list.add(AppStateItem(key, value)) - } - } - return list - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/ANRException.kt b/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/ANRException.kt deleted file mode 100644 index d8e04fb0..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/ANRException.kt +++ /dev/null @@ -1,79 +0,0 @@ -package com.mocklets.pluto.modules.exceptions - -import androidx.annotation.Keep -import java.io.ByteArrayOutputStream -import java.io.PrintStream -import java.util.Locale - -/** - * [Exception] to represent an ANR. This [Exception]'s stack trace will be the current stack trace of the given [Thread] - */ -@Keep -class ANRException(thread: Thread) : Exception("ANR detected") { - - val threadStateMap: String - internal val threadStateList: List - - init { - stackTrace = thread.stackTrace - threadStateMap = generateProcessMap() - threadStateList = generateProcessList() - } - - /** - * Logs the current process and all its threads - */ - private fun generateProcessMap(): String { - val bos = ByteArrayOutputStream() - val ps = PrintStream(bos) - printProcessMap(ps) - return String(bos.toByteArray()) - } - - /** - * Prints the current process and all its threads - * - * @param ps the [PrintStream] to which the - * info is written - */ - private fun printProcessMap(ps: PrintStream) { - // Get all stack traces in the system - val stackTraces = Thread.getAllStackTraces() - ps.println("Process map:") - for (thread in stackTraces.keys) { - if (!stackTraces[thread].isNullOrEmpty()) { - printThread(ps, Locale.getDefault(), thread, stackTraces[thread]!!) - ps.println() - } - } - } - - /** - * Prints the given thread - * @param ps the [PrintStream] to which the - * info is written - * @param l the [Locale] to use - * @param thread the [Thread] to print - * @param stack the [Thread]'s stack trace - */ - private fun printThread(ps: PrintStream, l: Locale, thread: Thread, stack: Array) { - ps.println(String.format(l, "\t%s (%s)", thread.name, thread.state)) - for (element in stack) { - element.apply { - ps.println(String.format(l, "\t\t%s.%s(%s:%d)", className, methodName, fileName, lineNumber)) - } - } - } - - private fun generateProcessList(): List { - val list = arrayListOf() - val stackTraces = Thread.getAllStackTraces() - for (thread in stackTraces.keys) { - if (!stackTraces[thread].isNullOrEmpty()) { - val process = ProcessThread(thread.name, thread.state.name, stackTraces[thread]!!.asStringArray()) - list.add(process) - } - } - return list - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/ANRListener.kt b/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/ANRListener.kt deleted file mode 100644 index 30c4d19d..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/ANRListener.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.mocklets.pluto.modules.exceptions - -import androidx.annotation.Keep - -@Keep -interface ANRListener { - fun onAppNotResponding(exception: ANRException) -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/DataModel.kt b/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/DataModel.kt deleted file mode 100644 index 6e8517f6..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/DataModel.kt +++ /dev/null @@ -1,110 +0,0 @@ -package com.mocklets.pluto.modules.exceptions - -import androidx.annotation.Keep -import com.mocklets.pluto.BuildConfig -import com.mocklets.pluto.core.DeviceFingerPrint -import com.mocklets.pluto.core.ui.list.ListItem - -@Keep -internal data class ExceptionAllData( - val thread: ThreadData? = null, - val exception: ExceptionData, - val device: DeviceFingerPrint, - val threadStateList: ThreadStates? = null -) - -@Keep -internal data class ThreadData( - val id: Long, - val name: String, - val priority: Int, - val isDaemon: Boolean, - val state: String, - val group: ThreadGroupData? -) : ListItem() - -@Keep -internal data class ThreadGroupData( - val name: String, - val parent: String, - val activeCount: Int -) - -@Keep -internal data class ExceptionData( - val message: String?, - val name: String?, - val file: String?, - val lineNumber: Int, - val stackTrace: ArrayList, - val timeStamp: Long = System.currentTimeMillis(), - val isANRException: Boolean = false -) : ListItem() - -@Keep -internal data class ThreadStates( - val states: List -) : ListItem() - -@Keep -internal data class ProcessThread( - val name: String, - val state: String, - val stackTrace: ArrayList -) : ListItem() - -internal fun Throwable.asExceptionData(isANR: Boolean = false): ExceptionData { - return ExceptionData( - name = this.toString().replace(": $message", "", true), - message = message, - stackTrace = stackTrace.asStringArray(), - file = stackTrace.getOrNull(0)?.fileName, - lineNumber = stackTrace.getOrNull(0)?.lineNumber ?: Int.MIN_VALUE, - isANRException = isANR - ) -} - -internal fun Array.asStringArray(): ArrayList { - val array = arrayListOf() - forEach { - if (it.isNativeMethod) { - array.add("${it.className}.${it.methodName}(Native Method)") - } else { - array.add("${it.className}.${it.methodName}(${it.fileName}:${it.lineNumber})") - } - } - return array -} - -internal fun Thread.asThreadData(): ThreadData { - return ThreadData( - id = id, - name = name, - isDaemon = isDaemon, - state = state.name, - group = threadGroup.convert(), - priority = priority - ) -} - -private fun ThreadGroup?.convert(): ThreadGroupData? { - this?.let { - ThreadGroupData( - name = it.name, - parent = it.parent.name, - activeCount = it.activeCount() - ) - } - return null -} - -@Keep -data class ReportData( - val message: String?, - val name: String?, - val stackTrace: ArrayList, - val client: String?, - val gitSha: String = BuildConfig.GIT_SHA, - val version: String = BuildConfig.VERSION_NAME, - val buildType: String = BuildConfig.BUILD_TYPE -) diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/ExceptionRepo.kt b/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/ExceptionRepo.kt deleted file mode 100644 index 1474a3eb..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/ExceptionRepo.kt +++ /dev/null @@ -1,65 +0,0 @@ -package com.mocklets.pluto.modules.exceptions - -import android.content.Context -import com.google.gson.Gson -import com.mocklets.pluto.Pluto -import com.mocklets.pluto.core.DeviceFingerPrint -import com.mocklets.pluto.core.database.DatabaseManager -import com.mocklets.pluto.core.extensions.capitalizeText -import com.mocklets.pluto.modules.exceptions.dao.ExceptionEntity -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch - -internal class ExceptionRepo(context: Context) { - - private val db = DatabaseManager(context).db - private val preferences by lazy { Pluto.preferences } - private val deviceFingerPrint = DeviceFingerPrint(context) - private val scope = CoroutineScope(Dispatchers.IO) - - init { - checkAndSaveCrash() - } - - @Synchronized - private fun checkAndSaveCrash() { - scope.launch { - preferences.lastSessionCrash?.let { - val exception = Gson().fromJson(it, ExceptionEntity::class.java) - db.exceptionDao().save( - ExceptionEntity( - timestamp = exception.timestamp, - data = exception.data - ) - ) - preferences.lastSessionCrash = null - } - } - } - - @Synchronized - internal fun saveANR(e: ANRException) { - scope.launch { - db.exceptionDao().save( - ExceptionEntity( - timestamp = System.currentTimeMillis(), - data = ExceptionAllData( - exception = e.asExceptionData(true), - device = deviceFingerPrint, - threadStateList = ThreadStates(e.threadStateList) - ) - ) - ) - } - } - - companion object { - fun getPriorityString(priority: Int) = - when (priority) { - Thread.MAX_PRIORITY -> "maximum" - Thread.MIN_PRIORITY -> "minimum" - else -> "normal" - }.capitalizeText() - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/anrs/AnrSupervisor.kt b/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/anrs/AnrSupervisor.kt deleted file mode 100644 index 00d37a9c..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/anrs/AnrSupervisor.kt +++ /dev/null @@ -1,54 +0,0 @@ -package com.mocklets.pluto.modules.exceptions.anrs - -import com.mocklets.pluto.modules.exceptions.ANRListener -import java.util.concurrent.Executors - -/** - * A class supervising the UI thread for ANR errors. Use - * [.start] and [.stop] to control - * when the UI thread is supervised - */ -internal class AnrSupervisor { - private val mExecutor = Executors.newSingleThreadExecutor() - - /** - * The [AnrSupervisorRunnable] running on a separate thread - */ - private val mSupervisor = AnrSupervisorRunnable() - - /** - * Starts the supervision - */ - @Synchronized - fun start() { - synchronized(mSupervisor) { - if (mSupervisor.isStopped) { - mExecutor.execute(mSupervisor) - } else { - mSupervisor.unstop() - } - } - } - - /** - * Stops the supervision. The stop is delayed, so if - * start() is called right after stop(), - * both methods will have no effect. There will be at least one - * more ANR check before the supervision is stopped. - */ - @Synchronized - fun stop() { - mSupervisor.stop() - } - - fun setListener(listener: ANRListener) { - mSupervisor.setListener(listener) - } - - companion object { - const val LOGTAG = "Pluto-ANR-watcher" - const val ANR_WATCHER_THREAD_NAME = "pluto-anr-watcher" - const val ANR_WATCHER_TIMEOUT: Long = 10_000 - const val MAIN_THREAD_RESPONSE_THRESHOLD: Long = 2_000 - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/anrs/AnrSupervisorCallback.kt b/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/anrs/AnrSupervisorCallback.kt deleted file mode 100644 index 337405b1..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/anrs/AnrSupervisorCallback.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.mocklets.pluto.modules.exceptions.anrs - -import com.mocklets.pluto.core.extensions.notifyAll - -/** - * A [Runnable] which calls [.notifyAll] when run. - */ -internal class AnrSupervisorCallback -/** - * Creates a new instance - */ - : Runnable { - /** - * Returns whether [.run] was called yet - * - * @return true if called, false if not - */ - /** - * Flag storing whether [.run] was called - */ - @get:Synchronized - var isCalled = false - private set - - @Synchronized - override fun run() { - isCalled = true - this.notifyAll() - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/anrs/AnrSupervisorRunnable.kt b/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/anrs/AnrSupervisorRunnable.kt deleted file mode 100644 index f21c6007..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/anrs/AnrSupervisorRunnable.kt +++ /dev/null @@ -1,114 +0,0 @@ -package com.mocklets.pluto.modules.exceptions.anrs - -import android.os.Handler -import android.os.Looper -import com.mocklets.pluto.Pluto -import com.mocklets.pluto.core.DebugLog -import com.mocklets.pluto.core.extensions.wait -import com.mocklets.pluto.modules.exceptions.ANRException -import com.mocklets.pluto.modules.exceptions.ANRListener -import com.mocklets.pluto.modules.exceptions.anrs.AnrSupervisor.Companion.ANR_WATCHER_THREAD_NAME -import com.mocklets.pluto.modules.exceptions.anrs.AnrSupervisor.Companion.ANR_WATCHER_TIMEOUT -import com.mocklets.pluto.modules.exceptions.anrs.AnrSupervisor.Companion.LOGTAG -import com.mocklets.pluto.modules.exceptions.anrs.AnrSupervisor.Companion.MAIN_THREAD_RESPONSE_THRESHOLD - -/** - * A [Runnable] testing the UI thread every 10s until [ ][.stop] is called - */ -internal class AnrSupervisorRunnable : Runnable { - /** - * The [Handler] to access the UI threads message queue - */ - private val mHandler = Handler(Looper.getMainLooper()) - private var anrListener: ANRListener? = null - - /** - * The stop flag - */ - private var mStopped = false - /** - * Returns whether the stop is completed - * - * @return true if stop is completed, false if not - */ - /** - * Flag indicating the stop was performed - */ - @get:Synchronized - var isStopped = true - private set - - override fun run() { - Thread.currentThread().name = ANR_WATCHER_THREAD_NAME - isStopped = false - - while (!Thread.interrupted()) { - try { - DebugLog.d(LOGTAG, "Check for ANR...") - - // Create new callback - val callback = AnrSupervisorCallback() - - // Perform test, Handler should run the callback within 1s - synchronized(callback) { - mHandler.post(callback) - callback.wait(MAIN_THREAD_RESPONSE_THRESHOLD) - - // Check if called - if (!callback.isCalled) { - val e = ANRException(mHandler.looper.thread) - anrListener?.onAppNotResponding(e) - Pluto.exceptionRepo.saveANR(e) - /** Wait until the thread responds again */ - callback.wait() - } else { - DebugLog.d(LOGTAG, "UI Thread responded within 1s") - } - } - // Check if stopped - checkStopped() - /** Sleep for next test */ - Thread.sleep(ANR_WATCHER_TIMEOUT) - } catch (e: InterruptedException) { - break - } - } - - // Set stop completed flag - isStopped = true - DebugLog.d(LOGTAG, "ANR supervision stopped") - } - - @Synchronized - @Throws(InterruptedException::class) - private fun checkStopped() { - if (mStopped) { - Thread.sleep(MAIN_THREAD_RESPONSE_THRESHOLD) - if (mStopped) { - throw InterruptedException() - } - } - } - - /** - * Stops the check - */ - @Synchronized - fun stop() { - DebugLog.d(LOGTAG, "Stopping...") - mStopped = true - } - - /** - * Stops the check - */ - @Synchronized - fun unstop() { - DebugLog.d(LOGTAG, "Revert stopping...") - mStopped = false - } - - fun setListener(listener: ANRListener) { - anrListener = listener - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/crashes/CrashHandler.kt b/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/crashes/CrashHandler.kt deleted file mode 100644 index 100a9052..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/crashes/CrashHandler.kt +++ /dev/null @@ -1,36 +0,0 @@ -package com.mocklets.pluto.modules.exceptions.crashes - -import android.content.Context -import com.google.gson.Gson -import com.mocklets.pluto.Pluto -import com.mocklets.pluto.core.DeviceFingerPrint -import com.mocklets.pluto.modules.exceptions.ExceptionAllData -import com.mocklets.pluto.modules.exceptions.asExceptionData -import com.mocklets.pluto.modules.exceptions.asThreadData -import com.mocklets.pluto.modules.exceptions.dao.ExceptionEntity - -internal class CrashHandler(context: Context) : Thread.UncaughtExceptionHandler { - - private var handler: Thread.UncaughtExceptionHandler? = Thread.getDefaultUncaughtExceptionHandler() - private val deviceFingerPrint = DeviceFingerPrint(context) - private val preferences by lazy { Pluto.preferences } - private val crashNotification: CrashNotification = CrashNotification(context) - - override fun uncaughtException(t: Thread, e: Throwable) { - val exceptionData = ExceptionEntity( - timestamp = System.currentTimeMillis(), - data = ExceptionAllData( - thread = t.asThreadData(), - exception = e.asExceptionData(), - device = deviceFingerPrint - ) - ) - preferences.lastSessionCrash = Gson().toJson(exceptionData) - crashNotification.add() - handler?.uncaughtException(t, e) - } - - internal fun setExceptionHandler(handler: Thread.UncaughtExceptionHandler) { - this.handler = handler - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/crashes/CrashNotification.kt b/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/crashes/CrashNotification.kt deleted file mode 100644 index 077e29ae..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/crashes/CrashNotification.kt +++ /dev/null @@ -1,41 +0,0 @@ -package com.mocklets.pluto.modules.exceptions.crashes - -import android.app.PendingIntent -import android.content.Context -import android.content.Intent -import android.content.pm.PackageManager -import android.os.Build -import com.mocklets.pluto.R -import com.mocklets.pluto.core.notification.NotificationUtil -import com.mocklets.pluto.ui.PlutoActivity - -internal class CrashNotification(private val context: Context) { - - private val notificationUtil = NotificationUtil(context) - - private val clientAppName: String = context.packageManager.getApplicationLabel( - context.packageManager.getApplicationInfo(context.packageName, PackageManager.GET_META_DATA) - ) as String - - fun add() { - val notificationIntent = Intent(context, PlutoActivity::class.java) - // todo add intent data to open crash tab directly - val pendingIntent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - PendingIntent.getActivity(context, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE) - } else { - PendingIntent.getActivity(context, 0, notificationIntent, 0) - } - - notificationUtil.notify( - title = context.getString(R.string.pluto___crash_notification_title, clientAppName), - text = context.getString(R.string.pluto___crash_notification_subtitle), - intent = pendingIntent, - isOngoing = false, - isAutoCancel = true - ) - } - - fun remove() { - notificationUtil.cancel() - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/dao/EntityConverters.kt b/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/dao/EntityConverters.kt deleted file mode 100644 index 81ccea20..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/dao/EntityConverters.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.mocklets.pluto.modules.exceptions.dao - -import androidx.room.TypeConverter -import com.google.gson.Gson -import com.mocklets.pluto.modules.exceptions.ExceptionAllData - -internal class EntityConverters { - - private val gson = Gson() - - @TypeConverter - fun stringToException(data: String?): ExceptionAllData { - return gson.fromJson(data, ExceptionAllData::class.java) - } - - @TypeConverter - fun exceptionToString(data: ExceptionAllData): String? { - return gson.toJson(data) - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/dao/ExceptionDao.kt b/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/dao/ExceptionDao.kt deleted file mode 100644 index d1140796..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/dao/ExceptionDao.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.mocklets.pluto.modules.exceptions.dao - -import androidx.room.Dao -import androidx.room.Insert -import androidx.room.OnConflictStrategy -import androidx.room.Query - -@Dao -internal interface ExceptionDao { - - @Insert(onConflict = OnConflictStrategy.REPLACE) - suspend fun save(entity: ExceptionEntity) - - @Query("SELECT * FROM exceptions where id is :id") - suspend fun fetch(id: Int): ExceptionEntity? - - @Query("SELECT * FROM exceptions order by timestamp DESC") - suspend fun fetchAll(): List? - - @Query("DELETE FROM exceptions where id is :id") - suspend fun delete(id: Int) - - @Query("DELETE FROM exceptions") - suspend fun deleteAll() -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/dao/ExceptionEntity.kt b/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/dao/ExceptionEntity.kt deleted file mode 100644 index f1cda27c..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/dao/ExceptionEntity.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.mocklets.pluto.modules.exceptions.dao - -import androidx.annotation.Keep -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey -import androidx.room.TypeConverters -import com.mocklets.pluto.core.ui.list.ListItem -import com.mocklets.pluto.modules.exceptions.ExceptionAllData - -@Keep -@TypeConverters(EntityConverters::class) -@Entity(tableName = "exceptions") -internal data class ExceptionEntity( - @PrimaryKey(autoGenerate = true) - @ColumnInfo(name = "id") - val id: Int? = null, - @ColumnInfo(name = "timestamp") - val timestamp: Long, - @ColumnInfo(name = "data") - val data: ExceptionAllData -) : ListItem() diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/ui/CrashDetailsFragment.kt b/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/ui/CrashDetailsFragment.kt deleted file mode 100644 index 996ff735..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/ui/CrashDetailsFragment.kt +++ /dev/null @@ -1,172 +0,0 @@ -package com.mocklets.pluto.modules.exceptions.ui - -import android.content.Intent -import android.net.Uri -import android.os.Bundle -import android.os.Parcelable -import android.util.Base64.DEFAULT -import android.util.Base64.encodeToString -import android.view.View -import androidx.fragment.app.Fragment -import androidx.fragment.app.viewModels -import androidx.lifecycle.Observer -import androidx.lifecycle.lifecycleScope -import com.google.gson.Gson -import com.mocklets.pluto.Pluto -import com.mocklets.pluto.R -import com.mocklets.pluto.core.DebugLog -import com.mocklets.pluto.core.binding.viewBinding -import com.mocklets.pluto.core.extensions.capitalizeText -import com.mocklets.pluto.core.extensions.delayedLaunchWhenResumed -import com.mocklets.pluto.core.extensions.lazyParcelExtra -import com.mocklets.pluto.core.extensions.toast -import com.mocklets.pluto.core.sharing.Shareable -import com.mocklets.pluto.core.sharing.lazyContentSharer -import com.mocklets.pluto.core.ui.list.BaseAdapter -import com.mocklets.pluto.core.ui.list.DiffAwareAdapter -import com.mocklets.pluto.core.ui.list.DiffAwareHolder -import com.mocklets.pluto.core.ui.list.ListItem -import com.mocklets.pluto.core.ui.setDebounceClickListener -import com.mocklets.pluto.databinding.PlutoFragmentCrashDetailsBinding -import com.mocklets.pluto.modules.exceptions.ExceptionAllData -import com.mocklets.pluto.modules.exceptions.ExceptionRepo -import com.mocklets.pluto.modules.exceptions.ReportData -import com.mocklets.pluto.modules.exceptions.dao.ExceptionEntity -import java.net.URLEncoder -import java.util.Locale -import kotlinx.parcelize.Parcelize - -internal class CrashDetailsFragment : Fragment(R.layout.pluto___fragment_crash_details) { - - private val binding by viewBinding(PlutoFragmentCrashDetailsBinding::bind) - private val viewModel: CrashesViewModel by viewModels() - private val crashAdapter: BaseAdapter by lazy { CrashesAdapter(onActionListener) } - private val arguments by lazyParcelExtra() - private val exceptionCipher by lazy { getCipheredException() } - private val contentSharer by lazyContentSharer() - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - arguments?.id?.let { viewModel.fetch(it) } - binding.list.apply { - adapter = crashAdapter - } - binding.close.setDebounceClickListener { - activity?.onBackPressed() - } - - binding.delete.setDebounceClickListener { - viewModel.currentException.value?.id?.let { id -> - lifecycleScope.delayedLaunchWhenResumed(SCREEN_CLOSE_DELAY) { - viewModel.delete(id) - activity?.onBackPressed() - context?.toast("Crash logs deleted.") - } - } - } - - binding.share.setDebounceClickListener { - viewModel.currentException.value?.let { - contentSharer.share(Shareable(title = "Share Crash Report", content = it.data.toShareText(), fileName = "Crash Report from Pluto")) - } - } - - viewModel.currentException.removeObservers(viewLifecycleOwner) - viewModel.currentException.observe(viewLifecycleOwner, exceptionObserver) - } - - private fun getCipheredException(): String? { - viewModel.currentException.value?.data?.exception?.let { - val reportData = ReportData( - name = it.name, - message = it.message, - stackTrace = it.stackTrace.take(STACK_TRACE_SHORT_LENGTH) as ArrayList, - client = Pluto.appContext?.packageName - ) - val exceptionString = Gson().toJson(reportData) - - val encodedByteArray = exceptionString.toByteArray(Charsets.UTF_8) - val encodedString = encodeToString(encodedByteArray, DEFAULT) - -// val decodedByteArray = decode(encodedString, DEFAULT) -// val decodedString = String(decodedByteArray, Charsets.UTF_8) - - return URLEncoder.encode(encodedString, "utf-8") - } - return null - } - - private val onActionListener = object : DiffAwareAdapter.OnActionListener { - override fun onAction(action: String, data: ListItem, holder: DiffAwareHolder?) { - if (action == "report_crash") { - exceptionCipher?.let { - val url = "https://pluto.mocklets.com/exception/$it/a0bbe9cd-2f02-4a12-b7b7-36fce61a6b48" - DebugLog.d("Prateek", url) - val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(url)) - startActivity(browserIntent) - } - } - } - } - - private val exceptionObserver = Observer { - val list = arrayListOf() - list.add(it.data.exception) - it.data.threadStateList?.let { states -> list.add(states) } - it.data.thread?.let { thread -> list.add(thread) } - list.add(it.data.device) - - crashAdapter.list = list - } - - @Parcelize - data class Data(val id: Int) : Parcelable - - private companion object { - const val SCREEN_CLOSE_DELAY = 200L - private const val STACK_TRACE_SHORT_LENGTH = 15 - } -} - -private const val STACK_TRACE_LENGTH = 25 -private fun ExceptionAllData.toShareText(): String { - val text = StringBuilder() - text.append("EXCEPTION : \n") - text.append("${this.exception.name}: ${this.exception.message}\n") - this.exception.stackTrace.take(STACK_TRACE_LENGTH).forEach { - text.append("\t at $it\n") - } - if (this.exception.stackTrace.size - STACK_TRACE_LENGTH > 0) { - text.append("\t + ${this.exception.stackTrace.size - STACK_TRACE_LENGTH} more lines\n\n") - } - - this.thread?.let { - text.append("Thread : ") - text.append("${it.name.uppercase(Locale.getDefault())} (") - text.append("id : ${it.id}, ") - text.append("priority : ${ExceptionRepo.getPriorityString(it.priority)}, ") - text.append("is_Daemon : ${it.isDaemon}, ") - text.append("state : ${it.state}") - text.append(")") - - text.append("\n\n==================\n\n") - } - - text.append("APP STATE : \n") - this.device.software.appVersion?.let { - text.append("App Version : ${it.name} (${it.code})\n") - } - text.append("Android (OS : ${this.device.software.androidOs}, API_Level : ${this.device.software.androidAPILevel})\n") - text.append("Orientation : ${this.device.software.orientation}\n") - text.append("is_Rooted : ${this.device.isRooted}") - - text.append("\n\n==================\n\n") - - text.append("DEVICE INFO : \n") - text.append("Model : ${this.device.build.brand?.capitalizeText()} ${this.device.build.model}\n") - text.append( - "Screen : { height : ${this.device.screen.height}, width : ${this.device.screen.height}, " + - "density : ${this.device.screen.density}, size : ${this.device.screen.size} }" - ) - return text.toString() -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/ui/CrashesAdapter.kt b/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/ui/CrashesAdapter.kt deleted file mode 100644 index 08151520..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/ui/CrashesAdapter.kt +++ /dev/null @@ -1,53 +0,0 @@ -package com.mocklets.pluto.modules.exceptions.ui - -import android.view.ViewGroup -import com.mocklets.pluto.core.DeviceFingerPrint -import com.mocklets.pluto.core.ui.list.BaseAdapter -import com.mocklets.pluto.core.ui.list.DiffAwareHolder -import com.mocklets.pluto.core.ui.list.ListItem -import com.mocklets.pluto.modules.exceptions.ExceptionData -import com.mocklets.pluto.modules.exceptions.ProcessThread -import com.mocklets.pluto.modules.exceptions.ThreadData -import com.mocklets.pluto.modules.exceptions.ThreadStates -import com.mocklets.pluto.modules.exceptions.dao.ExceptionEntity -import com.mocklets.pluto.modules.exceptions.ui.holder.CrashItemDetailsDeviceHolder -import com.mocklets.pluto.modules.exceptions.ui.holder.CrashItemDetailsHeaderHolder -import com.mocklets.pluto.modules.exceptions.ui.holder.CrashItemDetailsThreadHolder -import com.mocklets.pluto.modules.exceptions.ui.holder.CrashItemDetailsThreadStatesHolder -import com.mocklets.pluto.modules.exceptions.ui.holder.CrashItemDetailsThreadStatesItemHolder -import com.mocklets.pluto.modules.exceptions.ui.holder.CrashItemHolder - -internal class CrashesAdapter(private val listener: OnActionListener) : BaseAdapter() { - override fun getItemViewType(item: ListItem): Int? { - return when (item) { - is ExceptionEntity -> ITEM_TYPE_CRASH - is ExceptionData -> ITEM_DETAILS_TYPE_HEADER - is ThreadData -> ITEM_DETAILS_TYPE_THREAD - is DeviceFingerPrint -> ITEM_DETAILS_TYPE_DEVICE - is ThreadStates -> ITEM_DETAILS_TYPE_THREAD_STATES - is ProcessThread -> ITEM_DETAILS_TYPE_THREAD_STATES_ITEM - else -> null - } - } - - override fun onViewHolderCreated(parent: ViewGroup, viewType: Int): DiffAwareHolder? { - return when (viewType) { - ITEM_TYPE_CRASH -> CrashItemHolder(parent, listener) - ITEM_DETAILS_TYPE_HEADER -> CrashItemDetailsHeaderHolder(parent, listener) - ITEM_DETAILS_TYPE_THREAD -> CrashItemDetailsThreadHolder(parent, listener) - ITEM_DETAILS_TYPE_DEVICE -> CrashItemDetailsDeviceHolder(parent, listener) - ITEM_DETAILS_TYPE_THREAD_STATES -> CrashItemDetailsThreadStatesHolder(parent, listener) - ITEM_DETAILS_TYPE_THREAD_STATES_ITEM -> CrashItemDetailsThreadStatesItemHolder(parent, listener) - else -> null - } - } - - companion object { - const val ITEM_TYPE_CRASH = 1000 - const val ITEM_DETAILS_TYPE_HEADER = 1100 - const val ITEM_DETAILS_TYPE_THREAD = 1101 - const val ITEM_DETAILS_TYPE_DEVICE = 1102 - const val ITEM_DETAILS_TYPE_THREAD_STATES = 1103 - const val ITEM_DETAILS_TYPE_THREAD_STATES_ITEM = 1104 - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/ui/CrashesFragment.kt b/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/ui/CrashesFragment.kt deleted file mode 100644 index f8163ae3..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/ui/CrashesFragment.kt +++ /dev/null @@ -1,95 +0,0 @@ -package com.mocklets.pluto.modules.exceptions.ui - -import android.os.Bundle -import android.view.View -import androidx.core.widget.addTextChangedListener -import androidx.fragment.app.Fragment -import androidx.fragment.app.activityViewModels -import androidx.lifecycle.Observer -import androidx.lifecycle.lifecycleScope -import com.mocklets.pluto.Pluto -import com.mocklets.pluto.R -import com.mocklets.pluto.core.binding.viewBinding -import com.mocklets.pluto.core.extensions.hideKeyboard -import com.mocklets.pluto.core.extensions.linearLayoutManager -import com.mocklets.pluto.core.extensions.showMoreOptions -import com.mocklets.pluto.core.extensions.toast -import com.mocklets.pluto.core.ui.list.BaseAdapter -import com.mocklets.pluto.core.ui.list.CustomItemDecorator -import com.mocklets.pluto.core.ui.list.DiffAwareAdapter -import com.mocklets.pluto.core.ui.list.DiffAwareHolder -import com.mocklets.pluto.core.ui.list.ListItem -import com.mocklets.pluto.core.ui.routing.Screens -import com.mocklets.pluto.core.ui.routing.lazyRouter -import com.mocklets.pluto.core.ui.setDebounceClickListener -import com.mocklets.pluto.databinding.PlutoFragmentCrashesBinding -import com.mocklets.pluto.modules.exceptions.dao.ExceptionEntity - -internal class CrashesFragment : Fragment(R.layout.pluto___fragment_crashes) { - - private val binding by viewBinding(PlutoFragmentCrashesBinding::bind) - private val viewModel: CrashesViewModel by activityViewModels() - private val crashAdapter: BaseAdapter by lazy { CrashesAdapter(onActionListener) } - private val router by lazyRouter() - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - binding.logList.apply { - adapter = crashAdapter - addItemDecoration(CustomItemDecorator(requireContext())) - } - binding.search.addTextChangedListener { editable -> - lifecycleScope.launchWhenResumed { - editable?.toString()?.let { - Pluto.session.exceptionSearchText = it - crashAdapter.list = filteredLogs(it) - if (it.isEmpty()) { - binding.logList.linearLayoutManager()?.scrollToPositionWithOffset(0, 0) - } - } - } - } - binding.more.setDebounceClickListener { - requireContext().showMoreOptions(it, R.menu.pluto___popup_menu_crashes) { item -> - when (item.itemId) { - R.id.clear -> viewModel.deleteAll() - } - } - } - binding.search.setText(Pluto.session.exceptionSearchText) - viewModel.exceptions.removeObserver(exceptionObserver) - viewModel.exceptions.observe(viewLifecycleOwner, exceptionObserver) - } - - private fun filteredLogs(search: String): List { - var list = emptyList() - viewModel.exceptions.value?.let { - list = it.filter { exception -> - (exception.data.exception.name ?: "").contains(search, true) || - (exception.data.exception.file ?: "").contains(search, true) - } - } - binding.noItemText.text = getString( - if (search.isNotEmpty()) R.string.pluto___no_search_result else R.string.pluto___no_crashes_text - ) - binding.noItemText.visibility = if (list.isEmpty()) View.VISIBLE else View.GONE - return list - } - - private val exceptionObserver = Observer> { - crashAdapter.list = filteredLogs(binding.search.text.toString()) - } - - private val onActionListener = object : DiffAwareAdapter.OnActionListener { - override fun onAction(action: String, data: ListItem, holder: DiffAwareHolder?) { - if (data is ExceptionEntity) { - activity?.hideKeyboard() - if (data.id != null) { - router.navigate(Screens.CrashDetails(CrashDetailsFragment.Data(data.id))) - } else { - context?.toast("invalid crash id") - } - } - } - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/ui/CrashesViewModel.kt b/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/ui/CrashesViewModel.kt deleted file mode 100644 index 26f03c9a..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/ui/CrashesViewModel.kt +++ /dev/null @@ -1,58 +0,0 @@ -package com.mocklets.pluto.modules.exceptions.ui - -import android.app.Application -import androidx.lifecycle.AndroidViewModel -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.viewModelScope -import com.mocklets.pluto.core.database.DatabaseManager -import com.mocklets.pluto.modules.exceptions.dao.ExceptionDao -import com.mocklets.pluto.modules.exceptions.dao.ExceptionEntity -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch - -internal class CrashesViewModel(application: Application) : AndroidViewModel(application) { - - private val exceptionDao: ExceptionDao by lazy { DatabaseManager(application.applicationContext).db.exceptionDao() } - - init { - fetchAll() - } - - val exceptions: LiveData> - get() = _exceptions - private val _exceptions = MutableLiveData>() - - val currentException: LiveData - get() = _currentException - private val _currentException = MutableLiveData() - - private fun fetchAll() { - viewModelScope.launch(Dispatchers.IO) { - val list = exceptionDao.fetchAll() ?: arrayListOf() - _exceptions.postValue(list) - } - } - - fun fetch(id: Int) { - viewModelScope.launch(Dispatchers.IO) { - exceptionDao.fetch(id)?.let { - _currentException.postValue(it) - } - } - } - - fun delete(id: Int) { - viewModelScope.launch(Dispatchers.IO) { - exceptionDao.delete(id) - _exceptions.postValue(exceptionDao.fetchAll() ?: arrayListOf()) - } - } - - fun deleteAll() { - viewModelScope.launch(Dispatchers.IO) { - exceptionDao.deleteAll() - _exceptions.postValue(arrayListOf()) - } - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/ui/holder/CrashItemDetailsDeviceHolder.kt b/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/ui/holder/CrashItemDetailsDeviceHolder.kt deleted file mode 100644 index f3ebec0e..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/ui/holder/CrashItemDetailsDeviceHolder.kt +++ /dev/null @@ -1,47 +0,0 @@ -package com.mocklets.pluto.modules.exceptions.ui.holder - -import android.view.ViewGroup -import com.mocklets.pluto.R -import com.mocklets.pluto.core.DeviceFingerPrint -import com.mocklets.pluto.core.extensions.capitalizeText -import com.mocklets.pluto.core.extensions.inflate -import com.mocklets.pluto.core.ui.list.DiffAwareAdapter -import com.mocklets.pluto.core.ui.list.DiffAwareHolder -import com.mocklets.pluto.core.ui.list.ListItem -import com.mocklets.pluto.databinding.PlutoItemCrashDetailsDeviceBinding - -internal class CrashItemDetailsDeviceHolder( - parent: ViewGroup, - actionListener: DiffAwareAdapter.OnActionListener -) : DiffAwareHolder(parent.inflate(R.layout.pluto___item_crash_details_device), actionListener) { - - private val binding = PlutoItemCrashDetailsDeviceBinding.bind(itemView) - - private val appVersion = binding.appVersion - private val androidOS = binding.androidOS - private val androidAPI = binding.androidAPILevel - private val orientation = binding.orientation - private val rooted = binding.rooted - - private val height = binding.deviceHeight - private val width = binding.deviceWidth - private val density = binding.density - private val size = binding.size - private val build = binding.build - - override fun onBind(item: ListItem) { - if (item is DeviceFingerPrint) { - item.software.appVersion?.let { appVersion.text = "${it.name} (${it.code})" } - androidOS.text = item.software.androidOs - androidAPI.text = item.software.androidAPILevel - orientation.text = item.software.orientation.capitalizeText() - rooted.text = item.isRooted.toString() - - build.text = "${item.build.brand?.capitalizeText()} ${item.build.model}" - height.text = item.screen.height - width.text = item.screen.width - density.text = item.screen.density - size.text = item.screen.size - } - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/ui/holder/CrashItemDetailsHeaderHolder.kt b/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/ui/holder/CrashItemDetailsHeaderHolder.kt deleted file mode 100644 index bb392e2d..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/ui/holder/CrashItemDetailsHeaderHolder.kt +++ /dev/null @@ -1,112 +0,0 @@ -package com.mocklets.pluto.modules.exceptions.ui.holder - -import android.view.View.GONE -import android.view.View.VISIBLE -import android.view.ViewGroup -import com.mocklets.pluto.BuildConfig -import com.mocklets.pluto.R -import com.mocklets.pluto.core.extensions.asFormattedDate -import com.mocklets.pluto.core.extensions.color -import com.mocklets.pluto.core.extensions.inflate -import com.mocklets.pluto.core.ui.list.DiffAwareAdapter -import com.mocklets.pluto.core.ui.list.DiffAwareHolder -import com.mocklets.pluto.core.ui.list.ListItem -import com.mocklets.pluto.core.ui.setDebounceClickListener -import com.mocklets.pluto.core.ui.spannable.setSpan -import com.mocklets.pluto.databinding.PlutoItemCrashDetailsHeaderBinding -import com.mocklets.pluto.modules.exceptions.ExceptionData -import com.mocklets.pluto.modules.exceptions.anrs.AnrSupervisor.Companion.MAIN_THREAD_RESPONSE_THRESHOLD - -internal class CrashItemDetailsHeaderHolder( - parent: ViewGroup, - actionListener: DiffAwareAdapter.OnActionListener -) : DiffAwareHolder(parent.inflate(R.layout.pluto___item_crash_details_header), actionListener) { - - private val binding = PlutoItemCrashDetailsHeaderBinding.bind(itemView) - private val timestamp = binding.timestamp - private val stacktrace = binding.stackTrace - private val reportCrash = binding.reportCrash.crashReportRoot - private val message = binding.message - private val title = binding.title - - override fun onBind(item: ListItem) { - if (item is ExceptionData) { - handleTitle(item) - timestamp.text = item.timeStamp.asFormattedDate() - - stacktrace.setSpan { - append("${item.name}: ${item.message}") - item.stackTrace.take(MAX_STACK_TRACE_LINES).forEach { - append("\n\t\t\t") - append( - fontColor( - " at ", context.color(R.color.pluto___text_dark_40) - ) - ) - append(it) - } - val extraTrace = item.stackTrace.size - MAX_STACK_TRACE_LINES - if (extraTrace > 0) { - append( - fontColor( - "\n\t\t\t + $extraTrace more lines", context.color(R.color.pluto___text_dark_40) - ) - ) - } - } - - if (isPlutoCrash(item.stackTrace.take(MAX_STACK_TRACE_LINES))) { - reportCrash.visibility = VISIBLE - reportCrash.setDebounceClickListener { - onAction("report_crash") - } - } else { - reportCrash.visibility = GONE - reportCrash.setDebounceClickListener {} - } - } - } - - private fun handleTitle(item: ExceptionData) { - if (item.isANRException) { - message.text = - context.getString(R.string.pluto___anr_list_message, MAIN_THREAD_RESPONSE_THRESHOLD) - title.setSpan { - context.apply { - append( - fontColor( - getString(R.string.pluto___anr_list_title), - color(R.color.pluto___text_dark_80) - ) - ) - } - } - } else { - title.setSpan { - append("${item.file}\t\t") - append( - fontColor("line: ${item.lineNumber}", context.color(R.color.pluto___text_dark_80)) - ) - } - message.setSpan { - append("${item.name}\n") - append( - fontColor("${item.message}", context.color(R.color.pluto___text_dark_60)) - ) - } - } - } - - private fun isPlutoCrash(trace: List): Boolean { - trace.forEach { - if (it.startsWith(BuildConfig.LIBRARY_PACKAGE_NAME)) { - return true - } - } - return false - } - - companion object { - const val MAX_STACK_TRACE_LINES = 20 - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/ui/holder/CrashItemDetailsThreadHolder.kt b/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/ui/holder/CrashItemDetailsThreadHolder.kt deleted file mode 100644 index 0c40ba65..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/ui/holder/CrashItemDetailsThreadHolder.kt +++ /dev/null @@ -1,39 +0,0 @@ -package com.mocklets.pluto.modules.exceptions.ui.holder - -import android.view.ViewGroup -import com.mocklets.pluto.R -import com.mocklets.pluto.core.extensions.color -import com.mocklets.pluto.core.extensions.inflate -import com.mocklets.pluto.core.ui.list.DiffAwareAdapter -import com.mocklets.pluto.core.ui.list.DiffAwareHolder -import com.mocklets.pluto.core.ui.list.ListItem -import com.mocklets.pluto.core.ui.spannable.setSpan -import com.mocklets.pluto.databinding.PlutoItemCrashDetailsThreadBinding -import com.mocklets.pluto.modules.exceptions.ExceptionRepo -import com.mocklets.pluto.modules.exceptions.ThreadData - -internal class CrashItemDetailsThreadHolder( - parent: ViewGroup, - actionListener: DiffAwareAdapter.OnActionListener -) : DiffAwareHolder(parent.inflate(R.layout.pluto___item_crash_details_thread), actionListener) { - - private val binding = PlutoItemCrashDetailsThreadBinding.bind(itemView) - private val name = binding.name - private val priority = binding.priority - private val daemon = binding.daemon - private val state = binding.state - - override fun onBind(item: ListItem) { - if (item is ThreadData) { - name.setSpan { - append("${item.name.uppercase()} ") - append( - fontColor("(thread id: ${item.id})", context.color(R.color.pluto___text_dark_60)) - ) - } - priority.text = ExceptionRepo.getPriorityString(item.priority) - daemon.text = item.isDaemon.toString() - state.text = item.state - } - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/ui/holder/CrashItemDetailsThreadStatesHolder.kt b/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/ui/holder/CrashItemDetailsThreadStatesHolder.kt deleted file mode 100644 index 807c22e1..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/ui/holder/CrashItemDetailsThreadStatesHolder.kt +++ /dev/null @@ -1,55 +0,0 @@ -package com.mocklets.pluto.modules.exceptions.ui.holder - -import android.view.ViewGroup -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView -import com.mocklets.pluto.R -import com.mocklets.pluto.core.extensions.color -import com.mocklets.pluto.core.extensions.dp -import com.mocklets.pluto.core.extensions.inflate -import com.mocklets.pluto.core.ui.list.BaseAdapter -import com.mocklets.pluto.core.ui.list.CustomItemDecorator -import com.mocklets.pluto.core.ui.list.DiffAwareAdapter -import com.mocklets.pluto.core.ui.list.DiffAwareHolder -import com.mocklets.pluto.core.ui.list.ListItem -import com.mocklets.pluto.core.ui.spannable.setSpan -import com.mocklets.pluto.databinding.PlutoItemCrashDetailsThreadStatesBinding -import com.mocklets.pluto.modules.exceptions.ThreadStates -import com.mocklets.pluto.modules.exceptions.ui.CrashesAdapter - -internal class CrashItemDetailsThreadStatesHolder( - parent: ViewGroup, - actionListener: DiffAwareAdapter.OnActionListener -) : DiffAwareHolder(parent.inflate(R.layout.pluto___item_crash_details_thread_states), actionListener) { - - private val binding = PlutoItemCrashDetailsThreadStatesBinding.bind(itemView) - private val onActionListener = object : DiffAwareAdapter.OnActionListener { - override fun onAction(action: String, data: ListItem, holder: DiffAwareHolder?) { - } - } - - private var linearLayoutManager: LinearLayoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false) - private var threadAdapter: BaseAdapter = CrashesAdapter(onActionListener) - - init { - binding.list.apply { - adapter = threadAdapter - layoutManager = linearLayoutManager - addItemDecoration(CustomItemDecorator(context!!, DECORATOR_DIVIDER_PADDING)) - } - } - - override fun onBind(item: ListItem) { - if (item is ThreadStates) { - threadAdapter.list = item.states - binding.label.setSpan { - append(context.getString(R.string.pluto___thread_states_label)) - append(fontColor(" (${item.states.size})", context.color(R.color.pluto___text_dark_40))) - } - } - } - - private companion object { - val DECORATOR_DIVIDER_PADDING = 16f.dp.toInt() - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/ui/holder/CrashItemDetailsThreadStatesItemHolder.kt b/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/ui/holder/CrashItemDetailsThreadStatesItemHolder.kt deleted file mode 100644 index 6d1e1571..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/ui/holder/CrashItemDetailsThreadStatesItemHolder.kt +++ /dev/null @@ -1,50 +0,0 @@ -package com.mocklets.pluto.modules.exceptions.ui.holder - -import android.view.ViewGroup -import com.mocklets.pluto.R -import com.mocklets.pluto.core.extensions.color -import com.mocklets.pluto.core.extensions.inflate -import com.mocklets.pluto.core.ui.list.DiffAwareAdapter -import com.mocklets.pluto.core.ui.list.DiffAwareHolder -import com.mocklets.pluto.core.ui.list.ListItem -import com.mocklets.pluto.core.ui.spannable.setSpan -import com.mocklets.pluto.databinding.PlutoItemAnrThreadStateBinding -import com.mocklets.pluto.modules.exceptions.ProcessThread - -internal class CrashItemDetailsThreadStatesItemHolder( - parent: ViewGroup, - actionListener: DiffAwareAdapter.OnActionListener -) : DiffAwareHolder(parent.inflate(R.layout.pluto___item_anr_thread_state), actionListener) { - - private val binding = PlutoItemAnrThreadStateBinding.bind(itemView) - - override fun onBind(item: ListItem) { - if (item is ProcessThread) { - binding.stackTrace.setSpan { - append( - fontColor( - semiBold("${item.name} (${item.state.uppercase()})"), - context.color(if (item.state == Thread.State.BLOCKED.name) R.color.pluto___red else R.color.pluto___text_dark_80) - ) - ) - item.stackTrace.take(MAX_STACK_TRACE_LINES).forEach { - append("\n\t") - append(fontColor(" at ", context.color(R.color.pluto___text_dark_40))) - append(it) - } - val extraTrace = item.stackTrace.size - MAX_STACK_TRACE_LINES - if (extraTrace > 0) { - append( - fontColor( - "\n\t + $extraTrace more lines", context.color(R.color.pluto___text_dark_40) - ) - ) - } - } - } - } - - companion object { - const val MAX_STACK_TRACE_LINES = 10 - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/ui/holder/CrashItemHolder.kt b/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/ui/holder/CrashItemHolder.kt deleted file mode 100644 index c8b8f173..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/exceptions/ui/holder/CrashItemHolder.kt +++ /dev/null @@ -1,61 +0,0 @@ -package com.mocklets.pluto.modules.exceptions.ui.holder - -import android.view.ViewGroup -import com.mocklets.pluto.R -import com.mocklets.pluto.core.extensions.asTimeElapsed -import com.mocklets.pluto.core.extensions.color -import com.mocklets.pluto.core.extensions.inflate -import com.mocklets.pluto.core.ui.list.DiffAwareAdapter -import com.mocklets.pluto.core.ui.list.DiffAwareHolder -import com.mocklets.pluto.core.ui.list.ListItem -import com.mocklets.pluto.core.ui.setDebounceClickListener -import com.mocklets.pluto.core.ui.spannable.setSpan -import com.mocklets.pluto.databinding.PlutoItemCrashBinding -import com.mocklets.pluto.modules.exceptions.anrs.AnrSupervisor.Companion.MAIN_THREAD_RESPONSE_THRESHOLD -import com.mocklets.pluto.modules.exceptions.dao.ExceptionEntity - -internal class CrashItemHolder( - parent: ViewGroup, - actionListener: DiffAwareAdapter.OnActionListener -) : DiffAwareHolder(parent.inflate(R.layout.pluto___item_crash), actionListener) { - - private val binding = PlutoItemCrashBinding.bind(itemView) - private val timeElapsed = binding.timeElapsed - - override fun onBind(item: ListItem) { - if (item is ExceptionEntity) { - with(item.data.exception) { - if (isANRException) { - binding.message.text = - context.getString(R.string.pluto___anr_list_message, MAIN_THREAD_RESPONSE_THRESHOLD) - binding.title.setSpan { - context.apply { - append( - fontColor( - getString(R.string.pluto___anr_list_title), color(R.color.pluto___text_dark_80) - ) - ) - } - } - binding.title.setCompoundDrawablesWithIntrinsicBounds(R.drawable.pluto___ic_anr_warning, 0, 0, 0) - } else { - binding.message.setSpan { - append("${item.data.exception.file}\t\t") - append( - fontColor( - "line:${item.data.exception.lineNumber}", - context.color(R.color.pluto___text_dark_60) - ) - ) - } - binding.title.text = item.data.exception.name - binding.title.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0) - } - } - timeElapsed.text = item.timestamp.asTimeElapsed() - itemView.setDebounceClickListener { - onAction("click") - } - } - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/logging/ui/LogDetailsDialog.kt b/pluto/src/main/java/com/mocklets/pluto/modules/logging/ui/LogDetailsDialog.kt deleted file mode 100644 index b2adac13..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/logging/ui/LogDetailsDialog.kt +++ /dev/null @@ -1,142 +0,0 @@ -package com.mocklets.pluto.modules.logging.ui - -import android.content.Context -import android.graphics.drawable.ColorDrawable -import android.view.View -import android.view.View.GONE -import android.view.View.VISIBLE -import android.widget.FrameLayout -import androidx.fragment.app.FragmentActivity -import com.google.android.material.bottomsheet.BottomSheetBehavior -import com.google.android.material.bottomsheet.BottomSheetDialog -import com.mocklets.pluto.R -import com.mocklets.pluto.core.DeviceInfo -import com.mocklets.pluto.core.extensions.color -import com.mocklets.pluto.core.extensions.inflate -import com.mocklets.pluto.core.sharing.Shareable -import com.mocklets.pluto.core.sharing.lazyContentSharer -import com.mocklets.pluto.core.ui.setDebounceClickListener -import com.mocklets.pluto.core.ui.spannable.createSpan -import com.mocklets.pluto.core.ui.spannable.setSpan -import com.mocklets.pluto.databinding.PlutoLayoutLogDetailsBinding -import com.mocklets.pluto.modules.exceptions.asExceptionData -import com.mocklets.pluto.modules.logging.LogData -import com.mocklets.pluto.modules.logging.ui.LogDetailsDialog.Companion.MAX_STACK_TRACE_LINES - -internal class LogDetailsDialog(context: FragmentActivity, data: LogData) : - BottomSheetDialog(context, R.style.PlutoBottomSheetDialogTheme) { - - private val sheetView: View = context.inflate(R.layout.pluto___layout_log_details) - private val binding = PlutoLayoutLogDetailsBinding.bind(sheetView) - private val deviceInfo = DeviceInfo(context) - private val contentSharer by context.lazyContentSharer() - - init { - setContentView(sheetView) - (sheetView.parent as View).background = ColorDrawable(context.color(R.color.pluto___transparent)) - - this.setOnShowListener { dialog -> - val d = dialog as BottomSheetDialog - - val bottomSheet = d.findViewById(R.id.design_bottom_sheet) as FrameLayout? - val behavior = BottomSheetBehavior.from(bottomSheet!!) - behavior.apply { - state = BottomSheetBehavior.STATE_EXPANDED - isHideable = false - skipCollapsed = true - peekHeight = deviceInfo.height - } - - binding.title.setSpan { - append("${context.getString(R.string.pluto___log_details)} ") - append(italic(fontColor(data.level.label.uppercase(), context.color(data.level.textColor)))) - } - - binding.cta.setDebounceClickListener { - contentSharer.share(Shareable(title = "Share Log details", content = data.toShareText(context))) - } - - binding.tag.text = data.tag - binding.filename.setSpan { - append(fontColor("called from\n", context.color(R.color.pluto___text_dark_40))) - append(data.stackTraceElement.methodName) - append(fontColor(" (", context.color(R.color.pluto___text_dark_40))) - append(data.stackTraceElement.fileName) - append(fontColor(", line:", context.color(R.color.pluto___text_dark_60))) - append(fontColor("${data.stackTraceElement.lineNumber}", context.color(R.color.pluto___text_dark_80))) - append(fontColor(")", context.color(R.color.pluto___text_dark_40))) - } - binding.message.text = data.message - binding.stackTraceContainer.visibility = GONE - data.tr?.asExceptionData()?.let { - binding.stackTraceContainer.visibility = VISIBLE - binding.stackTrace.setSpan { - append(fontColor("${it.name}: ${it.message}", context.color(R.color.pluto___text_dark_80))) - it.stackTrace.take(MAX_STACK_TRACE_LINES).forEach { - append("\n\t\t\t") - append(fontColor(" at ", context.color(R.color.pluto___text_dark_40))) - append(it) - } - val extraTrace = it.stackTrace.size - MAX_STACK_TRACE_LINES - if (extraTrace > 0) { - append( - fontColor( - "\n\t\t\t + $extraTrace more lines", context.color(R.color.pluto___text_dark_40) - ) - ) - } - } - } - - if (!data.eventAttributes.isNullOrEmpty()) { - binding.stackTraceContainer.visibility = VISIBLE - binding.stackTraceTitle.setSpan { - append(context.getString(R.string.pluto___event_attributes)) - append(fontColor(" (${data.eventAttributes.size})", context.color(R.color.pluto___text_dark_40))) - } - binding.stackTrace.text = context.beautifyAttributes(data.eventAttributes) - } - } - } - - companion object { - const val MAX_STACK_TRACE_LINES = 15 - } -} - -private fun Context?.beautifyAttributes(data: Map): CharSequence? { - return this?.createSpan { - data.forEach { - append("${it.key} : ") - if (it.value != null) { - append(fontColor(semiBold("${it.value}"), context.color(R.color.pluto___text_dark_80))) - } else { - append(fontColor(light(italic("null")), context.color(R.color.pluto___text_dark_40))) - } - append("\n") - } - }?.trim() -} - -private fun LogData.toShareText(context: Context): String { - val text = StringBuilder() - text.append("$tag : $message\n") - - tr?.asExceptionData()?.let { - text.append("\n${it.name}: ${it.message}\n") - it.stackTrace.take(MAX_STACK_TRACE_LINES).forEach { trace -> - text.append("\t at $trace\n") - } - if (it.stackTrace.size - MAX_STACK_TRACE_LINES > 0) { - text.append("\t + ${it.stackTrace.size - MAX_STACK_TRACE_LINES} more lines\n\n") - } - } - - if (!eventAttributes.isNullOrEmpty()) { - text.append("\n${context.getString(R.string.pluto___event_attributes).lowercase()} - ") - eventAttributes.entries.forEach { - text.append("\n\t ${it.key} : ${it.value}") - } - } - return text.toString() -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/logging/ui/LogsViewModel.kt b/pluto/src/main/java/com/mocklets/pluto/modules/logging/ui/LogsViewModel.kt deleted file mode 100644 index 386c9ac3..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/logging/ui/LogsViewModel.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.mocklets.pluto.modules.logging.ui - -import androidx.lifecycle.LiveData -import androidx.lifecycle.ViewModel -import com.mocklets.pluto.modules.logging.LogData -import com.mocklets.pluto.modules.logging.LogsRepo - -internal class LogsViewModel : ViewModel() { - - val logs: LiveData> - get() = LogsRepo.getLogsLD() -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/network/BodyTransformer.kt b/pluto/src/main/java/com/mocklets/pluto/modules/network/BodyTransformer.kt deleted file mode 100644 index b3a5c281..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/network/BodyTransformer.kt +++ /dev/null @@ -1,154 +0,0 @@ -package com.mocklets.pluto.modules.network - -import android.content.Context -import com.mocklets.pluto.R -import com.mocklets.pluto.core.DebugLog -import com.mocklets.pluto.core.extensions.color -import com.mocklets.pluto.core.ui.spannable.createSpan -import com.mocklets.pluto.modules.network.interceptor.doUnZipToString -import com.mocklets.pluto.modules.network.transformers.FormEncodedTransformer -import com.mocklets.pluto.modules.network.transformers.JsonBaseTransformer -import com.mocklets.pluto.modules.network.transformers.XmlBaseTransformer -import java.nio.charset.Charset -import okhttp3.HttpUrl -import okhttp3.RequestBody -import okhttp3.ResponseBody -import okio.Buffer -import okio.IOException - -internal fun RequestBody.convertPretty(gzipped: Boolean): ProcessedBody { - contentType()?.let { - DebugLog.e(LOGTAG, "request : ${it.type()}, ${it.subtype()}, ${it.charset()}") - return if (it.isText()) { - val plainBody = convert(gzipped) - ProcessedBody( - isValid = true, - body = it.beautify(plainBody), - mediaType = it.type(), - mediaSubtype = it.subtype() - ) - } else { - ProcessedBody( - isValid = true, - body = BINARY_BODY, - mediaType = BINARY_MEDIA_TYPE, - mediaSubtype = BINARY_MEDIA_TYPE - ) - } - } - return ProcessedBody( - isValid = false, - mediaType = null, - mediaSubtype = null - ) -} - -internal fun ResponseBody?.convertPretty(buffer: Buffer): ProcessedBody? { - this?.let { - val contentType = it.contentType() - if (contentType != null) { - return if (contentType.isText()) { - val body = buffer.readString(contentType.charset(UTF8) ?: UTF8) - ProcessedBody( - isValid = true, - body = contentType.beautify(body), - mediaType = contentType.type(), - mediaSubtype = contentType.subtype() - ) - } else { - // todo process image response - ProcessedBody( - isValid = true, - body = BINARY_BODY, - mediaType = BINARY_MEDIA_TYPE, - mediaSubtype = BINARY_MEDIA_TYPE - ) - } - } - return ProcessedBody( - isValid = false, - mediaType = null, - mediaSubtype = null - ) - } - return null -} - -private fun RequestBody.convert(gzipped: Boolean): CharSequence { - return try { - val buffer = Buffer() - writeTo(buffer) - if (gzipped) { - doUnZipToString(buffer.readByteArray()) - } else { - buffer.readUtf8() - } - } catch (e: IOException) { - DebugLog.e(LOGTAG, "request body parsing failed", e) - "" - } -} - -internal fun Context?.beautifyHeaders(data: Map): CharSequence? { - return this?.createSpan { - data.forEach { - append("${it.key} : ") - if (it.value != null) { - append(fontColor(semiBold("${it.value}"), context.color(R.color.pluto___text_dark_80))) - } else { - append(fontColor(light(italic("null")), context.color(R.color.pluto___text_dark_40))) - } - append("\n") - } - }?.trim() -} - -internal fun Context?.beautifyQueryParams(url: HttpUrl): CharSequence? { - return this?.createSpan { - url.queryParameterNames().forEach { - append("$it : ") - val value = url.queryParameter(it) - if (value != null) { - append(fontColor(semiBold("$value"), context.color(R.color.pluto___text_dark_80))) - } else { - append(fontColor(light(italic("null")), context.color(R.color.pluto___text_dark_40))) - } - append("\n") - } - }?.trim() -} - -internal fun ProcessedBody.flatten(): String? { - body?.toString()?.let { body -> - return when { - mediaType == "binary" -> body - mediaSubtype == "json" -> JsonBaseTransformer().flatten(body) - mediaSubtype == "xml" || mediaSubtype == "html" -> XmlBaseTransformer().flatten(body) - mediaSubtype == "x-www-form-urlencoded" -> FormEncodedTransformer().flatten(body) - else -> body - } - } - return null -} - -internal fun String.pruneQueryParams(): String { - val separated: List = split("?") - return separated[0] -} - -internal fun HttpUrl.hostUrl(): String { - val hostString = StringBuilder() - hostString.append("${scheme()}://${host()}") - if (port() != HTTP_PORT && port() != HTTPS_PORT) { - hostString.append(":${port()}") - } - return hostString.toString() -} - -internal const val LOGTAG = "pluto_sdk" -internal const val BODY_INDENTATION = 4 -private const val BINARY_BODY = "~ Binary Data" -internal const val BINARY_MEDIA_TYPE = "binary" -internal val UTF8 = Charset.forName("UTF-8") -private const val HTTP_PORT = 80 -private const val HTTPS_PORT = 443 diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/network/CurlBuilder.kt b/pluto/src/main/java/com/mocklets/pluto/modules/network/CurlBuilder.kt deleted file mode 100644 index eca8c99b..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/network/CurlBuilder.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.mocklets.pluto.modules.network - -internal fun RequestData.getCurl(): String { - val curlCommandBuilder = StringBuilder("") - curlCommandBuilder.append("cURL") - curlCommandBuilder.append(" -X") - curlCommandBuilder.append(" ${method.uppercase()}") - for (headerName in headers) { - curlCommandBuilder.append(headerPair(headerName.key, headerName.value)) - } - - body?.let { - curlCommandBuilder.append(" -d '${it.flatten()}'") - } - curlCommandBuilder.append(" \"$url\"") - curlCommandBuilder.append(" -L") - return curlCommandBuilder.toString() // beautify(request.url.toString(), curlCommandBuilder.toString()) -} - -private fun headerPair(headerName: String, headerValue: String?): String { - return " -H \"$headerName: $headerValue\"" -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/network/DataModel.kt b/pluto/src/main/java/com/mocklets/pluto/modules/network/DataModel.kt deleted file mode 100644 index 597c0855..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/network/DataModel.kt +++ /dev/null @@ -1,63 +0,0 @@ -package com.mocklets.pluto.modules.network - -import com.mocklets.pluto.core.ui.list.ListItem -import com.mocklets.pluto.modules.exceptions.ExceptionData -import okhttp3.HttpUrl -import okhttp3.Protocol - -internal data class RequestData( - val url: HttpUrl, - val method: String, - val body: ProcessedBody?, - val headers: Map, -// val headerCount: Int, - val timestamp: Long, - val isGzipped: Boolean -) - -internal data class ProxyConfig( - val url: String -) - -internal data class ResponseData( - val status: Status, - val isSuccessful: Boolean, - val body: ProcessedBody?, - val protocol: Protocol, - val fromDiskCache: Boolean, - val headers: Map, - val sendTimeMillis: Long, - val receiveTimeMillis: Long, - val isGzipped: Boolean -) - -internal data class Status( - val code: Int, - val message: String -) - -internal class ApiCallData( - val id: String, - val request: RequestData, - var hasResponseBody: Boolean = false, - var response: ResponseData? = null, - var exception: ExceptionData? = null, - var proxy: ProxyConfig? = null -) : ListItem() { - val curl: String = request.getCurl() - override fun isEqual(other: Any): Boolean { - if (other is ApiCallData) { - id == other.id && response == other.response && exception == other.exception - } - return false - } -} - -internal data class ProcessedBody( - val isValid: Boolean, - val body: CharSequence? = null, - val mediaType: String?, - val mediaSubtype: String? -) { - val isBinary: Boolean = mediaType == BINARY_MEDIA_TYPE -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/network/MediaTypeKtx.kt b/pluto/src/main/java/com/mocklets/pluto/modules/network/MediaTypeKtx.kt deleted file mode 100644 index e25689bb..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/network/MediaTypeKtx.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.mocklets.pluto.modules.network - -import com.mocklets.pluto.modules.network.transformers.FormEncodedTransformer -import com.mocklets.pluto.modules.network.transformers.JsonBaseTransformer -import com.mocklets.pluto.modules.network.transformers.XmlBaseTransformer -import okhttp3.MediaType - -internal fun MediaType.isText(): Boolean { - return (type() == "application" || type() == "text") && - (subtype().endsWith("json") || subtype() == "plain" || subtype() == "xml" || subtype() == "html" || subtype() == "x-www-form-urlencoded") -} - -internal fun MediaType.beautify(plain: CharSequence, indent: Int = BODY_INDENTATION): CharSequence? { - return when { - subtype().endsWith("json") -> JsonBaseTransformer().beautify(plain, indent) - subtype() == "xml" || subtype() == "html" -> XmlBaseTransformer().beautify(plain, indent) - subtype() == "x-www-form-urlencoded" -> FormEncodedTransformer().beautify(plain) - else -> plain - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/network/NetworkCallsRepo.kt b/pluto/src/main/java/com/mocklets/pluto/modules/network/NetworkCallsRepo.kt deleted file mode 100644 index 91c5dada..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/network/NetworkCallsRepo.kt +++ /dev/null @@ -1,40 +0,0 @@ -package com.mocklets.pluto.modules.network - -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData -import java.util.Collections - -internal object NetworkCallsRepo { - - val apiCalls: LiveData> - get() = _apiCalls - private val _apiCalls = MutableLiveData>() - private val apiCallMap = Collections.synchronizedMap(LinkedHashMap()) - - fun set(request: ApiCallData) { - synchronized(apiCallMap) { - apiCallMap[request.id] = request - apiCallMap.updateLiveData() - } - } - - fun get(id: String): ApiCallData? { - return synchronized(apiCallMap) { - apiCallMap.getOrElse(id) { null } - } - } - - fun deleteAll() { - synchronized(apiCallMap) { - apiCallMap.clear() - apiCallMap.updateLiveData() - } - } - - private fun MutableMap.updateLiveData() { - val list = arrayListOf() - list.addAll(values) - list.reverse() - _apiCalls.postValue(list) - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/network/interceptor/CacheDirectoryProvider.kt b/pluto/src/main/java/com/mocklets/pluto/modules/network/interceptor/CacheDirectoryProvider.kt deleted file mode 100644 index fe15a6e2..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/network/interceptor/CacheDirectoryProvider.kt +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Source: chucker - https://github.com/ChuckerTeam/chucker/blob/develop/library/src/main/java/com/chuckerteam/chucker/internal/support/CacheDirectoryProvider.kt - * License: https://github.com/ChuckerTeam/chucker/blob/develop/LICENSE.txt - */ -package com.mocklets.pluto.modules.network.interceptor - -import java.io.File - -/** - * An interface that returns a reference to a cache directory where temporary files can be - * saved. - */ -internal fun interface CacheDirectoryProvider { - fun provide(): File? -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/network/interceptor/DataConvertor.kt b/pluto/src/main/java/com/mocklets/pluto/modules/network/interceptor/DataConvertor.kt deleted file mode 100644 index cd8823ab..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/network/interceptor/DataConvertor.kt +++ /dev/null @@ -1,149 +0,0 @@ -package com.mocklets.pluto.modules.network.interceptor - -import com.mocklets.pluto.core.DebugLog -import com.mocklets.pluto.modules.network.ProcessedBody -import com.mocklets.pluto.modules.network.RequestData -import com.mocklets.pluto.modules.network.ResponseData -import com.mocklets.pluto.modules.network.Status -import com.mocklets.pluto.modules.network.convertPretty -import java.io.ByteArrayInputStream -import java.io.ByteArrayOutputStream -import java.io.InputStream -import java.io.OutputStream -import java.nio.charset.Charset -import java.util.zip.GZIPInputStream -import okhttp3.Request -import okhttp3.Response -import okio.IOException - -internal fun Request.convert(): RequestData { - return RequestData( - url = this.url(), - method = this.method(), - body = this.body()?.convertPretty(this.isGzipped), - headers = this.headerMap(), - timestamp = System.currentTimeMillis(), - isGzipped = this.isGzipped - ) -} - -internal fun Request.headerMap(): Map { - val headerNames = arrayListOf() - headerNames.addAll(headers().names()) - headerNames.add("content-type") -// headerNames.add("content-length") todo get content-length header - headerNames.sortBy { it } - - val map = mutableMapOf() - headerNames.forEach { - val key = it.lowercase().trim() - when (it) { - "content-type" -> body()?.contentType()?.toString()?.let { value -> - map[key] = value.trim() - } - else -> map[key] = headers().get(it)?.trim() - } - } - return map -} - -internal fun Response.convert(body: ProcessedBody?): ResponseData { - return ResponseData( - status = Status(code(), statusCodeMessage()), - isSuccessful = isSuccessful, - body = body, - protocol = protocol(), - fromDiskCache = false, - headers = headersMap(), - sendTimeMillis = sentRequestAtMillis(), - receiveTimeMillis = receivedResponseAtMillis(), - isGzipped = isGzipped - ) -} - -private fun Response.statusCodeMessage(): String { - return message() // TODO compile local message if server message is blank -} - -private fun Response.headersMap(): Map { - val headerNames = arrayListOf() - headerNames.addAll(headers().names()) - headerNames.sortBy { it } - - val map = mutableMapOf() - headerNames.forEach { - map[it.lowercase().trim()] = headers().get(it)?.trim() - } - - return map -} - -@Suppress("TooGenericExceptionCaught") -internal fun doUnZipToString(gzippedMessage: ByteArray?): String { - var unzippedMessage: String? = null - try { - val gzippped = doUnZip(gzippedMessage) - unzippedMessage = String(gzippped!!, Charset.defaultCharset()) - } catch (e: Throwable) { - DebugLog.e(LOGTAG, "doUnZipToString 1", e) - } - return unzippedMessage ?: "" -} - -private fun doUnZip(stream: InputStream?): ByteArray? { - if (stream !is ByteArrayInputStream) { - return try { - doUnZip(stream?.readBytes()) - } catch (e: IOException) { - DebugLog.e(LOGTAG, "doUnZip 1", e) - null -// throw SystemFailedException(e.getMessage(), e) - } - } - var bos: ByteArrayOutputStream? = null - var gzipStream: InputStream? = null - var bytes: ByteArray? = null - try { - bos = ByteArrayOutputStream() - gzipStream = GZIPInputStream(stream) - copy(gzipStream, bos) - bytes = bos.toByteArray() - } catch (e: IOException) { - DebugLog.e(LOGTAG, "error while unzip", e) - } finally { - try { - gzipStream?.close() - bos?.close() - } catch (e: IOException) { - DebugLog.e(LOGTAG, "error while closing stream", e) - } - } - return bytes -} - -private fun doUnZip(zippedMessage: ByteArray?): ByteArray? { - var stream: ByteArrayInputStream? = null - return try { - stream = ByteArrayInputStream(zippedMessage) - doUnZip(stream) - } finally { - try { - stream?.close() - } catch (e: IOException) { - DebugLog.e(LOGTAG, "error while closing zippedMessage stream", e) - } - } -} - -private const val BUFFER_SIZE = 1024 - -@Throws(IOException::class) -private fun copy(stream: InputStream, out: OutputStream) { - val buf = ByteArray(BUFFER_SIZE) - var len: Int - while (stream.read(buf, 0, buf.size).also { len = it } != -1) { - out.write(buf, 0, len) - } -} - -private const val LOGTAG = "data-convertor" diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/network/interceptor/DepletingSource.kt b/pluto/src/main/java/com/mocklets/pluto/modules/network/interceptor/DepletingSource.kt deleted file mode 100644 index 869e4097..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/network/interceptor/DepletingSource.kt +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Source: chucker - https://github.com/ChuckerTeam/chucker.git - */ -package com.mocklets.pluto.modules.network.interceptor - -import java.io.IOException -import okio.Buffer -import okio.ForwardingSource -import okio.Source -import okio.blackholeSink -import okio.buffer - -internal class DepletingSource(delegate: Source) : ForwardingSource(delegate) { - private var shouldDeplete = true - - override fun read(sink: Buffer, byteCount: Long) = try { - val bytesRead = super.read(sink, byteCount) - if (bytesRead == -1L) shouldDeplete = false - bytesRead - } catch (e: IOException) { - shouldDeplete = false - throw e - } - - override fun close() { - if (shouldDeplete) { - try { - delegate.buffer().readAll(blackholeSink()) - } catch (e: IOException) { - IOException("error occurred while depleting the source", e).printStackTrace() - } - } - shouldDeplete = false - - super.close() - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/network/interceptor/FileFactory.kt b/pluto/src/main/java/com/mocklets/pluto/modules/network/interceptor/FileFactory.kt deleted file mode 100644 index a7ccedd4..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/network/interceptor/FileFactory.kt +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Source: chucker - https://github.com/ChuckerTeam/chucker/blob/develop/library/src/main/java/com/chuckerteam/chucker/internal/support/FileFactory.kt - * License: https://github.com/ChuckerTeam/chucker/blob/develop/LICENSE.txt - */ -package com.mocklets.pluto.modules.network.interceptor - -import java.io.File -import java.io.IOException -import java.util.concurrent.atomic.AtomicLong - -internal object FileFactory { - private val uniqueIdGenerator = AtomicLong() - - fun create(directory: File) = create(directory, fileName = "pluto-${uniqueIdGenerator.getAndIncrement()}") - - private fun create(directory: File, fileName: String): File? = try { - File(directory, fileName).apply { - if (exists() && !delete()) { - throw IOException("Failed to delete file $this") - } - parentFile?.mkdirs() - if (!createNewFile()) { - throw IOException("File $this already exists") - } - } - } catch (e: IOException) { - IOException("An error occurred while creating a Pluto file", e).printStackTrace() - null - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/network/interceptor/OkHttpKtx.kt b/pluto/src/main/java/com/mocklets/pluto/modules/network/interceptor/OkHttpKtx.kt deleted file mode 100644 index 31eb7403..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/network/interceptor/OkHttpKtx.kt +++ /dev/null @@ -1,61 +0,0 @@ -package com.mocklets.pluto.modules.network.interceptor - -import java.net.HttpURLConnection -import okhttp3.Headers -import okhttp3.Request -import okhttp3.Response - -private const val HTTP_CONTINUE = 100 - -/** Returns true if the response must have a (possibly 0-length) body. See RFC 7231. */ -internal fun Response.hasBody(): Boolean { - // HEAD requests never yield a body regardless of the response headers. - if (request().method() == "HEAD") { - return false - } - - val responseCode = code() - val isSuccessResponse = responseCode < HTTP_CONTINUE || responseCode >= HttpURLConnection.HTTP_OK - if (isSuccessResponse && - responseCode != HttpURLConnection.HTTP_NO_CONTENT && - responseCode != HttpURLConnection.HTTP_NOT_MODIFIED - ) { - return true - } - - // If the Content-Length or Transfer-Encoding headers disagree with the response code, the - // response is malformed. For best compatibility, we honor the headers. - return contentLength > 0 || isChunked -} - -internal val Response.contentLength: Long - get() { - return this.header("Content-Length")?.toLongOrNull() ?: -1L - } - -internal val Response.isChunked: Boolean - get() { - return this.header("Transfer-Encoding").equals("chunked", ignoreCase = true) - } - -internal val Response.contentType: String? - get() { - return this.header("Content-Type") - } - -/** Checks if the OkHttp response uses gzip encoding. */ -internal val Response.isGzipped: Boolean - get() { - return this.headers().containsGzip - } - -/** Checks if the OkHttp request uses gzip encoding. */ -internal val Request.isGzipped: Boolean - get() { - return this.headers().containsGzip - } - -private val Headers.containsGzip: Boolean - get() { - return this["Content-Encoding"].equals("gzip", ignoreCase = true) - } diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/network/interceptor/ReportingSink.kt b/pluto/src/main/java/com/mocklets/pluto/modules/network/interceptor/ReportingSink.kt deleted file mode 100644 index 09ca59ba..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/network/interceptor/ReportingSink.kt +++ /dev/null @@ -1,106 +0,0 @@ -/** - * Source: chucker - https://github.com/ChuckerTeam/chucker/blob/develop/library/src/main/java/com/chuckerteam/chucker/internal/support/ReportingSink.kt - * License: https://github.com/ChuckerTeam/chucker/blob/develop/LICENSE.txt - */ -package com.mocklets.pluto.modules.network.interceptor - -import java.io.File -import java.io.IOException -import okio.Buffer -import okio.Sink -import okio.Timeout -import okio.sink - -/** - * A sink that reports result of writing to it via [callback]. - * - * Takes an input [downstreamFile] and writes bytes from a source into this input. Amount of bytes - * to copy can be limited with [writeByteLimit]. Results are reported back to a client - * when sink is closed or when an exception occurs while creating a downstream sink or while - * writing bytes. - */ -internal class ReportingSink( - private val downstreamFile: File?, - private val callback: Callback, - private val writeByteLimit: Long = Long.MAX_VALUE -) : Sink { - private var totalByteCount = 0L - private var isFailure = false - private var isClosed = false - private var downstream = try { - downstreamFile?.sink() - } catch (e: IOException) { - callDownstreamFailure(IOException("Failed to use file $downstreamFile by Pluto", e)) - null - } - - override fun write(source: Buffer, byteCount: Long) { - val previousTotalByteCount = totalByteCount - totalByteCount += byteCount - if (isFailure || previousTotalByteCount >= writeByteLimit) return - - val bytesToWrite = if (previousTotalByteCount + byteCount <= writeByteLimit) { - byteCount - } else { - writeByteLimit - previousTotalByteCount - } - - if (bytesToWrite == 0L) return - - try { - downstream?.write(source, bytesToWrite) - } catch (e: IOException) { - callDownstreamFailure(e) - } - } - - override fun flush() { - if (isFailure) return - try { - downstream?.flush() - } catch (e: IOException) { - callDownstreamFailure(e) - } - } - - override fun close() { - if (isClosed) return - isClosed = true - safeCloseDownstream() - callback.onSuccess(downstreamFile, totalByteCount) - } - - override fun timeout(): Timeout = downstream?.timeout() ?: Timeout.NONE - - private fun callDownstreamFailure(exception: IOException) { - if (!isFailure) { - isFailure = true - safeCloseDownstream() - callback.onFailure(downstreamFile, exception) - } - } - - private fun safeCloseDownstream() = try { - downstream?.close() - } catch (e: IOException) { - callDownstreamFailure(e) - } - - interface Callback { - /** - * Called when the sink is closed. All written bytes are copied to the [file]. - * This does not mean that the content of the [file] is valid. Only that the client - * is done with the writing process. - * - * [sourceByteCount] is the exact amount of bytes that the were read from upstream even if - * the [file] is corrupted or does not exist. It is not limited by [writeByteLimit]. - */ - fun onSuccess(file: File?, sourceByteCount: Long) - - /** - * Called when an [exception] was thrown while processing data. - * Any written bytes are available in a [file]. - */ - fun onFailure(file: File?, exception: IOException) - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/network/interceptor/ResponseBodyProcessor.kt b/pluto/src/main/java/com/mocklets/pluto/modules/network/interceptor/ResponseBodyProcessor.kt deleted file mode 100644 index d34e68cb..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/network/interceptor/ResponseBodyProcessor.kt +++ /dev/null @@ -1,106 +0,0 @@ -package com.mocklets.pluto.modules.network.interceptor - -import android.content.Context -import com.mocklets.pluto.modules.network.ApiCallData -import com.mocklets.pluto.modules.network.NetworkCallsRepo -import com.mocklets.pluto.modules.network.convertPretty -import java.io.File -import java.io.IOException -import okhttp3.MediaType -import okhttp3.Response -import okhttp3.ResponseBody -import okio.Buffer -import okio.BufferedSource -import okio.GzipSource -import okio.Source -import okio.buffer -import okio.source - -internal class ResponseBodyProcessor(context: Context) { - private val maxContentLength = MAX_CONTENT_LENGTH - private val cacheDirectoryProvider = CacheDirectoryProvider { context.filesDir } - - fun processBody(response: Response, apiCallData: ApiCallData): Response { - apiCallData.response = response.convert(null) - NetworkCallsRepo.set(apiCallData) - val responseBody = response.body() - if (!response.hasBody() || responseBody == null) { - return response - } - - val contentType = responseBody.contentType() - val contentLength = responseBody.contentLength() - - apiCallData.hasResponseBody = true - NetworkCallsRepo.set(apiCallData) - - val sideStream = ReportingSink( - createTempTransactionFile(), - ApiCallReportingSinkCallback(response, apiCallData), - maxContentLength - ) - var upstream: Source = TeeSource(responseBody.source(), sideStream) - upstream = DepletingSource(upstream) - - return response.newBuilder() - .body(upstream.buffer().asResponseBody(contentType, contentLength)) - .build() - } - - private fun createTempTransactionFile(): File? { - val cache = cacheDirectoryProvider.provide() - return if (cache == null) { - IOException("Failed to obtain a valid cache directory for Pluto transaction file").printStackTrace() - null - } else { - FileFactory.create(cache) - } - } - - private inner class ApiCallReportingSinkCallback( - private val response: Response, - private val apiCallData: ApiCallData - ) : ReportingSink.Callback { - - override fun onSuccess(file: File?, sourceByteCount: Long) { - file?.let { f -> - readResponseBuffer(f, response.isGzipped)?.let { - val responseBody = response.body() ?: return - val body = responseBody.convertPretty(it) - apiCallData.response = response.convert(body) - NetworkCallsRepo.set(apiCallData) - } - f.delete() - } - } - - override fun onFailure(file: File?, exception: IOException) = exception.printStackTrace() - - private fun readResponseBuffer(responseBody: File, isGzipped: Boolean) = try { - val bufferedSource = responseBody.source().buffer() - val source = if (isGzipped) { - GzipSource(bufferedSource) - } else { - bufferedSource - } - Buffer().apply { source.use { writeAll(it) } } - } catch (e: IOException) { - IOException("Response payload couldn't be processed by Pluto", e).printStackTrace() - null - } - } - - companion object { - private const val MAX_CONTENT_LENGTH = 300_000L -// private const val MAX_BLOB_SIZE = 1_000_000L - } -} - -/** Returns a new response body that transmits this source. */ -fun BufferedSource.asResponseBody(contentType: MediaType? = null, contentLength: Long = -1L) = object : ResponseBody() { - override fun contentType() = contentType - - override fun contentLength() = contentLength - - override fun source() = this@asResponseBody -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/network/interceptor/TeeSource.kt b/pluto/src/main/java/com/mocklets/pluto/modules/network/interceptor/TeeSource.kt deleted file mode 100644 index 48a69c70..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/network/interceptor/TeeSource.kt +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Source: chucker - https://github.com/ChuckerTeam/chucker/blob/develop/library/src/main/java/com/chuckerteam/chucker/internal/support/TeeSource.kt - * License: https://github.com/ChuckerTeam/chucker/blob/develop/LICENSE.txt - */ -package com.mocklets.pluto.modules.network.interceptor - -import java.io.IOException -import okio.Buffer -import okio.Sink -import okio.Source -import okio.Timeout - -/** - * A source that acts as a tee operator - https://en.wikipedia.org/wiki/Tee_(command). - * - * It takes the input [upstream] and reads from it serving the bytes to the end consumer - * like a regular [Source]. While bytes are read from the [upstream] the are also copied - * to a [sideStream]. - */ -internal class TeeSource(private val upstream: Source, private val sideStream: Sink) : Source { - private val tempBuffer = Buffer() - private var isFailure = false - - override fun read(sink: Buffer, byteCount: Long): Long { - val bytesRead = upstream.read(sink, byteCount) - - if (bytesRead == -1L) { - safeCloseSideStream() - return -1L - } - - if (!isFailure) { - copyBytesToSideStream(sink, bytesRead) - } - - return bytesRead - } - - private fun copyBytesToSideStream(sink: Buffer, bytesRead: Long) { - val offset = sink.size - bytesRead - sink.copyTo(tempBuffer, offset, bytesRead) - try { - sideStream.write(tempBuffer, bytesRead) - } catch (_: IOException) { - isFailure = true - safeCloseSideStream() - } - } - - override fun close() { - safeCloseSideStream() - upstream.close() - } - - private fun safeCloseSideStream() = try { - sideStream.close() - } catch (_: IOException) { - isFailure = true - } - - override fun timeout(): Timeout = upstream.timeout() -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/network/proxy/NetworkProxyRepo.kt b/pluto/src/main/java/com/mocklets/pluto/modules/network/proxy/NetworkProxyRepo.kt deleted file mode 100644 index 6b118de1..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/network/proxy/NetworkProxyRepo.kt +++ /dev/null @@ -1,77 +0,0 @@ -package com.mocklets.pluto.modules.network.proxy - -import android.content.Context -import com.mocklets.pluto.core.database.DatabaseManager -import com.mocklets.pluto.modules.network.proxy.dao.NetworkProxyDao -import com.mocklets.pluto.modules.network.proxy.dao.NetworkProxyEntity -import com.mocklets.pluto.modules.network.proxy.dao.ProxyData -import com.mocklets.pluto.modules.network.pruneQueryParams -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch -import okhttp3.HttpUrl - -internal object NetworkProxyRepo { - private var networkProxyDao: NetworkProxyDao? = null - private var proxyMap = linkedSetOf() - - fun init(context: Context) { - networkProxyDao = DatabaseManager(context).db.networkProxyDao() - GlobalScope.launch { - populateMap() - } - } - - private suspend fun populateMap() { - networkProxyDao?.fetchList()?.let { - proxyMap.clear() - proxyMap.addAll(it) - } - } - - fun get(url: HttpUrl, method: String): String? { - val proxyData = - proxyMap.singleOrNull { p -> - p.requestUrl == url.toString().pruneQueryParams() && p.requestMethod == method - }?.proxyData - return proxyData?.url // todo url before returning, like wildcards, response_status & delay - } - - fun fetch(url: String, method: String): NetworkProxyEntity? { - return proxyMap.singleOrNull { p -> p.requestUrl == url && p.requestMethod == method } - } - - fun fetchList(search: String = ""): List { - return if (search.trim().isEmpty()) { - proxyMap.toList() - } else { - proxyMap.filter { p -> p.requestUrl.contains(search.trim()) } - } - } - - suspend fun update(requestUrl: String, requestMethod: String, proxyData: ProxyData) { - val data = NetworkProxyEntity( - requestUrl = requestUrl, - requestMethod = requestMethod, - proxyData = proxyData, - timestamp = System.currentTimeMillis() - ) - networkProxyDao?.let { - it.save(data) - populateMap() - } - } - - suspend fun delete(url: String) { - networkProxyDao?.let { - it.delete(url) - populateMap() - } - } - - suspend fun deleteAll() { - networkProxyDao?.let { - it.deleteAll() - populateMap() - } - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/network/proxy/NetworkProxyViewModel.kt b/pluto/src/main/java/com/mocklets/pluto/modules/network/proxy/NetworkProxyViewModel.kt deleted file mode 100644 index fc9d2a17..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/network/proxy/NetworkProxyViewModel.kt +++ /dev/null @@ -1,74 +0,0 @@ -package com.mocklets.pluto.modules.network.proxy - -import android.app.Application -import android.webkit.URLUtil -import androidx.lifecycle.AndroidViewModel -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.viewModelScope -import com.mocklets.pluto.core.SingleLiveEvent -import com.mocklets.pluto.modules.network.proxy.dao.NetworkProxyEntity -import com.mocklets.pluto.modules.network.proxy.dao.ProxyData -import com.mocklets.pluto.modules.network.pruneQueryParams -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch - -internal class NetworkProxyViewModel(application: Application) : AndroidViewModel(application) { - - val proxyList: LiveData> - get() = _proxyList - private val _proxyList = MutableLiveData>() - - val currentProxy: SingleLiveEvent - get() = _currentProxy - private val _currentProxy = SingleLiveEvent() - - val event: SingleLiveEvent> - get() = _event - private val _event = SingleLiveEvent>() - - val mockletsUrl: SingleLiveEvent - get() = _mockletsUrl - private val _mockletsUrl = SingleLiveEvent() - - fun fetchList(search: String = "") { - viewModelScope.launch(Dispatchers.IO) { - val list = NetworkProxyRepo.fetchList(search) - _proxyList.postValue(list) - } - } - - fun update(requestUrl: String, requestMethod: String, proxyData: ProxyData) { - if (!URLUtil.isHttpsUrl(proxyData.url)) { - _event.postValue(Pair(false, "Need https:// URL")) - return - } - if (proxyData.url.length < URL_MIN_LENGTH) { // length of https:// - _event.postValue(Pair(false, "Malformed URL")) - return - } - viewModelScope.launch(Dispatchers.IO) { - NetworkProxyRepo.update(requestUrl.pruneQueryParams(), requestMethod, proxyData) - _event.postValue(Pair(true, "Proxy Setting updated!")) - } - } - - fun fetch(url: String, method: String) { - _currentProxy.postValue(NetworkProxyRepo.fetch(url.pruneQueryParams(), method)) - } - - fun delete(url: String) { - viewModelScope.launch(Dispatchers.IO) { - NetworkProxyRepo.delete(url) - _event.postValue(Pair(true, "Proxy Setting deleted!")) - } - } - - fun onInAppBrowserClose() { - _mockletsUrl.postValue("mock") - } - - private companion object { - const val URL_MIN_LENGTH = 9 - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/network/proxy/dao/NetworkProxyConverters.kt b/pluto/src/main/java/com/mocklets/pluto/modules/network/proxy/dao/NetworkProxyConverters.kt deleted file mode 100644 index 13c14c8e..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/network/proxy/dao/NetworkProxyConverters.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.mocklets.pluto.modules.network.proxy.dao - -import androidx.room.TypeConverter -import com.google.gson.Gson - -internal class NetworkProxyConverters { - - private val gson = Gson() - - @TypeConverter - fun stringToProxyData(data: String?): ProxyData { - return gson.fromJson(data, ProxyData::class.java) - } - - @TypeConverter - fun proxyDataToString(data: ProxyData): String? { - return gson.toJson(data) - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/network/proxy/dao/NetworkProxyDao.kt b/pluto/src/main/java/com/mocklets/pluto/modules/network/proxy/dao/NetworkProxyDao.kt deleted file mode 100644 index 5f0815d6..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/network/proxy/dao/NetworkProxyDao.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.mocklets.pluto.modules.network.proxy.dao - -import androidx.room.Dao -import androidx.room.Insert -import androidx.room.OnConflictStrategy -import androidx.room.Query - -@Dao -internal interface NetworkProxyDao { - - @Insert(onConflict = OnConflictStrategy.REPLACE) - suspend fun save(entity: NetworkProxyEntity) - - @Query("SELECT * FROM network_proxy where request_url is :requestUrl") - suspend fun fetch(requestUrl: String): NetworkProxyEntity? - - @Query("SELECT * FROM network_proxy where request_url like '%' || :search || '%' order by timestamp DESC") - suspend fun fetchList(search: String = ""): List? - - @Query("DELETE FROM network_proxy where request_url is :requestUrl") - suspend fun delete(requestUrl: String) - - @Query("DELETE FROM network_proxy") - suspend fun deleteAll() -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/network/proxy/dao/NetworkProxyEntity.kt b/pluto/src/main/java/com/mocklets/pluto/modules/network/proxy/dao/NetworkProxyEntity.kt deleted file mode 100644 index 3322b15c..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/network/proxy/dao/NetworkProxyEntity.kt +++ /dev/null @@ -1,41 +0,0 @@ -package com.mocklets.pluto.modules.network.proxy.dao - -import androidx.annotation.Keep -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.Index -import androidx.room.PrimaryKey -import androidx.room.TypeConverters -import com.mocklets.pluto.core.ui.list.ListItem - -@Keep -@TypeConverters(NetworkProxyConverters::class) -@Entity(tableName = "network_proxy", indices = [Index(value = ["request_url"], unique = true)]) -internal data class NetworkProxyEntity( - @PrimaryKey(autoGenerate = true) - @ColumnInfo(name = "id") - val id: Int? = null, - @ColumnInfo(name = "timestamp") - val timestamp: Long, - @ColumnInfo(name = "request_url") - val requestUrl: String, - @ColumnInfo(name = "request_method") - val requestMethod: String, - @ColumnInfo(name = "proxy_data") - val proxyData: ProxyData -) : ListItem() { - override fun isSame(other: Any): Boolean { - return other is NetworkProxyEntity && other.requestUrl == requestUrl - } - - override fun equals(other: Any?): Boolean { - return other is NetworkProxyEntity && other.requestUrl == requestUrl - } -} - -@Keep -internal data class ProxyData( - val url: String, - val statusCode: Int? = null, - val delay: Int? = null -) diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/network/proxy/ui/NetworkProxyItemAdapter.kt b/pluto/src/main/java/com/mocklets/pluto/modules/network/proxy/ui/NetworkProxyItemAdapter.kt deleted file mode 100644 index 68fabc10..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/network/proxy/ui/NetworkProxyItemAdapter.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.mocklets.pluto.modules.network.proxy.ui - -import android.view.ViewGroup -import com.mocklets.pluto.core.ui.list.BaseAdapter -import com.mocklets.pluto.core.ui.list.DiffAwareHolder -import com.mocklets.pluto.core.ui.list.ListItem -import com.mocklets.pluto.modules.network.proxy.dao.NetworkProxyEntity - -internal class NetworkProxyItemAdapter(private val listener: OnActionListener) : BaseAdapter() { - override fun getItemViewType(item: ListItem): Int? { - return when (item) { - is NetworkProxyEntity -> ITEM_TYPE_NETWORK_PROXY - else -> null - } - } - - override fun onViewHolderCreated(parent: ViewGroup, viewType: Int): DiffAwareHolder? { - return when (viewType) { - ITEM_TYPE_NETWORK_PROXY -> NetworkProxyItemHolder(parent, listener) - else -> null - } - } - - companion object { - const val ITEM_TYPE_NETWORK_PROXY = 1000 - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/network/proxy/ui/NetworkProxyItemHolder.kt b/pluto/src/main/java/com/mocklets/pluto/modules/network/proxy/ui/NetworkProxyItemHolder.kt deleted file mode 100644 index 24d09a1a..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/network/proxy/ui/NetworkProxyItemHolder.kt +++ /dev/null @@ -1,29 +0,0 @@ -package com.mocklets.pluto.modules.network.proxy.ui - -import android.view.ViewGroup -import com.mocklets.pluto.R -import com.mocklets.pluto.core.extensions.inflate -import com.mocklets.pluto.core.ui.list.DiffAwareAdapter -import com.mocklets.pluto.core.ui.list.DiffAwareHolder -import com.mocklets.pluto.core.ui.list.ListItem -import com.mocklets.pluto.core.ui.setDebounceClickListener -import com.mocklets.pluto.databinding.PlutoItemNetworkProxySettingsBinding -import com.mocklets.pluto.modules.network.proxy.dao.NetworkProxyEntity - -internal class NetworkProxyItemHolder( - parent: ViewGroup, - listener: DiffAwareAdapter.OnActionListener -) : DiffAwareHolder(parent.inflate(R.layout.pluto___item_network_proxy_settings), listener) { - - private val binding = PlutoItemNetworkProxySettingsBinding.bind(itemView) - private val value = binding.value - - override fun onBind(item: ListItem) { - if (item is NetworkProxyEntity) { - value.text = item.requestUrl - binding.root.setDebounceClickListener { - onAction("click") - } - } - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/network/proxy/ui/NetworkProxySettingsFragment.kt b/pluto/src/main/java/com/mocklets/pluto/modules/network/proxy/ui/NetworkProxySettingsFragment.kt deleted file mode 100644 index d415f7bc..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/network/proxy/ui/NetworkProxySettingsFragment.kt +++ /dev/null @@ -1,162 +0,0 @@ -package com.mocklets.pluto.modules.network.proxy.ui - -import android.content.ClipboardManager -import android.content.Context -import android.net.Uri -import android.os.Bundle -import android.os.Parcelable -import android.view.View -import android.view.View.GONE -import android.view.View.VISIBLE -import androidx.fragment.app.Fragment -import androidx.fragment.app.activityViewModels -import androidx.lifecycle.Observer -import androidx.lifecycle.lifecycleScope -import com.mocklets.pluto.R -import com.mocklets.pluto.core.binding.viewBinding -import com.mocklets.pluto.core.extensions.color -import com.mocklets.pluto.core.extensions.customTab -import com.mocklets.pluto.core.extensions.delayedLaunchWhenResumed -import com.mocklets.pluto.core.extensions.fadeInAndOut -import com.mocklets.pluto.core.extensions.hideKeyboard -import com.mocklets.pluto.core.extensions.lazyParcelExtra -import com.mocklets.pluto.core.extensions.toast -import com.mocklets.pluto.core.ui.routing.Screens -import com.mocklets.pluto.core.ui.routing.lazyRouter -import com.mocklets.pluto.core.ui.setDebounceClickListener -import com.mocklets.pluto.core.ui.spannable.setSpan -import com.mocklets.pluto.databinding.PlutoFragmentNetworkProxySettingsBinding -import com.mocklets.pluto.modules.network.proxy.NetworkProxyViewModel -import com.mocklets.pluto.modules.network.proxy.dao.NetworkProxyEntity -import com.mocklets.pluto.modules.network.proxy.dao.ProxyData -import com.mocklets.pluto.modules.network.pruneQueryParams -import kotlinx.parcelize.Parcelize - -internal class NetworkProxySettingsFragment : Fragment(R.layout.pluto___fragment_network_proxy_settings) { - - private val binding by viewBinding(PlutoFragmentNetworkProxySettingsBinding::bind) - private val arguments by lazyParcelExtra() - private val viewModel: NetworkProxyViewModel by activityViewModels() - private val router by lazyRouter() - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - viewModel.currentProxy.removeObservers(viewLifecycleOwner) - viewModel.currentProxy.observe(viewLifecycleOwner, apiCallObserver) - arguments?.let { viewModel.fetch(it.url, it.method) } - arguments?.showListCta?.let { - if (!it) { - binding.seeAll.visibility = GONE - binding.divider.visibility = GONE - } - } - binding.requestLabel.setSpan { - append(getString(R.string.pluto___network_proxy_settings_request_label)) - arguments?.let { - append(bold(fontColor(" (", context.color(R.color.pluto___text_dark_60)))) - append(bold(fontColor(it.method, context.color(R.color.pluto___text_dark)))) - append(bold(fontColor(")", context.color(R.color.pluto___text_dark_60)))) - } - } - - binding.seeAll.setDebounceClickListener { - requireActivity().hideKeyboard() - activity?.onBackPressed() - router.navigate(Screens.NetworkProxySettingsList) - } - binding.close.setDebounceClickListener { - requireActivity().hideKeyboard() - activity?.onBackPressed() - } - binding.save.setDebounceClickListener { - arguments?.let { - viewModel.update( - binding.endPoint.text.toString(), - it.method, - ProxyData(url = binding.proxyUrl.text.toString()) - ) - } - requireActivity().hideKeyboard() - } - binding.delete.setDebounceClickListener { - requireActivity().hideKeyboard() - viewModel.delete(binding.endPoint.text.toString()) - } - binding.accessMocklets.setDebounceClickListener { - val uri = Uri.parse(MOCKLETS_URL) - .buildUpon() - .appendQueryParameter(METHOD_PARAM, arguments?.method?.lowercase() ?: "any") - .build() - requireActivity().customTab(uri) - } - - viewModel.event.removeObservers(viewLifecycleOwner) - viewModel.event.observe(viewLifecycleOwner, eventsObserver) - - viewModel.mockletsUrl.removeObservers(viewLifecycleOwner) - viewModel.mockletsUrl.observe(viewLifecycleOwner, mockletsUrlObserver) - } - - private val eventsObserver = Observer> { - context?.toast(it.second) - if (it.first) { - activity?.onBackPressed() - } - } - - private val mockletsUrlObserver = Observer { - binding.proxyUrl.requestFocus() - lifecycleScope.delayedLaunchWhenResumed(CLIPBOARD_PROCESS_DELAY) { - context?.clipboardData()?.let { - if (isSelectionValid(it)) { - binding.proxyUrl.setText(it) - binding.mockletsSuccess.fadeInAndOut(lifecycleScope) - } - } - binding.proxyUrl.clearFocus() - } - } - - private fun isSelectionValid(it: String): Boolean = - it.startsWith(MOCKLETS_API_PREFIX) && binding.proxyUrl.text.toString() != it - - private val apiCallObserver = Observer { - binding.endPoint.setText(arguments?.url?.pruneQueryParams()) - binding.delete.visibility = GONE - binding.divider.visibility = GONE - it?.let { - setupUpdateUI(it) - } - binding.proxyUrl.setSelection(binding.proxyUrl.text.toString().length) - } - - private fun setupUpdateUI(entity: NetworkProxyEntity) { - binding.endPoint.isEnabled = false - binding.delete.visibility = VISIBLE - binding.note.visibility = VISIBLE - arguments?.showListCta?.let { - if (it) { - binding.divider.visibility = VISIBLE - } - } - context?.let { binding.endPoint.setTextColor(it.color(R.color.pluto___text_dark_40)) } - binding.endPoint.setText(entity.requestUrl) - binding.proxyUrl.setText(entity.proxyData.url) - } - - private fun Context.clipboardData(): String? { - val clipBoardManager = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager - return clipBoardManager.primaryClip?.getItemAt(0)?.text?.toString() - } - - @Parcelize - data class Data(val url: String, val method: String, val showListCta: Boolean = false) : Parcelable - - companion object { - const val IN_APP_BROWSER_RESULT_CODE = 10_001 - const val MOCKLETS_URL = "https://connect.mocklets.com?ref=pluto" - const val MOCKLETS_API_PREFIX = "https://api.mocklets.com/" - const val METHOD_PARAM = "method" - const val CLIPBOARD_PROCESS_DELAY = 50L - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/network/proxy/ui/NetworkProxySettingsListFragment.kt b/pluto/src/main/java/com/mocklets/pluto/modules/network/proxy/ui/NetworkProxySettingsListFragment.kt deleted file mode 100644 index cb7e5b1b..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/network/proxy/ui/NetworkProxySettingsListFragment.kt +++ /dev/null @@ -1,119 +0,0 @@ -package com.mocklets.pluto.modules.network.proxy.ui - -import android.os.Bundle -import android.view.View -import android.view.View.GONE -import android.view.View.VISIBLE -import androidx.core.view.isVisible -import androidx.core.widget.addTextChangedListener -import androidx.fragment.app.Fragment -import androidx.fragment.app.activityViewModels -import androidx.lifecycle.Observer -import androidx.lifecycle.lifecycleScope -import com.mocklets.pluto.R -import com.mocklets.pluto.core.binding.viewBinding -import com.mocklets.pluto.core.extensions.dp -import com.mocklets.pluto.core.extensions.hideKeyboard -import com.mocklets.pluto.core.extensions.showKeyboard -import com.mocklets.pluto.core.ui.list.BaseAdapter -import com.mocklets.pluto.core.ui.list.CustomItemDecorator -import com.mocklets.pluto.core.ui.list.DiffAwareAdapter -import com.mocklets.pluto.core.ui.list.DiffAwareHolder -import com.mocklets.pluto.core.ui.list.ListItem -import com.mocklets.pluto.core.ui.routing.OnBackKeyHandler -import com.mocklets.pluto.core.ui.routing.Screens -import com.mocklets.pluto.core.ui.routing.lazyRouter -import com.mocklets.pluto.core.ui.setDebounceClickListener -import com.mocklets.pluto.databinding.PlutoFragmentNetworkProxySettingsListBinding -import com.mocklets.pluto.modules.network.proxy.NetworkProxyViewModel -import com.mocklets.pluto.modules.network.proxy.dao.NetworkProxyEntity - -internal class NetworkProxySettingsListFragment : - Fragment(R.layout.pluto___fragment_network_proxy_settings_list), - OnBackKeyHandler { - - private val binding by viewBinding(PlutoFragmentNetworkProxySettingsListBinding::bind) - private val networkProxyAdapter: BaseAdapter by lazy { NetworkProxyItemAdapter(onActionListener) } - private val viewModel: NetworkProxyViewModel by activityViewModels() - private val router by lazyRouter() - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - binding.list.apply { - adapter = networkProxyAdapter - addItemDecoration(CustomItemDecorator(context, DECORATOR_DIVIDER_PADDING)) - } - binding.close.setDebounceClickListener { - activity?.onBackPressed() - } - binding.search.setDebounceClickListener { - binding.searchView.visibility = VISIBLE - binding.searchView.requestFocus() - } - binding.closeSearch.setDebounceClickListener { - exitSearch() - } - binding.clearSearch.setDebounceClickListener { - binding.editSearch.text = null - } - binding.editSearch.setOnFocusChangeListener { v, hasFocus -> - if (hasFocus) { - v.showKeyboard() - } else { - v.hideKeyboard() - } - } - binding.editSearch.addTextChangedListener { editable -> - lifecycleScope.launchWhenResumed { - editable?.toString()?.let { - viewModel.fetchList(it) - } - } - } - binding.create.setDebounceClickListener { - router.navigate(Screens.NetworkProxySettings()) - } - - viewModel.proxyList.removeObserver(networkProxyObserver) - viewModel.proxyList.observe(viewLifecycleOwner, networkProxyObserver) - viewModel.fetchList() - } - - private val networkProxyObserver = Observer> { - networkProxyAdapter.list = it - binding.noNetworkProxy.visibility = if (it.isEmpty()) VISIBLE else GONE - } - - override fun onBackPressed(): Boolean { - if (binding.searchView.isVisible) { - exitSearch() - return true - } - return false - } - - private fun exitSearch() { - binding.editSearch.text = null - binding.searchView.visibility = GONE - binding.editSearch.clearFocus() - } - - private val onActionListener = object : DiffAwareAdapter.OnActionListener { - override fun onAction(action: String, data: ListItem, holder: DiffAwareHolder?) { - when (data) { - is NetworkProxyEntity -> router.navigate( - Screens.NetworkProxySettings( - NetworkProxySettingsFragment.Data( - data.requestUrl, - data.requestMethod - ) - ) - ) - } - } - } - - private companion object { - val DECORATOR_DIVIDER_PADDING = 16f.dp.toInt() - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/network/transformers/BaseTransformer.kt b/pluto/src/main/java/com/mocklets/pluto/modules/network/transformers/BaseTransformer.kt deleted file mode 100644 index 861d363a..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/network/transformers/BaseTransformer.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.mocklets.pluto.modules.network.transformers - -import com.mocklets.pluto.modules.network.BODY_INDENTATION - -internal interface BaseTransformer { - fun beautify(plain: CharSequence, indent: Int = BODY_INDENTATION): CharSequence? - fun flatten(plain: CharSequence): String -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/network/transformers/FormEncodedTransformer.kt b/pluto/src/main/java/com/mocklets/pluto/modules/network/transformers/FormEncodedTransformer.kt deleted file mode 100644 index 5a414f22..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/network/transformers/FormEncodedTransformer.kt +++ /dev/null @@ -1,48 +0,0 @@ -package com.mocklets.pluto.modules.network.transformers - -import com.mocklets.pluto.core.DebugLog -import com.mocklets.pluto.modules.network.LOGTAG -import com.mocklets.pluto.modules.network.UTF8 -import java.net.URLDecoder -import java.net.URLEncoder - -internal class FormEncodedTransformer : BaseTransformer { - - @Suppress("TooGenericExceptionCaught") - override fun beautify(plain: CharSequence, indent: Int): CharSequence? { - return try { - val items = plain.split("&") - val stringBuilder = StringBuilder() - items.forEachIndexed { index, data -> - val pair = data.split("=") - stringBuilder.append("${pair[0]} = ${URLDecoder.decode(pair[1], UTF8.toString())}") - if (index < items.size - 1) { - stringBuilder.append("\t\t&\n") - } - } - stringBuilder - } catch (e: Exception) { - DebugLog.e(LOGTAG, "error while beautifying form url encoded body", e) - plain - } - } - - @Suppress("TooGenericExceptionCaught") - override fun flatten(plain: CharSequence): String { - return try { - val items = plain.split("\t\t&\n") - val stringBuilder = StringBuilder() - items.forEachIndexed { index, data -> - val pair = data.split(" = ") - stringBuilder.append("${pair[0]}=${URLEncoder.encode(pair[1], UTF8.toString())}") - if (index < items.size - 1) { - stringBuilder.append("&") - } - } - stringBuilder.toString() - } catch (e: Exception) { - DebugLog.e(LOGTAG, "error while beautifying form url encoded body", e) - plain.toString() - } - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/network/transformers/JsonBaseTransformer.kt b/pluto/src/main/java/com/mocklets/pluto/modules/network/transformers/JsonBaseTransformer.kt deleted file mode 100644 index f4098aea..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/network/transformers/JsonBaseTransformer.kt +++ /dev/null @@ -1,33 +0,0 @@ -package com.mocklets.pluto.modules.network.transformers - -import com.google.gson.JsonElement -import com.google.gson.JsonParser -import com.google.gson.JsonSyntaxException -import com.mocklets.pluto.core.DebugLog -import com.mocklets.pluto.modules.network.LOGTAG -import org.json.JSONArray -import org.json.JSONException -import org.json.JSONObject - -internal class JsonBaseTransformer : BaseTransformer { - override fun beautify(plain: CharSequence, indent: Int): CharSequence? { - return try { - val je: JsonElement = JsonParser.parseString(plain.toString()) - when { - je.isJsonArray -> JSONArray(plain.toString()).toString(indent) - je.isJsonObject -> JSONObject(plain.toString()).toString(indent) - else -> plain - } - } catch (e: JsonSyntaxException) { - DebugLog.e(LOGTAG, "json parsing failed", e) - plain - } catch (e: JSONException) { - DebugLog.e(LOGTAG, "json parsing failed", e) - plain - } - } - - override fun flatten(plain: CharSequence): String { - return plain.toString().replace("\n", "").replace("\\s+".toRegex(), "") - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/network/transformers/XmlBaseTransformer.kt b/pluto/src/main/java/com/mocklets/pluto/modules/network/transformers/XmlBaseTransformer.kt deleted file mode 100644 index 1ae681f3..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/network/transformers/XmlBaseTransformer.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.mocklets.pluto.modules.network.transformers - -import com.mocklets.pluto.core.DebugLog -import com.mocklets.pluto.modules.network.LOGTAG -import java.io.ByteArrayInputStream -import java.io.ByteArrayOutputStream -import javax.xml.transform.OutputKeys -import javax.xml.transform.Transformer -import javax.xml.transform.sax.SAXSource -import javax.xml.transform.sax.SAXTransformerFactory -import javax.xml.transform.stream.StreamResult -import org.xml.sax.InputSource - -internal class XmlBaseTransformer : BaseTransformer { - - @Suppress("TooGenericExceptionCaught") - override fun beautify(plain: CharSequence, indent: Int): CharSequence? { - return try { - val serializer: Transformer = SAXTransformerFactory.newInstance().newTransformer() - serializer.setOutputProperty(OutputKeys.INDENT, "yes") - serializer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "$indent") - val xmlSource = SAXSource(InputSource(ByteArrayInputStream(plain.toString().toByteArray()))) - val res = StreamResult(ByteArrayOutputStream()) - serializer.transform(xmlSource, res) - String((res.outputStream as ByteArrayOutputStream).toByteArray()) - } catch (e: Exception) { - DebugLog.e(LOGTAG, "xml parsing failed", e) - plain - } - } - - override fun flatten(plain: CharSequence): String { - return plain.toString().replace("\n", "").replace("\\s+".toRegex(), "") - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/network/ui/ApiItemHolder.kt b/pluto/src/main/java/com/mocklets/pluto/modules/network/ui/ApiItemHolder.kt deleted file mode 100644 index e269ad35..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/network/ui/ApiItemHolder.kt +++ /dev/null @@ -1,113 +0,0 @@ -package com.mocklets.pluto.modules.network.ui - -import android.view.View.GONE -import android.view.View.INVISIBLE -import android.view.View.VISIBLE -import android.view.ViewGroup -import com.mocklets.pluto.R -import com.mocklets.pluto.core.extensions.asTimeElapsed -import com.mocklets.pluto.core.extensions.color -import com.mocklets.pluto.core.extensions.inflate -import com.mocklets.pluto.core.ui.list.DiffAwareAdapter -import com.mocklets.pluto.core.ui.list.DiffAwareHolder -import com.mocklets.pluto.core.ui.list.ListItem -import com.mocklets.pluto.core.ui.setDebounceClickListener -import com.mocklets.pluto.core.ui.spannable.setSpan -import com.mocklets.pluto.databinding.PlutoItemNetworkBinding -import com.mocklets.pluto.modules.network.ApiCallData -import com.mocklets.pluto.modules.network.ResponseData -import com.mocklets.pluto.modules.network.hostUrl - -internal class ApiItemHolder(parent: ViewGroup, actionListener: DiffAwareAdapter.OnActionListener) : - DiffAwareHolder(parent.inflate(R.layout.pluto___item_network), actionListener) { - - private val binding = PlutoItemNetworkBinding.bind(itemView) - private val host = binding.host - private val url = binding.url - private val progress = binding.progress - private val status = binding.status - private val error = binding.error - private val timeElapsed = binding.timeElapsed - private val proxyIndicator = binding.proxyIndicator - - override fun onBind(item: ListItem) { - if (item is ApiCallData) { - host.text = item.request.url.hostUrl() - timeElapsed.text = item.request.timestamp.asTimeElapsed() - itemView.setBackgroundColor(itemView.context.color(R.color.pluto___transparent)) - - val endPoint = StringBuilder() - item.request.url.pathSegments().forEach { segment -> - endPoint.append("/$segment") - } - url.setSpan { - append(fontColor(item.request.method.uppercase(), context.color(R.color.pluto___text_dark_60))) - append(" $endPoint") - } - progress.visibility = VISIBLE - status.visibility = INVISIBLE - error.visibility = INVISIBLE - proxyIndicator.visibility = GONE - - item.exception?.let { - handleExceptionUI() - } - - item.proxy?.let { - proxyIndicator.visibility = VISIBLE - } - - item.response?.let { - handleResponseUI(it) - } - itemView.setDebounceClickListener(DEBOUNCE_DELAY) { - onAction("click") - } - } - } - - private fun handleResponseUI(it: ResponseData) { - error.visibility = INVISIBLE - progress.visibility = INVISIBLE - status.visibility = VISIBLE - status.text = it.status.code.toString() - status.setTextColor( - itemView.context.color( - if (it.isSuccessful) { - R.color.pluto___dull_green - } else { - if (it.status.code in RESPONSE_ERROR_STATUS_RANGE) { - R.color.pluto___orange - } else { - R.color.pluto___red - } - } - ) - ) - itemView.setBackgroundColor( - itemView.context.color( - if (it.isSuccessful) { - R.color.pluto___dull_green_05 - } else { - if (it.status.code in RESPONSE_ERROR_STATUS_RANGE) { - R.color.pluto___orange_05 - } else { - R.color.pluto___red_05 - } - } - ) - ) - } - - private fun handleExceptionUI() { - error.visibility = VISIBLE - progress.visibility = INVISIBLE - status.visibility = INVISIBLE - itemView.setBackgroundColor(itemView.context.color(R.color.pluto___red_05)) - } - - private companion object { - const val DEBOUNCE_DELAY = 1_000L - val RESPONSE_ERROR_STATUS_RANGE = 400..499 - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/network/ui/NetworkAdapter.kt b/pluto/src/main/java/com/mocklets/pluto/modules/network/ui/NetworkAdapter.kt deleted file mode 100644 index 8abcdad8..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/network/ui/NetworkAdapter.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.mocklets.pluto.modules.network.ui - -import android.view.ViewGroup -import com.mocklets.pluto.core.ui.list.BaseAdapter -import com.mocklets.pluto.core.ui.list.DiffAwareHolder -import com.mocklets.pluto.core.ui.list.ListItem -import com.mocklets.pluto.modules.network.ApiCallData - -internal class NetworkAdapter(private val listener: OnActionListener) : BaseAdapter() { - override fun getItemViewType(item: ListItem): Int? { - return when (item) { - is ApiCallData -> ITEM_TYPE_API - else -> null - } - } - - override fun onViewHolderCreated(parent: ViewGroup, viewType: Int): DiffAwareHolder? { - return when (viewType) { - ITEM_TYPE_API -> ApiItemHolder(parent, listener) - else -> null - } - } - - companion object { - const val ITEM_TYPE_API = 1000 - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/network/ui/NetworkFragment.kt b/pluto/src/main/java/com/mocklets/pluto/modules/network/ui/NetworkFragment.kt deleted file mode 100644 index fb717767..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/network/ui/NetworkFragment.kt +++ /dev/null @@ -1,91 +0,0 @@ -package com.mocklets.pluto.modules.network.ui - -import android.os.Bundle -import android.view.View -import android.view.View.GONE -import android.view.View.VISIBLE -import androidx.core.widget.addTextChangedListener -import androidx.fragment.app.Fragment -import androidx.fragment.app.activityViewModels -import androidx.lifecycle.Observer -import androidx.lifecycle.lifecycleScope -import com.mocklets.pluto.Pluto -import com.mocklets.pluto.R -import com.mocklets.pluto.core.binding.viewBinding -import com.mocklets.pluto.core.extensions.hideKeyboard -import com.mocklets.pluto.core.extensions.linearLayoutManager -import com.mocklets.pluto.core.extensions.showMoreOptions -import com.mocklets.pluto.core.ui.list.BaseAdapter -import com.mocklets.pluto.core.ui.list.CustomItemDecorator -import com.mocklets.pluto.core.ui.list.DiffAwareAdapter -import com.mocklets.pluto.core.ui.list.DiffAwareHolder -import com.mocklets.pluto.core.ui.list.ListItem -import com.mocklets.pluto.core.ui.routing.Screens -import com.mocklets.pluto.core.ui.routing.lazyRouter -import com.mocklets.pluto.core.ui.setDebounceClickListener -import com.mocklets.pluto.databinding.PlutoFragmentNetworkBinding -import com.mocklets.pluto.modules.network.ApiCallData -import com.mocklets.pluto.modules.network.NetworkCallsRepo -import com.mocklets.pluto.modules.network.ui.details.NetworkCallDetailsFragment - -internal class NetworkFragment : Fragment(R.layout.pluto___fragment_network) { - - private val binding by viewBinding(PlutoFragmentNetworkBinding::bind) - private val logsAdapter: BaseAdapter by lazy { NetworkAdapter(onActionListener) } - private val viewModel: NetworkViewModel by activityViewModels() - private val router by lazyRouter() - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - binding.apiList.apply { - adapter = logsAdapter - addItemDecoration(CustomItemDecorator(requireContext())) - } - binding.search.addTextChangedListener { editable -> - lifecycleScope.launchWhenResumed { - editable?.toString()?.let { - Pluto.session.networkSearchText = it - logsAdapter.list = filteredApi(it) - if (it.isEmpty()) { - binding.apiList.linearLayoutManager()?.scrollToPositionWithOffset(0, 0) - } - } - } - } - binding.more.setDebounceClickListener { - requireContext().showMoreOptions(it, R.menu.pluto___popup_menu_network) { item -> - when (item.itemId) { - R.id.proxy_settings -> router.navigate(Screens.NetworkProxySettingsList) - R.id.clear -> NetworkCallsRepo.deleteAll() - } - } - } - binding.search.setText(Pluto.session.networkSearchText) - viewModel.apiCalls.removeObserver(networkObserver) - viewModel.apiCalls.observe(viewLifecycleOwner, networkObserver) - } - - private fun filteredApi(search: String): List { - val list = (viewModel.apiCalls.value ?: emptyList()).filter { api -> - api.request.url.toString().contains(search, true) - } - binding.noItemText.text = getString( - if (search.isNotEmpty()) R.string.pluto___no_search_result else R.string.pluto___no_api_text - ) - binding.noItemText.visibility = if (list.isEmpty()) VISIBLE else GONE - return list - } - - private val networkObserver = Observer> { - logsAdapter.list = filteredApi(binding.search.text.toString()) - } - - private val onActionListener = object : DiffAwareAdapter.OnActionListener { - override fun onAction(action: String, data: ListItem, holder: DiffAwareHolder?) { - if (data is ApiCallData) { - activity!!.hideKeyboard() - router.navigate(Screens.NetworkCallDetails(NetworkCallDetailsFragment.Data(data.id))) - } - } - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/network/ui/NetworkViewModel.kt b/pluto/src/main/java/com/mocklets/pluto/modules/network/ui/NetworkViewModel.kt deleted file mode 100644 index 5ce3075c..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/network/ui/NetworkViewModel.kt +++ /dev/null @@ -1,49 +0,0 @@ -package com.mocklets.pluto.modules.network.ui - -import androidx.lifecycle.LiveData -import androidx.lifecycle.MediatorLiveData -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel -import com.mocklets.pluto.modules.network.ApiCallData -import com.mocklets.pluto.modules.network.NetworkCallsRepo - -internal class NetworkViewModel : ViewModel() { - - val apiCalls: LiveData> - get() = NetworkCallsRepo.apiCalls - - private val _currentApiCall = MutableLiveData() - private val _contentSearch = MutableLiveData() - - val detailContentLiveData: LiveData - get() = _detailContentLiveData - private val _detailContentLiveData = MediatorLiveData() - - init { - _detailContentLiveData.addSource(_currentApiCall) { - combineData(_currentApiCall, _contentSearch) - } - _detailContentLiveData.addSource(_contentSearch) { - combineData(_currentApiCall, _contentSearch) - } - } - - private fun combineData(apiCallLD: MutableLiveData, searchLD: MutableLiveData) { - apiCallLD.value?.let { - _detailContentLiveData.postValue(DetailContentData(it, searchLD.value)) - } - } - - fun fetchCurrent(id: String) { - _currentApiCall.postValue(NetworkCallsRepo.get(id)) - } - - fun searchContent(it: String) { - _contentSearch.postValue(it.trim()) - } -} - -internal data class DetailContentData( - val api: ApiCallData, - val search: String? -) diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/network/ui/details/NetworkCallDetailsFragment.kt b/pluto/src/main/java/com/mocklets/pluto/modules/network/ui/details/NetworkCallDetailsFragment.kt deleted file mode 100644 index 06231966..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/network/ui/details/NetworkCallDetailsFragment.kt +++ /dev/null @@ -1,186 +0,0 @@ -package com.mocklets.pluto.modules.network.ui.details - -import android.os.Bundle -import android.os.Parcelable -import android.view.View -import android.view.View.GONE -import android.view.View.VISIBLE -import androidx.core.view.isVisible -import androidx.core.widget.addTextChangedListener -import androidx.fragment.app.Fragment -import androidx.fragment.app.activityViewModels -import androidx.lifecycle.Observer -import androidx.lifecycle.lifecycleScope -import androidx.viewpager2.widget.ViewPager2 -import com.google.android.material.tabs.TabLayout -import com.google.android.material.tabs.TabLayoutMediator -import com.google.gson.Gson -import com.mocklets.pluto.R -import com.mocklets.pluto.core.binding.viewBinding -import com.mocklets.pluto.core.extensions.asFormattedDate -import com.mocklets.pluto.core.extensions.color -import com.mocklets.pluto.core.extensions.hideKeyboard -import com.mocklets.pluto.core.extensions.lazyParcelExtra -import com.mocklets.pluto.core.extensions.showKeyboard -import com.mocklets.pluto.core.sharing.Shareable -import com.mocklets.pluto.core.sharing.lazyContentSharer -import com.mocklets.pluto.core.ui.routing.OnBackKeyHandler -import com.mocklets.pluto.core.ui.setDebounceClickListener -import com.mocklets.pluto.core.ui.spannable.setSpan -import com.mocklets.pluto.databinding.PlutoLayoutNetworkCallDetailsBinding -import com.mocklets.pluto.modules.network.ApiCallData -import com.mocklets.pluto.modules.network.ui.DetailContentData -import com.mocklets.pluto.modules.network.ui.NetworkViewModel -import java.util.Locale -import kotlinx.parcelize.Parcelize - -internal class NetworkCallDetailsFragment : Fragment(R.layout.pluto___layout_network_call_details), OnBackKeyHandler { - - private val binding by viewBinding(PlutoLayoutNetworkCallDetailsBinding::bind) - private val arguments by lazyParcelExtra() - private val viewModel: NetworkViewModel by activityViewModels() - private val contentSharer by lazyContentSharer() - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - if (arguments == null) { - activity?.onBackPressed() - } - viewModel.apiCalls.observe(viewLifecycleOwner, listUpdateObserver) - viewModel.detailContentLiveData.observe(viewLifecycleOwner, apiCallObserver) - setupPager() - binding.close.setDebounceClickListener { - activity?.onBackPressed() - } - binding.share.setDebounceClickListener { - viewModel.detailContentLiveData.value?.let { - contentSharer.share( - Shareable( - title = "Share Network Call details", - content = it.api.toShareText(), - fileName = "Network Call details from Pluto" - ) - ) - } - } - binding.search.setDebounceClickListener { - binding.searchView.visibility = VISIBLE - binding.searchView.requestFocus() - } - binding.closeSearch.setDebounceClickListener { - exitSearch() - } - binding.clearSearch.setDebounceClickListener { - binding.editSearch.text = null - } - binding.editSearch.setOnFocusChangeListener { v, hasFocus -> - if (hasFocus) { - v.showKeyboard() - } else { - v.hideKeyboard() - } - } - binding.editSearch.addTextChangedListener { editable -> - lifecycleScope.launchWhenResumed { - editable?.toString()?.let { - viewModel.searchContent(it) - } - } - } - } - - private fun exitSearch() { - binding.editSearch.text = null - binding.searchView.visibility = GONE - binding.editSearch.clearFocus() - } - - private fun setupPager() { - val pagerAdapter = NetworkDetailsPagerAdapter(this) - binding.viewPager.adapter = pagerAdapter - binding.viewPager.orientation = ViewPager2.ORIENTATION_HORIZONTAL - - TabLayoutMediator(binding.tabs, binding.viewPager) { tab, position -> - tab.text = context?.getString(TAB_TITLES[position]) - }.attach() - binding.tabs.tabMode = TabLayout.MODE_SCROLLABLE - binding.tabs.isInlineLabel = false - } - - private val listUpdateObserver = Observer> { - arguments?.id?.let { id -> - viewModel.fetchCurrent(id) - } - } - - private val apiCallObserver = Observer { - val endPoint = StringBuilder() - it.api.request.url.pathSegments().forEach { segment -> - endPoint.append("/$segment") - } - binding.title.setSpan { - append( - fontColor( - it.api.request.method.uppercase(Locale.getDefault()), - context.color(R.color.pluto___text_dark_60) - ) - ) - append(" $endPoint") - } - } - - @Parcelize - data class Data(val id: String) : Parcelable - - override fun onBackPressed(): Boolean { - if (binding.searchView.isVisible) { - exitSearch() - return true - } - return false - } -} - -private const val STACK_TRACE_LENGTH = 10 -@Suppress("StringLiteralDuplication") -private fun ApiCallData.toShareText(): String { - val text = StringBuilder() - text.append("${request.method.uppercase(Locale.getDefault())}, ${request.url} ") - if (response != null) { - text.append( - "\n\nRequested at : ${response!!.sendTimeMillis.asFormattedDate("MMM dd, yyyy, HH:mm:ss.SSS")}" - ) - text.append( - "\nReceived at : ${response!!.receiveTimeMillis.asFormattedDate("MMM dd, yyyy, HH:mm:ss.SSS")}" - ) - text.append("\nDelay : ${response!!.receiveTimeMillis - response!!.sendTimeMillis} ms") - text.append("\nProtocol : ${response!!.protocol.name}") - } else { - text.append( - "\n\nRequested at : ${request.timestamp.asFormattedDate("MMM dd, yyyy, HH:mm:ss.SSS")}" - ) - } - text.append("\n\n==================\n\n") - text.append("REQUEST") - text.append("\n\nHeaders : \n${Gson().toJson(request.headers)}") - text.append("\n\nBody :\n${request.body?.body}") - - response?.let { response -> - text.append("\n\n==================\n\n") - text.append("RESPONSE") - text.append("\n\nHeaders : \n${Gson().toJson(response.headers)}") - text.append("\n\nBody : \n${response.body?.body}") - } - exception?.let { exception -> - text.append("\n\n==================\n\n") - text.append("RESPONSE\n") - text.append("\n${exception.name}: ${exception.message}\n") - exception.stackTrace.take(STACK_TRACE_LENGTH).forEach { - text.append("\t at $it\n") - } - if (exception.stackTrace.size - STACK_TRACE_LENGTH > 0) { - text.append("\t + ${exception.stackTrace.size - STACK_TRACE_LENGTH} more lines") - } - } - return text.toString() -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/network/ui/details/NetworkDetailsOverviewFragment.kt b/pluto/src/main/java/com/mocklets/pluto/modules/network/ui/details/NetworkDetailsOverviewFragment.kt deleted file mode 100644 index b139656a..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/network/ui/details/NetworkDetailsOverviewFragment.kt +++ /dev/null @@ -1,241 +0,0 @@ -package com.mocklets.pluto.modules.network.ui.details - -import android.os.Bundle -import android.view.View -import android.view.View.GONE -import android.view.View.VISIBLE -import androidx.fragment.app.Fragment -import androidx.fragment.app.activityViewModels -import androidx.fragment.app.viewModels -import androidx.lifecycle.Observer -import com.mocklets.pluto.R -import com.mocklets.pluto.core.binding.viewBinding -import com.mocklets.pluto.core.extensions.asFormattedDate -import com.mocklets.pluto.core.extensions.color -import com.mocklets.pluto.core.sharing.Shareable -import com.mocklets.pluto.core.sharing.lazyContentSharer -import com.mocklets.pluto.core.ui.routing.Screens -import com.mocklets.pluto.core.ui.routing.lazyRouter -import com.mocklets.pluto.core.ui.setDebounceClickListener -import com.mocklets.pluto.core.ui.spannable.createSpan -import com.mocklets.pluto.core.ui.spannable.setSpan -import com.mocklets.pluto.databinding.PlutoFragmentNetworkDetailsOverviewBinding -import com.mocklets.pluto.modules.exceptions.ExceptionData -import com.mocklets.pluto.modules.network.ApiCallData -import com.mocklets.pluto.modules.network.ResponseData -import com.mocklets.pluto.modules.network.proxy.NetworkProxyViewModel -import com.mocklets.pluto.modules.network.proxy.dao.NetworkProxyEntity -import com.mocklets.pluto.modules.network.proxy.ui.NetworkProxySettingsFragment -import com.mocklets.pluto.modules.network.ui.DetailContentData -import com.mocklets.pluto.modules.network.ui.NetworkViewModel - -internal class NetworkDetailsOverviewFragment : Fragment(R.layout.pluto___fragment_network_details_overview) { - - private val binding by viewBinding(PlutoFragmentNetworkDetailsOverviewBinding::bind) - private val viewModel: NetworkViewModel by activityViewModels() - private val proxyViewModel: NetworkProxyViewModel by viewModels() - private val router by lazyRouter() - private val contentSharer by lazyContentSharer() - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - viewModel.detailContentLiveData.removeObservers(viewLifecycleOwner) - viewModel.detailContentLiveData.observe(viewLifecycleOwner, apiCallObserver) - - proxyViewModel.currentProxy.removeObservers(viewLifecycleOwner) - proxyViewModel.currentProxy.observe(viewLifecycleOwner, proxySettingsObserver) - - binding.proxyStub.setupProxy.setDebounceClickListener { - viewModel.detailContentLiveData.value?.api?.request?.let { request -> - router.navigate( - Screens.NetworkProxySettings( - NetworkProxySettingsFragment.Data( - request.url.toString(), - request.method, - true - ) - ) - ) - } - } - } - - private val apiCallObserver = Observer { - updateUi(it.api, it.search) - proxyViewModel.fetch(it.api.request.url.toString(), it.api.request.method) - } - - private val proxySettingsObserver = Observer { proxy -> - context?.apply { - binding.proxyStub.setupProxy.text = - getString(if (proxy != null) R.string.pluto___update_api_proxy else R.string.pluto___setup_api_proxy) - } - } - - private fun updateUi(data: ApiCallData, search: String?) { - setupStatusView(data) - binding.url.setSpan { append(highlight(data.request.url.toString(), search)) } - setupProxyState(data) - binding.method.setSpan { append(highlight(data.request.method, search)) } - binding.ssl.setSpan { append(highlight(data.request.url.isHttps.toString(), search)) } - binding.requestTime.setSpan { - append( - highlight( - semiBold( - fontColor( - data.request.timestamp.asFormattedDate(DATE_FORMAT), - context.color(R.color.pluto___text_dark_80) - ) - ), - search - ) - ) - } - binding.proxyStub.copyCurl.setDebounceClickListener { - contentSharer.share(Shareable(title = "Share Request cURL", content = data.curl, fileName = "cURL Request from Pluto")) - } - - data.exception?.let { - handleExceptionUI(it, data, search) - } - - data.response?.let { - handleResponseUI(it, search) - } - } - - private fun handleResponseUI(it: ResponseData, search: String?) { - binding.protocol.setSpan { - val protocolSpan = context.createSpan { - append(semiBold(fontColor("${it.protocol}", context.color(R.color.pluto___text_dark_80)))) - append(regular(fontColor(" (${it.protocol.name})", context.color(R.color.pluto___text_dark_60)))) - } - append(highlight(protocolSpan, search)) - } - binding.requestTime.setSpan { - append( - highlight( - semiBold( - fontColor( - it.sendTimeMillis.asFormattedDate(DATE_FORMAT), - context.color(R.color.pluto___text_dark_80) - ) - ), - search - ) - ) - } - binding.responseTime.setSpan { - append( - highlight( - semiBold( - fontColor( - it.receiveTimeMillis.asFormattedDate(DATE_FORMAT), - context.color(R.color.pluto___text_dark_80) - ) - ), - search - ) - ) - } - binding.delay.setSpan { - append( - highlight( - semiBold( - fontColor( - "${it.receiveTimeMillis - it.sendTimeMillis} ms", - context.color(R.color.pluto___text_dark_80) - ) - ), - search - ) - ) - } - } - - private fun handleExceptionUI(it: ExceptionData, data: ApiCallData, search: String?) { - binding.protocol.text = context?.getString(R.string.pluto___na) - binding.responseTime.setSpan { - append( - highlight( - semiBold( - fontColor( - it.timeStamp.asFormattedDate(DATE_FORMAT), - context.color(R.color.pluto___text_dark_80) - ) - ), - search - ) - ) - } - binding.delay.setSpan { - append( - highlight( - semiBold( - fontColor( - "${it.timeStamp - data.request.timestamp} ms", - context.color(R.color.pluto___text_dark_80) - ) - ), - search - ) - ) - } - } - - private fun setupProxyState(data: ApiCallData) { - if (data.proxy != null) { - binding.proxyStub.proxyUrlGroup.visibility = VISIBLE - binding.proxyStub.settingsUrl.text = data.proxy?.url - } else { - binding.proxyStub.proxyUrlGroup.visibility = GONE - } - } - - private fun setupStatusView(data: ApiCallData) { - binding.progress.visibility = VISIBLE - binding.status.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0) - binding.status.text = context?.getString(R.string.pluto___network_state_in_progress) - binding.statusView.setBackgroundColor(requireContext().color(R.color.pluto___dark_05)) - - data.exception?.let { - binding.progress.visibility = GONE - binding.status.setCompoundDrawablesWithIntrinsicBounds(R.drawable.pluto___ic_error, 0, 0, 0) - binding.status.setSpan { - append( - fontColor( - it.name ?: context.getString(R.string.pluto___network_state_failed), - context.color(R.color.pluto___red) - ) - ) - } - binding.statusView.setBackgroundColor(requireContext().color(R.color.pluto___red_05)) - } - - data.response?.let { - binding.progress.visibility = GONE - binding.status.setCompoundDrawablesWithIntrinsicBounds( - if (it.isSuccessful) R.drawable.pluto___ic_success else R.drawable.pluto___ic_error, 0, 0, 0 - ) - binding.status.setSpan { - append( - fontColor( - bold("${it.status.code} "), - context.color(if (it.isSuccessful) R.color.pluto___dull_green else R.color.pluto___red) - ) - ) - append( - fontColor( - it.status.message, - context.color(if (it.isSuccessful) R.color.pluto___dull_green else R.color.pluto___red) - ) - ) - } - binding.statusView.setBackgroundColor(requireContext().color(if (it.isSuccessful) R.color.pluto___dull_green_08 else R.color.pluto___red_05)) - } - } - - private companion object { - const val DATE_FORMAT = "MMM dd, yyyy, HH:mm:ss.SSS" - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/network/ui/details/NetworkDetailsPagerAdapter.kt b/pluto/src/main/java/com/mocklets/pluto/modules/network/ui/details/NetworkDetailsPagerAdapter.kt deleted file mode 100644 index 7d93b1fe..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/network/ui/details/NetworkDetailsPagerAdapter.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.mocklets.pluto.modules.network.ui.details - -import androidx.fragment.app.Fragment -import androidx.viewpager2.adapter.FragmentStateAdapter -import com.mocklets.pluto.R - -internal val TAB_TITLES = arrayOf( - R.string.pluto___network_tab_overview, - R.string.pluto___network_tab_request, - R.string.pluto___network_tab_response -) - -internal class NetworkDetailsPagerAdapter(fragment: Fragment) : FragmentStateAdapter(fragment) { - - override fun getItemCount(): Int { - return TAB_TITLES.size - } - - override fun createFragment(position: Int): Fragment { - return when (TAB_TITLES[position]) { - R.string.pluto___network_tab_overview -> NetworkDetailsOverviewFragment() - R.string.pluto___network_tab_request -> NetworkDetailsRequestFragment() - R.string.pluto___network_tab_response -> NetworkDetailsResponseFragment() - else -> NetworkDetailsRequestFragment() - } - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/network/ui/details/NetworkDetailsRequestFragment.kt b/pluto/src/main/java/com/mocklets/pluto/modules/network/ui/details/NetworkDetailsRequestFragment.kt deleted file mode 100644 index 692ba52e..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/network/ui/details/NetworkDetailsRequestFragment.kt +++ /dev/null @@ -1,74 +0,0 @@ -package com.mocklets.pluto.modules.network.ui.details - -import android.os.Bundle -import android.view.View -import android.view.View.GONE -import android.view.View.VISIBLE -import androidx.fragment.app.Fragment -import androidx.fragment.app.activityViewModels -import androidx.lifecycle.Observer -import com.mocklets.pluto.R -import com.mocklets.pluto.core.binding.viewBinding -import com.mocklets.pluto.core.extensions.color -import com.mocklets.pluto.core.ui.spannable.setSpan -import com.mocklets.pluto.databinding.PlutoFragmentNetworkDetailsRequestBinding -import com.mocklets.pluto.modules.network.RequestData -import com.mocklets.pluto.modules.network.beautifyHeaders -import com.mocklets.pluto.modules.network.beautifyQueryParams -import com.mocklets.pluto.modules.network.ui.DetailContentData -import com.mocklets.pluto.modules.network.ui.NetworkViewModel - -internal class NetworkDetailsRequestFragment : Fragment(R.layout.pluto___fragment_network_details_request) { - - private val binding by viewBinding(PlutoFragmentNetworkDetailsRequestBinding::bind) - private val viewModel: NetworkViewModel by activityViewModels() - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - viewModel.detailContentLiveData.removeObservers(viewLifecycleOwner) - viewModel.detailContentLiveData.observe(viewLifecycleOwner, apiCallObserver) - } - - private val apiCallObserver = Observer { - updateUi(it.api.request, it.search) - } - - private fun updateUi(data: RequestData, search: String?) { - if (data.headers.isNotEmpty()) { - binding.headerTitle.setSpan { - append(context.getString(R.string.pluto___headers_title)) - append(fontColor(" (${data.headers.size})", context.color(R.color.pluto___text_dark_40))) - } - context?.beautifyHeaders(data.headers)?.let { binding.headers.setSpan { append(highlight(it, search)) } } - } - - val queryParamsText = context?.beautifyQueryParams(data.url) - if (!queryParamsText.isNullOrEmpty()) { - binding.queryParamsGroup.visibility = VISIBLE - binding.queryParamsTitle.setSpan { - append(context.getString(R.string.pluto___query_params_title)) - append(fontColor(" (${data.url.querySize()})", context.color(R.color.pluto___text_dark_40))) - } - binding.queryParams.setSpan { append(highlight(queryParamsText, search)) } - } else { - binding.queryParamsGroup.visibility = GONE - } - setBody(data, search) - } - - private fun setBody(data: RequestData, search: String?) { - binding.bodyGroup.visibility = GONE - data.body?.let { - if (it.isValid) { - binding.bodyGroup.visibility = VISIBLE - binding.body.setSpan { - if (it.isBinary) { - append(fontColor(italic("${it.body}"), context.color(R.color.pluto___text_dark_60))) - } else { - append(highlight("${it.body}", search)) - } - } - } - } - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/network/ui/details/NetworkDetailsResponseFragment.kt b/pluto/src/main/java/com/mocklets/pluto/modules/network/ui/details/NetworkDetailsResponseFragment.kt deleted file mode 100644 index 082ca438..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/network/ui/details/NetworkDetailsResponseFragment.kt +++ /dev/null @@ -1,147 +0,0 @@ -package com.mocklets.pluto.modules.network.ui.details - -import android.os.Bundle -import android.view.View -import android.view.View.GONE -import android.view.View.VISIBLE -import androidx.fragment.app.Fragment -import androidx.fragment.app.activityViewModels -import androidx.lifecycle.Observer -import com.mocklets.pluto.R -import com.mocklets.pluto.core.binding.viewBinding -import com.mocklets.pluto.core.extensions.color -import com.mocklets.pluto.core.ui.spannable.createSpan -import com.mocklets.pluto.core.ui.spannable.setSpan -import com.mocklets.pluto.databinding.PlutoFragmentNetworkDetailsResponseBinding -import com.mocklets.pluto.modules.exceptions.ExceptionData -import com.mocklets.pluto.modules.exceptions.ui.holder.CrashItemDetailsHeaderHolder -import com.mocklets.pluto.modules.network.ResponseData -import com.mocklets.pluto.modules.network.beautifyHeaders -import com.mocklets.pluto.modules.network.ui.DetailContentData -import com.mocklets.pluto.modules.network.ui.NetworkViewModel - -internal class NetworkDetailsResponseFragment : Fragment(R.layout.pluto___fragment_network_details_response) { - - private val binding by viewBinding(PlutoFragmentNetworkDetailsResponseBinding::bind) - private val viewModel: NetworkViewModel by activityViewModels() - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - binding.waiting.visibility = GONE - binding.headerGroup.visibility = GONE - binding.bodyGroup.visibility = GONE - - viewModel.detailContentLiveData.removeObservers(viewLifecycleOwner) - viewModel.detailContentLiveData.observe(viewLifecycleOwner, apiCallObserver) - } - - private val apiCallObserver = Observer { - when { - it.api.response != null -> updateResponse(it.api.response!!, it.search, it.api.hasResponseBody) - it.api.exception != null -> updateException(it.api.exception!!, it.search) - else -> updateWaiting() - } - } - - private fun updateException(data: ExceptionData, search: String?) { - binding.waiting.visibility = GONE - binding.headerGroup.visibility = VISIBLE - binding.bodyGroup.visibility = GONE - - binding.headerTitle.text = context?.getString(R.string.pluto___exception_title) - setException(data, search) - } - - private fun updateWaiting() { - binding.waiting.visibility = VISIBLE - binding.headerGroup.visibility = GONE - binding.bodyGroup.visibility = GONE - } - - private fun updateResponse(data: ResponseData, search: String?, hasBody: Boolean) { - binding.waiting.visibility = GONE - binding.headerGroup.visibility = VISIBLE - binding.bodyGroup.visibility = GONE - - if (data.headers.isNotEmpty()) { - binding.headerTitle.setSpan { - append(context.getString(R.string.pluto___headers_title)) - append(fontColor(" (${data.headers.size})", context.color(R.color.pluto___text_dark_40))) - } - context?.beautifyHeaders(data.headers)?.let { binding.headers.setSpan { append(highlight(it, search)) } } - } - setBody(data, search, hasBody) - } - - private fun setBody(data: ResponseData, search: String?, hasBody: Boolean) { - if (hasBody) { - binding.bodyGroup.visibility = VISIBLE - binding.body.setSpan { - append(fontColor(italic(context.getString(R.string.pluto___processing_body)), context.color(R.color.pluto___text_dark_40))) - } - data.body?.let { - if (it.isValid) { - binding.bodyGroup.visibility = VISIBLE - binding.body.setSpan { - if (it.isBinary) { - append(fontColor(italic("${it.body}"), context.color(R.color.pluto___text_dark_60))) - } else { - if (it.body?.length ?: 0 > MAX_BLOB_LENGTH) { - append(highlight("${it.body?.subSequence(0, MAX_BLOB_LENGTH)}", search)) - append("\n\n\t") - append( - fontColor( - italic(bold(context.getString(R.string.pluto___content_truncated))), - context.color(R.color.pluto___text_dark_40) - ) - ) - } else { - append(highlight("${it.body}", search)) - } - } - } - } else { - binding.bodyGroup.visibility = GONE - } - } - } - } - - private fun setException(data: ExceptionData, search: String?) { - binding.headerTitle.setSpan { - val exceptionData = context.createSpan { - append( - highlight( - semiBold(fontColor("${data.name}\n", context.color(R.color.pluto___text_dark_80))), - search - ) - ) - data.message?.let { append(highlight(semiBold(it), search)) } - } - append(highlight(exceptionData, search)) - } - binding.headers.setSpan { - append(highlight("${data.name}: ${data.message}", search)) - data.stackTrace.take(CrashItemDetailsHeaderHolder.MAX_STACK_TRACE_LINES).forEach { - append("\n\t\t\t") - append(fontColor(" at ", context.color(R.color.pluto___text_dark_40))) - append(highlight(it, search)) - } - val extraTrace = - data.stackTrace.size - CrashItemDetailsHeaderHolder.MAX_STACK_TRACE_LINES - if (extraTrace > 0) { - append( - fontColor( - "\n\t\t\t + $extraTrace more lines", - context.color(R.color.pluto___text_dark_40) - ) - ) - } - } - } - - private companion object { - const val MAX_BLOB_LENGTH = 25_000 - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/preferences/SharedPrefRepo.kt b/pluto/src/main/java/com/mocklets/pluto/modules/preferences/SharedPrefRepo.kt deleted file mode 100644 index 112769cf..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/preferences/SharedPrefRepo.kt +++ /dev/null @@ -1,127 +0,0 @@ -package com.mocklets.pluto.modules.preferences - -import android.content.Context -import android.content.SharedPreferences -import androidx.preference.PreferenceManager -import com.google.gson.Gson -import com.google.gson.reflect.TypeToken -import com.mocklets.pluto.Pluto -import com.mocklets.pluto.core.DebugLog -import com.mocklets.pluto.core.preferences.Preferences -import com.mocklets.pluto.modules.preferences.ui.SharedPrefFile -import com.mocklets.pluto.modules.preferences.ui.SharedPrefKeyValuePair -import java.io.File -import java.lang.reflect.Type - -internal object SharedPrefRepo { - -// private val prefList: ArrayList -// get() = Pluto.appContext.getSharePreferencesFiles() - - private val preferences by lazy { Pluto.preferences } - private val selectedFileListType: Type = object : TypeToken>() {}.type - - fun get(context: Context): List { - val list = arrayListOf() - val prefFilesList = getSelectedPreferenceFiles(context) - prefFilesList.forEach { - val data = context.getPrefKeyValueMap(it) - list.addAll(data.second) - } - list.sortBy { it.key } - return list - } - - fun set(context: Context, pair: SharedPrefKeyValuePair, value: Any) { - val prefFile = context.getPrefFile(pair.prefLabel ?: Preferences.DEFAULT) - val editor = context.getPrefManager(prefFile).edit() - when (value) { - is Int -> editor.putInt(pair.key, value).apply() - is Long -> editor.putLong(pair.key, value).apply() - is Float -> editor.putFloat(pair.key, value).apply() - is Boolean -> editor.putBoolean(pair.key, value).apply() - else -> editor.putString(pair.key, value.toString()).apply() - } - } - - internal fun getSelectedPreferenceFiles(context: Context): Set { - var selectedFiles: HashSet? = - Gson().fromJson(preferences.selectedPreferenceFiles, selectedFileListType) - if (selectedFiles == null) { - preferences.selectedPreferenceFiles = Gson().toJson(context.getSharePreferencesFiles()) - selectedFiles = Gson().fromJson(preferences.selectedPreferenceFiles, selectedFileListType) - } - return selectedFiles ?: emptySet() - } - - fun updateSelectedPreferenceFile(file: SharedPrefFile) { - val selectedFiles: HashSet? = - Gson().fromJson(preferences.selectedPreferenceFiles, selectedFileListType) - selectedFiles?.let { - if (!it.add(file)) { - it.remove(file) - } - preferences.selectedPreferenceFiles = Gson().toJson(selectedFiles) - } - } - - fun deSelectAll() { - preferences.selectedPreferenceFiles = Gson().toJson(emptySet()) - } -} - -internal fun Context.getSharePreferencesFiles(): ArrayList { - val prefsDir = File(applicationInfo?.dataDir, "shared_prefs") - val list = arrayListOf() - if (prefsDir.exists() && prefsDir.isDirectory) { - prefsDir.list()?.forEach { - if (!Preferences.isPlutoPref(it)) { - list.add( - if (it == "${packageName}_preferences.xml") { - SharedPrefFile(Preferences.DEFAULT, true) - } else { - val label = it.replace(".xml", "", true) - SharedPrefFile(label, false) - } - ) - } - } - } - return list -} - -private fun Context.getPrefManager(file: SharedPrefFile): SharedPreferences = - if (file.isDefault) { - PreferenceManager.getDefaultSharedPreferences(this) - } else { - getSharedPreferences(file.label, Context.MODE_PRIVATE) - } - -private fun Context.getPrefKeyValueMap(file: SharedPrefFile): Pair> { - val prefManager = getPrefManager(file) - val list = prefManager.list(file.label, file.isDefault) - return Pair(file.label, list) -} - -private fun SharedPreferences.list(label: String, default: Boolean): List { - val list = arrayListOf() - all.toList().forEach { - list.add(SharedPrefKeyValuePair(it.first, it.second, label, default)) - } - return list -} - -@Suppress("TooGenericExceptionCaught") -private fun Context.getPrefFile(label: String): SharedPrefFile { - try { - val prefFilesList = getSharePreferencesFiles() - prefFilesList.forEach { - if (it.label == label) { - return it - } - } - } catch (e: Exception) { - DebugLog.e("preferences", "error while fetching pref file", e) - } - return SharedPrefFile(Preferences.DEFAULT, isDefault = true) -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/preferences/ui/DataModel.kt b/pluto/src/main/java/com/mocklets/pluto/modules/preferences/ui/DataModel.kt deleted file mode 100644 index 6d87c098..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/preferences/ui/DataModel.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.mocklets.pluto.modules.preferences.ui - -import androidx.annotation.Keep -import com.mocklets.pluto.core.ui.list.ListItem - -@Keep -data class SharedPrefFile( - val label: String, - val isDefault: Boolean -) : ListItem() { - override fun equals(other: Any?): Boolean { - return other is SharedPrefFile && other.label == this.label - } -} - -data class SharedPrefKeyValuePair( - val key: String, - val value: Any?, - val prefLabel: String?, - val isDefault: Boolean = false -) : ListItem() diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/preferences/ui/SharedPrefAdapter.kt b/pluto/src/main/java/com/mocklets/pluto/modules/preferences/ui/SharedPrefAdapter.kt deleted file mode 100644 index d5b8085a..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/preferences/ui/SharedPrefAdapter.kt +++ /dev/null @@ -1,31 +0,0 @@ -package com.mocklets.pluto.modules.preferences.ui - -import android.view.ViewGroup -import com.mocklets.pluto.core.ui.list.BaseAdapter -import com.mocklets.pluto.core.ui.list.DiffAwareHolder -import com.mocklets.pluto.core.ui.list.ListItem -import com.mocklets.pluto.modules.preferences.ui.filter.SharedPrefFilterItemHolder - -internal class SharedPrefAdapter(private val listener: OnActionListener) : BaseAdapter() { - - override fun getItemViewType(item: ListItem): Int? { - return when (item) { - is SharedPrefKeyValuePair -> ITEM_TYPE_PAIR - is SharedPrefFile -> ITEM_TYPE_FILTER - else -> null - } - } - - override fun onViewHolderCreated(parent: ViewGroup, viewType: Int): DiffAwareHolder? { - return when (viewType) { - ITEM_TYPE_PAIR -> SharedPrefKeyValueItemHolder(parent, listener) - ITEM_TYPE_FILTER -> SharedPrefFilterItemHolder(parent, listener) - else -> null - } - } - - companion object { - const val ITEM_TYPE_PAIR = 1001 - const val ITEM_TYPE_FILTER = 1002 - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/preferences/ui/SharedPrefEditDialog.kt b/pluto/src/main/java/com/mocklets/pluto/modules/preferences/ui/SharedPrefEditDialog.kt deleted file mode 100644 index 91d8a6c8..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/preferences/ui/SharedPrefEditDialog.kt +++ /dev/null @@ -1,144 +0,0 @@ -package com.mocklets.pluto.modules.preferences.ui - -import android.graphics.drawable.ColorDrawable -import android.text.InputType -import android.view.View -import android.view.View.GONE -import android.view.View.VISIBLE -import android.widget.FrameLayout -import androidx.core.content.ContextCompat -import androidx.core.widget.addTextChangedListener -import androidx.fragment.app.Fragment -import androidx.lifecycle.lifecycleScope -import com.google.android.material.bottomsheet.BottomSheetBehavior -import com.google.android.material.bottomsheet.BottomSheetDialog -import com.mocklets.pluto.R -import com.mocklets.pluto.core.DeviceInfo -import com.mocklets.pluto.core.extensions.color -import com.mocklets.pluto.core.extensions.inflate -import com.mocklets.pluto.core.extensions.showKeyboard -import com.mocklets.pluto.core.sharing.Shareable -import com.mocklets.pluto.core.sharing.lazyContentSharer -import com.mocklets.pluto.core.ui.setDebounceClickListener -import com.mocklets.pluto.databinding.PlutoLayoutSharedPrefEditBinding -import java.util.Locale - -internal class SharedPrefEditDialog( - private val fragment: Fragment, - private val onSave: (SharedPrefKeyValuePair, Any) -> Unit -) : BottomSheetDialog(fragment.requireContext(), R.style.PlutoBottomSheetDialogTheme) { - - private val sheetView: View = context.inflate(R.layout.pluto___layout_shared_pref_edit) - private val binding = PlutoLayoutSharedPrefEditBinding.bind(sheetView) - private val deviceInfo = DeviceInfo(context) - private val contentSharer by fragment.lazyContentSharer() - - init { - setContentView(sheetView) - (sheetView.parent as View).background = - ColorDrawable(ContextCompat.getColor(context, R.color.pluto___transparent)) - - setOnShowListener { dialog -> - if (dialog is BottomSheetDialog) { - val bottomSheet = dialog.findViewById(R.id.design_bottom_sheet) as FrameLayout? - val behavior = BottomSheetBehavior.from(bottomSheet!!) - behavior.addBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() { - override fun onStateChanged(bottomSheet: View, newState: Int) { - if (newState == BottomSheetBehavior.STATE_EXPANDED) { - fragment.lifecycleScope.launchWhenResumed { - binding.value.requestFocus() - } - } - } - - override fun onSlide(bottomSheet: View, slideOffset: Float) { - } - }) - behavior.apply { - state = BottomSheetBehavior.STATE_EXPANDED - isHideable = false - skipCollapsed = true - peekHeight = deviceInfo.height - } - } - } - - binding.value.setOnFocusChangeListener { v, hasFocus -> - if (hasFocus) { - v.showKeyboard() - } - } - } - - fun show(pref: SharedPrefKeyValuePair) { - if (isPrefTypeSupported(pref.value)) { - binding.value.isFocusableInTouchMode = true - binding.value.isFocusable = true - binding.save.visibility = VISIBLE - binding.value.addTextChangedListener { - val isValid = it.toString().isValid(pref.value) - binding.save.isEnabled = isValid - binding.save.setBackgroundColor(context.color(if (isValid) R.color.pluto___dark else R.color.pluto___dark_40)) - } - binding.value.inputType = handleKeypad(pref.value) - binding.value.hint = setHint(pref.value) - binding.save.visibility = VISIBLE - binding.disabled.visibility = GONE - } else { - binding.value.isFocusableInTouchMode = false - binding.value.isFocusable = false - binding.save.visibility = GONE - binding.disabled.visibility = VISIBLE - } - binding.file.text = pref.prefLabel - binding.key.text = pref.key - binding.value.setText(pref.value.toString()) - binding.value.setSelection(pref.value.toString().length) - binding.save.setDebounceClickListener { - onSave.invoke(pref, binding.value.text.toString().convert(pref.value)) - } - binding.cta.setDebounceClickListener { - contentSharer.share( - Shareable( - content = "${pref.key} : ${pref.value}", - title = "Share Shared Preference", - fileName = "Preference data from Pluto" - ) - ) - } - show() - } - - private fun setHint(value: Any?): CharSequence = when (value) { - is Int, - is Long -> "12345" - is Float -> "1234.89" - is Boolean -> "true" - else -> "abcde 123" - } - - private fun isPrefTypeSupported(value: Any?): Boolean = value !is Set<*> - - private fun handleKeypad(value: Any?): Int = when (value) { - is Int, - is Long -> InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_SIGNED - is Float -> InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_SIGNED or InputType.TYPE_NUMBER_FLAG_DECIMAL - else -> InputType.TYPE_CLASS_TEXT - } -} - -private fun String.convert(value: Any?): Any = when (value) { - is Int -> this.toInt() - is Long -> this.toLong() - is Float -> this.toFloat() - is Boolean -> this.toBoolean() - else -> this -} - -private fun String.isValid(value: Any?): Boolean = when (value) { - is Boolean -> this.lowercase(Locale.getDefault()) == "true" || this.lowercase(Locale.getDefault()) == "false" - is Int, - is Float, - is Long -> this.isNotEmpty() - else -> true -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/preferences/ui/SharedPrefFragment.kt b/pluto/src/main/java/com/mocklets/pluto/modules/preferences/ui/SharedPrefFragment.kt deleted file mode 100644 index 1f8a6d6a..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/preferences/ui/SharedPrefFragment.kt +++ /dev/null @@ -1,100 +0,0 @@ -package com.mocklets.pluto.modules.preferences.ui - -import android.os.Bundle -import android.view.View -import androidx.core.widget.addTextChangedListener -import androidx.fragment.app.Fragment -import androidx.fragment.app.activityViewModels -import androidx.lifecycle.Observer -import androidx.lifecycle.lifecycleScope -import com.mocklets.pluto.Pluto -import com.mocklets.pluto.R -import com.mocklets.pluto.core.binding.viewBinding -import com.mocklets.pluto.core.extensions.delayedLaunchWhenResumed -import com.mocklets.pluto.core.extensions.linearLayoutManager -import com.mocklets.pluto.core.extensions.showMoreOptions -import com.mocklets.pluto.core.ui.list.BaseAdapter -import com.mocklets.pluto.core.ui.list.CustomItemDecorator -import com.mocklets.pluto.core.ui.list.DiffAwareAdapter -import com.mocklets.pluto.core.ui.list.DiffAwareHolder -import com.mocklets.pluto.core.ui.list.ListItem -import com.mocklets.pluto.core.ui.routing.Screens -import com.mocklets.pluto.core.ui.routing.lazyRouter -import com.mocklets.pluto.core.ui.setDebounceClickListener -import com.mocklets.pluto.databinding.PlutoFragmentSharedPrefBinding -import com.mocklets.pluto.modules.preferences.SharedPrefRepo - -internal class SharedPrefFragment : Fragment(R.layout.pluto___fragment_shared_pref) { - - private val binding by viewBinding(PlutoFragmentSharedPrefBinding::bind) - private val viewModel: SharedPrefViewModel by activityViewModels() - private lateinit var editDialog: SharedPrefEditDialog - - private val prefAdapter: BaseAdapter by lazy { SharedPrefAdapter(onActionListener) } - private val router by lazyRouter() - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - viewModel.refresh() - binding.list.apply { - adapter = prefAdapter - addItemDecoration(CustomItemDecorator(requireContext())) - } - editDialog = SharedPrefEditDialog(this) { pair, value -> - SharedPrefRepo.set(requireContext(), pair, value) - lifecycleScope.delayedLaunchWhenResumed(DIALOG_DISMISS_DELAY) { - viewModel.refresh() - editDialog.dismiss() - } - } -// filterDialog = SharedPrefFilterDilog(this) - binding.search.addTextChangedListener { editable -> - lifecycleScope.launchWhenResumed { - editable?.toString()?.let { - Pluto.session.preferencesSearchText = it - prefAdapter.list = filteredPrefs(it) - if (it.isEmpty()) { - binding.list.linearLayoutManager()?.scrollToPositionWithOffset(0, 0) - } - } - } - } - binding.more.setDebounceClickListener { - requireContext().showMoreOptions(it, R.menu.pluto___popup_menu_shared_pref) { item -> - when (item.itemId) { - R.id.filter -> router.navigate(Screens.SharedPrefFilter) - } - } - } - binding.search.setText(Pluto.session.preferencesSearchText) - viewModel.preferences.removeObserver(sharedPrefObserver) - viewModel.preferences.observe(viewLifecycleOwner, sharedPrefObserver) - } - - private fun filteredPrefs(search: String): List { - var list = emptyList() - viewModel.preferences.value?.let { - list = it.filter { pref -> - pref.key.contains(search, true) - } - } - binding.noItemText.visibility = if (list.isEmpty()) View.VISIBLE else View.GONE - return list - } - - private val sharedPrefObserver = Observer> { - prefAdapter.list = filteredPrefs(binding.search.text.toString()) - } - - private val onActionListener = object : DiffAwareAdapter.OnActionListener { - override fun onAction(action: String, data: ListItem, holder: DiffAwareHolder?) { - if (data is SharedPrefKeyValuePair) { - editDialog.show(data) - } - } - } - - private companion object { - const val DIALOG_DISMISS_DELAY = 200L - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/preferences/ui/SharedPrefKeyValueItemHolder.kt b/pluto/src/main/java/com/mocklets/pluto/modules/preferences/ui/SharedPrefKeyValueItemHolder.kt deleted file mode 100644 index c8ffbadd..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/preferences/ui/SharedPrefKeyValueItemHolder.kt +++ /dev/null @@ -1,53 +0,0 @@ -package com.mocklets.pluto.modules.preferences.ui - -import android.view.View.GONE -import android.view.View.VISIBLE -import android.view.ViewGroup -import com.mocklets.pluto.R -import com.mocklets.pluto.core.extensions.color -import com.mocklets.pluto.core.extensions.inflate -import com.mocklets.pluto.core.ui.list.DiffAwareAdapter -import com.mocklets.pluto.core.ui.list.DiffAwareHolder -import com.mocklets.pluto.core.ui.list.ListItem -import com.mocklets.pluto.core.ui.setDebounceClickListener -import com.mocklets.pluto.core.ui.spannable.createSpan -import com.mocklets.pluto.databinding.PlutoItemSharedPrefKeyValueBinding - -internal class SharedPrefKeyValueItemHolder( - parent: ViewGroup, - actionListener: DiffAwareAdapter.OnActionListener -) : DiffAwareHolder(parent.inflate(R.layout.pluto___item_shared_pref_key_value), actionListener) { - - private val binding = PlutoItemSharedPrefKeyValueBinding.bind(itemView) - private val key = binding.key - private val value = binding.value - private val file = binding.file - - override fun onBind(item: ListItem) { - if (item is SharedPrefKeyValuePair) { - key.text = item.key - file.visibility = if (item.isDefault) GONE else VISIBLE - val fileName = item.prefLabel - file.text = if (fileName != null) { - if (fileName.length > MAX_FILENAME_LENGTH) { - "${fileName.substring(0, MAX_FILENAME_LENGTH - 2)}..." - } else { - fileName - } - } else { - itemView.context.createSpan { - append(fontColor(light(italic("null")), context.color(R.color.pluto___text_dark_40))) - } - } - item.value?.let { value.text = it.toString() } - - itemView.setDebounceClickListener { - onAction("click") - } - } - } - - companion object { - const val MAX_FILENAME_LENGTH = 18 - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/preferences/ui/SharedPrefViewModel.kt b/pluto/src/main/java/com/mocklets/pluto/modules/preferences/ui/SharedPrefViewModel.kt deleted file mode 100644 index c8cdaf11..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/preferences/ui/SharedPrefViewModel.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.mocklets.pluto.modules.preferences.ui - -import android.app.Application -import androidx.lifecycle.AndroidViewModel -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData -import com.mocklets.pluto.modules.preferences.SharedPrefRepo - -internal class SharedPrefViewModel(application: Application) : AndroidViewModel(application) { - - val preferences: LiveData> - get() = _preferences - private val _preferences = MutableLiveData>() - - fun refresh() { - _preferences.postValue(SharedPrefRepo.get(getApplication())) - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/preferences/ui/filter/SharedPrefFilterFragment.kt b/pluto/src/main/java/com/mocklets/pluto/modules/preferences/ui/filter/SharedPrefFilterFragment.kt deleted file mode 100644 index de788947..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/preferences/ui/filter/SharedPrefFilterFragment.kt +++ /dev/null @@ -1,67 +0,0 @@ -package com.mocklets.pluto.modules.preferences.ui.filter - -import android.os.Bundle -import android.view.View -import androidx.fragment.app.Fragment -import androidx.fragment.app.activityViewModels -import com.mocklets.pluto.R -import com.mocklets.pluto.core.binding.viewBinding -import com.mocklets.pluto.core.extensions.dp -import com.mocklets.pluto.core.extensions.toast -import com.mocklets.pluto.core.ui.list.BaseAdapter -import com.mocklets.pluto.core.ui.list.CustomItemDecorator -import com.mocklets.pluto.core.ui.list.DiffAwareAdapter -import com.mocklets.pluto.core.ui.list.DiffAwareHolder -import com.mocklets.pluto.core.ui.list.ListItem -import com.mocklets.pluto.core.ui.setDebounceClickListener -import com.mocklets.pluto.databinding.PlutoFragmentSharedPrefFilterBinding -import com.mocklets.pluto.modules.preferences.SharedPrefRepo -import com.mocklets.pluto.modules.preferences.getSharePreferencesFiles -import com.mocklets.pluto.modules.preferences.ui.SharedPrefAdapter -import com.mocklets.pluto.modules.preferences.ui.SharedPrefFile -import com.mocklets.pluto.modules.preferences.ui.SharedPrefViewModel - -internal class SharedPrefFilterFragment : Fragment(R.layout.pluto___fragment_shared_pref_filter) { - - private val binding by viewBinding(PlutoFragmentSharedPrefFilterBinding::bind) - private val viewModel: SharedPrefViewModel by activityViewModels() - - private val prefAdapter: BaseAdapter by lazy { SharedPrefAdapter(onActionListener) } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - binding.list.apply { - adapter = prefAdapter - addItemDecoration(CustomItemDecorator(requireContext(), DECORATOR_DIVIDER_PADDING)) - } - binding.close.setDebounceClickListener { - activity?.onBackPressed() - } - - binding.clear.setDebounceClickListener { - SharedPrefRepo.deSelectAll() - prefAdapter.notifyDataSetChanged() - requireContext().toast(requireContext().getString(R.string.pluto___preferences_cleared)) - } - - prefAdapter.list = context?.getSharePreferencesFiles() ?: emptyList() - } - - override fun onDestroyView() { - super.onDestroyView() - viewModel.refresh() - } - - private val onActionListener = object : DiffAwareAdapter.OnActionListener { - override fun onAction(action: String, data: ListItem, holder: DiffAwareHolder?) { - if (data is SharedPrefFile) { - SharedPrefRepo.updateSelectedPreferenceFile(data) - holder?.absoluteAdapterPosition?.let { prefAdapter.notifyItemChanged(it) } - } - } - } - - private companion object { - val DECORATOR_DIVIDER_PADDING = 16f.dp.toInt() - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/preferences/ui/filter/SharedPrefFilterItemHolder.kt b/pluto/src/main/java/com/mocklets/pluto/modules/preferences/ui/filter/SharedPrefFilterItemHolder.kt deleted file mode 100644 index dc864606..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/preferences/ui/filter/SharedPrefFilterItemHolder.kt +++ /dev/null @@ -1,40 +0,0 @@ -package com.mocklets.pluto.modules.preferences.ui.filter - -import android.view.ViewGroup -import com.mocklets.pluto.R -import com.mocklets.pluto.core.extensions.color -import com.mocklets.pluto.core.extensions.inflate -import com.mocklets.pluto.core.ui.list.DiffAwareAdapter -import com.mocklets.pluto.core.ui.list.DiffAwareHolder -import com.mocklets.pluto.core.ui.list.ListItem -import com.mocklets.pluto.core.ui.setDebounceClickListener -import com.mocklets.pluto.core.ui.spannable.setSpan -import com.mocklets.pluto.databinding.PlutoItemSharedPrefFilterBinding -import com.mocklets.pluto.modules.preferences.SharedPrefRepo -import com.mocklets.pluto.modules.preferences.ui.SharedPrefFile - -internal class SharedPrefFilterItemHolder( - parent: ViewGroup, - listener: DiffAwareAdapter.OnActionListener -) : DiffAwareHolder(parent.inflate(R.layout.pluto___item_shared_pref_filter), listener) { - - private val binding = PlutoItemSharedPrefFilterBinding.bind(itemView) - private val title = binding.title - private val checkbox = binding.checkbox - - override fun onBind(item: ListItem) { - if (item is SharedPrefFile) { - title.setSpan { - if (item.isDefault) { - append(italic(light(fontColor(item.label, context.color(R.color.pluto___text_dark_60))))) - } else { - append(item.label) - } - } - itemView.setDebounceClickListener { - onAction("click") - } - checkbox.isSelected = SharedPrefRepo.getSelectedPreferenceFiles(context).contains(item) - } - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/settings/DataModel.kt b/pluto/src/main/java/com/mocklets/pluto/modules/settings/DataModel.kt deleted file mode 100644 index a9179fc8..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/settings/DataModel.kt +++ /dev/null @@ -1,28 +0,0 @@ -package com.mocklets.pluto.modules.settings - -import com.mocklets.pluto.core.ui.list.ListItem - -data class SettingsEasyAccessEntity( - val label: String = "easy_access" -) : ListItem() - -data class SettingsClearDataEntity( - val id: String, - val label: String -) : ListItem() - -data class SettingsSharedPrefEntity( - val label: String = "shared_pref" -) : ListItem() - -data class SettingsLinkMockletsEntity( - val label: String = "mocklets" -) : ListItem() - -data class SettingsEasyAccessPopupAppearanceEntity( - val type: String -) : ListItem() - -data class SettingsResetAllEntity( - val type: String = "rest all" -) : ListItem() diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/settings/SettingsAdapter.kt b/pluto/src/main/java/com/mocklets/pluto/modules/settings/SettingsAdapter.kt deleted file mode 100644 index 79f9a8f0..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/settings/SettingsAdapter.kt +++ /dev/null @@ -1,47 +0,0 @@ -package com.mocklets.pluto.modules.settings - -import android.view.ViewGroup -import com.mocklets.pluto.core.ui.list.BaseAdapter -import com.mocklets.pluto.core.ui.list.DiffAwareHolder -import com.mocklets.pluto.core.ui.list.ListItem -import com.mocklets.pluto.modules.settings.holders.SettingsClearDataHolder -import com.mocklets.pluto.modules.settings.holders.SettingsEasyAccessHolder -import com.mocklets.pluto.modules.settings.holders.SettingsEasyAccessPopupAppearanceHolder -import com.mocklets.pluto.modules.settings.holders.SettingsMockletsLinkHolder -import com.mocklets.pluto.modules.settings.holders.SettingsResetAllHolder -import com.mocklets.pluto.modules.settings.holders.SettingsSharePreferenceHolder - -internal class SettingsAdapter(private val listener: OnActionListener) : BaseAdapter() { - override fun getItemViewType(item: ListItem): Int? { - return when (item) { - is SettingsEasyAccessEntity -> ITEM_TYPE_EASY_ACCESS - is SettingsClearDataEntity -> ITEM_TYPE_CLEAR_CRASHES - is SettingsSharedPrefEntity -> ITEM_TYPE_SHARED_PREF - is SettingsLinkMockletsEntity -> ITEM_TYPE_MOCKLETS - is SettingsEasyAccessPopupAppearanceEntity -> ITEM_TYPE_EASY_ACCESS_APPEARANCE - is SettingsResetAllEntity -> ITEM_TYPE_RESET_ALL - else -> null - } - } - - override fun onViewHolderCreated(parent: ViewGroup, viewType: Int): DiffAwareHolder? { - return when (viewType) { - ITEM_TYPE_EASY_ACCESS -> SettingsEasyAccessHolder(parent, listener) - ITEM_TYPE_CLEAR_CRASHES -> SettingsClearDataHolder(parent, listener) - ITEM_TYPE_SHARED_PREF -> SettingsSharePreferenceHolder(parent, listener) - ITEM_TYPE_MOCKLETS -> SettingsMockletsLinkHolder(parent, listener) - ITEM_TYPE_EASY_ACCESS_APPEARANCE -> SettingsEasyAccessPopupAppearanceHolder(parent, listener) - ITEM_TYPE_RESET_ALL -> SettingsResetAllHolder(parent, listener) - else -> null - } - } - - companion object { - const val ITEM_TYPE_EASY_ACCESS = 1000 - const val ITEM_TYPE_CLEAR_CRASHES = 1001 - const val ITEM_TYPE_SHARED_PREF = 1002 - const val ITEM_TYPE_MOCKLETS = 1003 - const val ITEM_TYPE_EASY_ACCESS_APPEARANCE = 1004 - const val ITEM_TYPE_RESET_ALL = 1005 - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/settings/SettingsFragment.kt b/pluto/src/main/java/com/mocklets/pluto/modules/settings/SettingsFragment.kt deleted file mode 100644 index 698f0cc5..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/settings/SettingsFragment.kt +++ /dev/null @@ -1,107 +0,0 @@ -package com.mocklets.pluto.modules.settings - -import android.content.Intent -import android.net.Uri -import android.os.Bundle -import android.view.View -import androidx.fragment.app.Fragment -import androidx.fragment.app.activityViewModels -import androidx.fragment.app.viewModels -import androidx.lifecycle.Observer -import com.mocklets.pluto.BuildConfig -import com.mocklets.pluto.R -import com.mocklets.pluto.core.binding.viewBinding -import com.mocklets.pluto.core.extensions.color -import com.mocklets.pluto.core.extensions.dp -import com.mocklets.pluto.core.extensions.openOverlaySettings -import com.mocklets.pluto.core.extensions.toast -import com.mocklets.pluto.core.preferences.Preferences -import com.mocklets.pluto.core.ui.list.BaseAdapter -import com.mocklets.pluto.core.ui.list.CustomItemDecorator -import com.mocklets.pluto.core.ui.list.DiffAwareAdapter -import com.mocklets.pluto.core.ui.list.DiffAwareHolder -import com.mocklets.pluto.core.ui.list.ListItem -import com.mocklets.pluto.core.ui.routing.Screens -import com.mocklets.pluto.core.ui.routing.lazyRouter -import com.mocklets.pluto.core.ui.setDebounceClickListener -import com.mocklets.pluto.core.ui.spannable.setSpan -import com.mocklets.pluto.databinding.PlutoFragmentSettingsBinding -import com.mocklets.pluto.modules.exceptions.ui.CrashesViewModel - -internal class SettingsFragment : Fragment(R.layout.pluto___fragment_settings) { - - private val binding by viewBinding(PlutoFragmentSettingsBinding::bind) - private val viewModel: SettingsViewModel by viewModels() - private val exceptionViewModel: CrashesViewModel by activityViewModels() - private val router by lazyRouter() - private val settingsAdapter: BaseAdapter by lazy { SettingsAdapter(onActionListener) } - - override fun onResume() { - super.onResume() - viewModel.generate(context) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - binding.list.apply { - adapter = settingsAdapter - addItemDecoration(CustomItemDecorator(context, DECORATOR_DIVIDER_PADDING)) - } - viewModel.list.removeObserver(settingsObserver) - viewModel.list.observe(viewLifecycleOwner, settingsObserver) - - binding.about.setDebounceClickListener { - val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse("http://pluto.mocklets.com")) - startActivity(browserIntent) - } - - binding.version.setSpan { - append(fontColor(regular("v "), context.color(R.color.pluto___text_dark_40))) - append(semiBold(BuildConfig.VERSION_NAME)) - } - - binding.close.setDebounceClickListener { - activity?.onBackPressed() - } - } - - private val settingsObserver = Observer> { - settingsAdapter.list = it - } - - private val onActionListener = object : DiffAwareAdapter.OnActionListener { - override fun onAction(action: String, data: ListItem, holder: DiffAwareHolder?) { - when (data) { - is SettingsEasyAccessEntity -> context?.openOverlaySettings() - is SettingsEasyAccessPopupAppearanceEntity -> { - when (data.type) { - "mode" -> { - val current = Preferences(context!!).isDarkAccessPopup - Preferences(context!!).isDarkAccessPopup = !current - } - "handed" -> { - val current = Preferences(context!!).isRightHandedAccessPopup - Preferences(context!!).isRightHandedAccessPopup = !current - } - else -> { - check(!BuildConfig.DEBUG) { - "unsupported appearance type" - } - } - } - settingsAdapter.notifyItemChanged(holder?.absoluteAdapterPosition ?: 0) - } - is SettingsSharedPrefEntity -> router.navigate(Screens.SharedPrefFilter) - is SettingsResetAllEntity -> { - viewModel.resetAll() - exceptionViewModel.deleteAll() - context?.toast(context!!.getString(R.string.pluto___reset_all_success_message)) - } - } - } - } - - private companion object { - val DECORATOR_DIVIDER_PADDING = 16f.dp.toInt() - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/settings/holders/SettingsClearDataHolder.kt b/pluto/src/main/java/com/mocklets/pluto/modules/settings/holders/SettingsClearDataHolder.kt deleted file mode 100644 index baed8f38..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/settings/holders/SettingsClearDataHolder.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.mocklets.pluto.modules.settings.holders - -import android.view.ViewGroup -import com.mocklets.pluto.R -import com.mocklets.pluto.core.extensions.inflate -import com.mocklets.pluto.core.ui.list.DiffAwareAdapter -import com.mocklets.pluto.core.ui.list.DiffAwareHolder -import com.mocklets.pluto.core.ui.list.ListItem -import com.mocklets.pluto.core.ui.setDebounceClickListener -import com.mocklets.pluto.databinding.PlutoItemSettingsClearDataBinding -import com.mocklets.pluto.modules.settings.SettingsClearDataEntity - -internal class SettingsClearDataHolder( - parent: ViewGroup, - listener: DiffAwareAdapter.OnActionListener -) : DiffAwareHolder(parent.inflate(R.layout.pluto___item_settings_clear_data), listener) { - - private val binding = PlutoItemSettingsClearDataBinding.bind(itemView) - private val title = binding.title - private val cta = binding.cta - - override fun onBind(item: ListItem) { - if (item is SettingsClearDataEntity) { - title.text = item.label - cta.setDebounceClickListener { - onAction(item.id) - } - } - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/settings/holders/SettingsEasyAccessHolder.kt b/pluto/src/main/java/com/mocklets/pluto/modules/settings/holders/SettingsEasyAccessHolder.kt deleted file mode 100644 index fc16000c..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/settings/holders/SettingsEasyAccessHolder.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.mocklets.pluto.modules.settings.holders - -import android.view.ViewGroup -import com.mocklets.pluto.R -import com.mocklets.pluto.core.extensions.canDrawOverlays -import com.mocklets.pluto.core.extensions.inflate -import com.mocklets.pluto.core.ui.list.DiffAwareAdapter -import com.mocklets.pluto.core.ui.list.DiffAwareHolder -import com.mocklets.pluto.core.ui.list.ListItem -import com.mocklets.pluto.core.ui.setDebounceClickListener -import com.mocklets.pluto.databinding.PlutoItemSettingsEasyAccessBinding -import com.mocklets.pluto.modules.settings.SettingsEasyAccessEntity - -internal class SettingsEasyAccessHolder( - parent: ViewGroup, - listener: DiffAwareAdapter.OnActionListener -) : DiffAwareHolder(parent.inflate(R.layout.pluto___item_settings_easy_access), listener) { - - private val binding = PlutoItemSettingsEasyAccessBinding.bind(itemView) - private val checkbox = binding.checkbox - - override fun onBind(item: ListItem) { - if (item is SettingsEasyAccessEntity) { - checkbox.isSelected = itemView.context.canDrawOverlays() - itemView.setDebounceClickListener { - onAction("click") - } - } - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/settings/holders/SettingsMockletsLinkHolder.kt b/pluto/src/main/java/com/mocklets/pluto/modules/settings/holders/SettingsMockletsLinkHolder.kt deleted file mode 100644 index 65abc7c0..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/settings/holders/SettingsMockletsLinkHolder.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.mocklets.pluto.modules.settings.holders - -import android.view.ViewGroup -import com.mocklets.pluto.R -import com.mocklets.pluto.core.extensions.inflate -import com.mocklets.pluto.core.ui.list.DiffAwareAdapter -import com.mocklets.pluto.core.ui.list.DiffAwareHolder -import com.mocklets.pluto.core.ui.list.ListItem -import com.mocklets.pluto.core.ui.setDebounceClickListener -import com.mocklets.pluto.databinding.PlutoItemSettingsLinkMockletsBinding -import com.mocklets.pluto.modules.settings.SettingsSharedPrefEntity - -internal class SettingsMockletsLinkHolder( - parent: ViewGroup, - listener: DiffAwareAdapter.OnActionListener -) : DiffAwareHolder(parent.inflate(R.layout.pluto___item_settings_link_mocklets), listener) { - - private val binding = PlutoItemSettingsLinkMockletsBinding.bind(itemView) - - override fun onBind(item: ListItem) { - if (item is SettingsSharedPrefEntity) { - binding.root.setDebounceClickListener { - onAction("click") - } - } - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/settings/holders/SettingsResetAllHolder.kt b/pluto/src/main/java/com/mocklets/pluto/modules/settings/holders/SettingsResetAllHolder.kt deleted file mode 100644 index b45b447e..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/settings/holders/SettingsResetAllHolder.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.mocklets.pluto.modules.settings.holders - -import android.view.ViewGroup -import com.mocklets.pluto.R -import com.mocklets.pluto.core.extensions.inflate -import com.mocklets.pluto.core.ui.list.DiffAwareAdapter -import com.mocklets.pluto.core.ui.list.DiffAwareHolder -import com.mocklets.pluto.core.ui.list.ListItem -import com.mocklets.pluto.core.ui.setDebounceClickListener -import com.mocklets.pluto.databinding.PlutoItemSettingsResetAllBinding -import com.mocklets.pluto.modules.settings.SettingsResetAllEntity - -internal class SettingsResetAllHolder( - parent: ViewGroup, - listener: DiffAwareAdapter.OnActionListener -) : DiffAwareHolder(parent.inflate(R.layout.pluto___item_settings_reset_all), listener) { - - private val binding = PlutoItemSettingsResetAllBinding.bind(itemView) - - override fun onBind(item: ListItem) { - if (item is SettingsResetAllEntity) { - binding.root.setDebounceClickListener { - onAction("click") - } - } - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/settings/holders/SettingsSharePreferenceHolder.kt b/pluto/src/main/java/com/mocklets/pluto/modules/settings/holders/SettingsSharePreferenceHolder.kt deleted file mode 100644 index 6e4274a2..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/settings/holders/SettingsSharePreferenceHolder.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.mocklets.pluto.modules.settings.holders - -import android.view.ViewGroup -import com.mocklets.pluto.R -import com.mocklets.pluto.core.extensions.inflate -import com.mocklets.pluto.core.ui.list.DiffAwareAdapter -import com.mocklets.pluto.core.ui.list.DiffAwareHolder -import com.mocklets.pluto.core.ui.list.ListItem -import com.mocklets.pluto.core.ui.setDebounceClickListener -import com.mocklets.pluto.databinding.PlutoItemSettingsSharedPrefBinding -import com.mocklets.pluto.modules.settings.SettingsSharedPrefEntity - -internal class SettingsSharePreferenceHolder( - parent: ViewGroup, - listener: DiffAwareAdapter.OnActionListener -) : DiffAwareHolder(parent.inflate(R.layout.pluto___item_settings_shared_pref), listener) { - - private val binding = PlutoItemSettingsSharedPrefBinding.bind(itemView) - - override fun onBind(item: ListItem) { - if (item is SettingsSharedPrefEntity) { - binding.root.setDebounceClickListener { - onAction("click") - } - } - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/setup/easyaccess/EasyAccessSetupDialog.kt b/pluto/src/main/java/com/mocklets/pluto/modules/setup/easyaccess/EasyAccessSetupDialog.kt deleted file mode 100644 index 682d64d0..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/setup/easyaccess/EasyAccessSetupDialog.kt +++ /dev/null @@ -1,58 +0,0 @@ -package com.mocklets.pluto.modules.setup.easyaccess - -import android.content.Context -import android.graphics.drawable.ColorDrawable -import android.view.View -import android.widget.FrameLayout -import com.google.android.material.bottomsheet.BottomSheetBehavior -import com.google.android.material.bottomsheet.BottomSheetDialog -import com.mocklets.pluto.R -import com.mocklets.pluto.core.DeviceInfo -import com.mocklets.pluto.core.extensions.color -import com.mocklets.pluto.core.extensions.inflate -import com.mocklets.pluto.core.preferences.Preferences -import com.mocklets.pluto.core.ui.setDebounceClickListener -import com.mocklets.pluto.databinding.PlutoLayoutEasyAccessSetupBinding - -internal class EasyAccessSetupDialog(context: Context, onEnabled: () -> Unit) : - BottomSheetDialog(context, R.style.PlutoBottomSheetDialogTheme) { - - private val sheetView: View = context.inflate(R.layout.pluto___layout_easy_access_setup) - private val binding = PlutoLayoutEasyAccessSetupBinding.bind(sheetView) - private val deviceInfo = DeviceInfo(context) - private val preferences = Preferences(context) - - init { - setContentView(sheetView) - (sheetView.parent as View).background = ColorDrawable(context.color(R.color.pluto___transparent)) - - this.setOnShowListener { dialog -> - val d = dialog as BottomSheetDialog - - val bottomSheet = d.findViewById(R.id.design_bottom_sheet) as FrameLayout? - val behavior = BottomSheetBehavior.from(bottomSheet!!) - behavior.apply { - state = BottomSheetBehavior.STATE_EXPANDED - isHideable = false - skipCollapsed = true - peekHeight = deviceInfo.height - } - - dialog.setCancelable(false) - dialog.setCanceledOnTouchOutside(false) - } - - binding.cta.setDebounceClickListener { - dismiss() - onEnabled.invoke() - } - - binding.dismiss.setDebounceClickListener { - dismiss() - } - - setOnShowListener { - preferences.isEasyAccessSetupDialogShown = true - } - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/setup/easyaccess/Popup.kt b/pluto/src/main/java/com/mocklets/pluto/modules/setup/easyaccess/Popup.kt deleted file mode 100644 index 72c6693b..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/modules/setup/easyaccess/Popup.kt +++ /dev/null @@ -1,51 +0,0 @@ -package com.mocklets.pluto.modules.setup.easyaccess - -import android.app.Service -import android.content.Context -import android.content.Intent -import android.view.WindowManager -import com.mocklets.pluto.R -import com.mocklets.pluto.core.extensions.canDrawOverlays -import com.mocklets.pluto.core.extensions.toast -import com.mocklets.pluto.ui.PlutoActivity - -internal class Popup(context: Context, shouldShowIntroToast: Boolean) { - - private var isIntroToastAlreadyShown = false - - init { - isIntroToastAlreadyShown = !shouldShowIntroToast - } - - private val interactionListener = object : OnPopupInteractionListener { - override fun onClick() { - val intent = Intent(context, PlutoActivity::class.java) - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - context.startActivity(intent) - } - - override fun onLayoutParamsUpdated(params: WindowManager.LayoutParams) { - popupViewManager.view?.parent?.let { - windowManager.updateViewLayout(popupViewManager.view, params) - } - } - } - - private val popupViewManager: PopupViewManager = PopupViewManager(context, interactionListener) - private val windowManager: WindowManager = context.getSystemService(Service.WINDOW_SERVICE) as WindowManager - - internal fun add(context: Context) { - if (context.canDrawOverlays()) { - popupViewManager.addView(context, windowManager) - } else { - if (!isIntroToastAlreadyShown) { - context.toast(context.getString(R.string.pluto___welcome_toast)) - isIntroToastAlreadyShown = true - } - } - } - - internal fun remove() { - popupViewManager.removeView(windowManager) - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/ui/AboutFragment.kt b/pluto/src/main/java/com/mocklets/pluto/ui/AboutFragment.kt deleted file mode 100644 index 2b23d776..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/ui/AboutFragment.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.mocklets.pluto.ui - -import androidx.fragment.app.Fragment -import com.mocklets.pluto.R - -internal class AboutFragment : Fragment(R.layout.pluto___fragment_about) diff --git a/pluto/src/main/java/com/mocklets/pluto/ui/BaseFragment.kt b/pluto/src/main/java/com/mocklets/pluto/ui/BaseFragment.kt deleted file mode 100644 index 022a5b5c..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/ui/BaseFragment.kt +++ /dev/null @@ -1,65 +0,0 @@ -package com.mocklets.pluto.ui - -import android.os.Bundle -import android.view.View -import androidx.annotation.Keep -import androidx.fragment.app.Fragment -import androidx.lifecycle.lifecycleScope -import androidx.viewpager2.widget.ViewPager2 -import com.google.android.material.tabs.TabLayout.MODE_SCROLLABLE -import com.google.android.material.tabs.TabLayoutMediator -import com.mocklets.pluto.Pluto -import com.mocklets.pluto.R -import com.mocklets.pluto.core.binding.viewBinding -import com.mocklets.pluto.core.extensions.delayedLaunchWhenResumed -import com.mocklets.pluto.core.extensions.hideKeyboard -import com.mocklets.pluto.core.ui.routing.RouterAction -import com.mocklets.pluto.core.ui.routing.Screens -import com.mocklets.pluto.core.ui.routing.lazyRouter -import com.mocklets.pluto.core.ui.setDebounceClickListener -import com.mocklets.pluto.databinding.PlutoFragmentBaseBinding - -@Keep -internal class BaseFragment : Fragment(R.layout.pluto___fragment_base) { - - private val binding by viewBinding(PlutoFragmentBaseBinding::bind) - private val router by lazyRouter() - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - setupPager() - binding.close.setDebounceClickListener { router.perform(RouterAction.BackToApp("top_back")) } - binding.settings.setDebounceClickListener(haptic = true) { router.navigate(Screens.Settings) } - binding.appState.setDebounceClickListener(haptic = true) { router.navigate(Screens.AppState) } - } - - private fun setupPager() { - val sectionsPagerAdapter = SectionsPagerAdapter(this) - binding.viewPager.adapter = sectionsPagerAdapter - binding.viewPager.orientation = ViewPager2.ORIENTATION_HORIZONTAL - binding.viewPager.setCurrentItem(Pluto.session.selectTabIndex, false) - binding.viewPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { - override fun onPageScrollStateChanged(state: Int) { - } - - override fun onPageScrolled(position: Int, offset: Float, offsetPixels: Int) { - lifecycleScope.delayedLaunchWhenResumed(SMOOTH_TRANSITION_DELAY) { activity!!.hideKeyboard() } - Pluto.session.selectTabIndex = position - } - - override fun onPageSelected(position: Int) { - } - }) - - TabLayoutMediator(binding.tabs, binding.viewPager) { tab, position -> - tab.text = context?.getString(TAB_TITLES[position]) - }.attach() - binding.tabs.tabMode = MODE_SCROLLABLE - binding.tabs.isInlineLabel = false - } - - private companion object { - const val SMOOTH_TRANSITION_DELAY = 200L - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/ui/PlutoActivity.kt b/pluto/src/main/java/com/mocklets/pluto/ui/PlutoActivity.kt deleted file mode 100644 index 1c278062..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/ui/PlutoActivity.kt +++ /dev/null @@ -1,58 +0,0 @@ -package com.mocklets.pluto.ui - -import android.content.Intent -import android.os.Bundle -import androidx.activity.viewModels -import androidx.annotation.Keep -import androidx.fragment.app.FragmentActivity -import com.mocklets.pluto.Pluto -import com.mocklets.pluto.R -import com.mocklets.pluto.core.extensions.canDrawOverlays -import com.mocklets.pluto.core.extensions.openOverlaySettings -import com.mocklets.pluto.core.preferences.Preferences -import com.mocklets.pluto.core.sharing.ContentShare -import com.mocklets.pluto.core.ui.routing.RouteManager -import com.mocklets.pluto.modules.network.proxy.NetworkProxyViewModel -import com.mocklets.pluto.modules.network.proxy.ui.NetworkProxySettingsFragment.Companion.IN_APP_BROWSER_RESULT_CODE -import com.mocklets.pluto.modules.setup.easyaccess.EasyAccessSetupDialog - -@Keep -internal class PlutoActivity : FragmentActivity(R.layout.pluto___activity_pluto) { - - private lateinit var routeManager: RouteManager - private lateinit var contentShareHelper: ContentShare - private val networkProxyViewModel: NetworkProxyViewModel by viewModels() - private lateinit var preferences: Preferences - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - routeManager = RouteManager(this, R.id.container) - contentShareHelper = ContentShare(this) - preferences = Preferences(this) - } - - override fun onResume() { - super.onResume() - if (!canDrawOverlays() && !preferences.isEasyAccessSetupDialogShown) { - showEasyAccessSetup() - } - } - - override fun onBackPressed() { - if (!routeManager.onBackPressed()) { - super.onBackPressed() - } - } - - private fun showEasyAccessSetup() { - EasyAccessSetupDialog(this) { openOverlaySettings() }.show() - } - - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - super.onActivityResult(requestCode, resultCode, data) - if (requestCode == IN_APP_BROWSER_RESULT_CODE) { - Pluto.activity.customTabClosed() - networkProxyViewModel.onInAppBrowserClose() - } - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/ui/SectionsPagerAdapter.kt b/pluto/src/main/java/com/mocklets/pluto/ui/SectionsPagerAdapter.kt deleted file mode 100644 index 9c3a502a..00000000 --- a/pluto/src/main/java/com/mocklets/pluto/ui/SectionsPagerAdapter.kt +++ /dev/null @@ -1,33 +0,0 @@ -package com.mocklets.pluto.ui - -import androidx.fragment.app.Fragment -import androidx.viewpager2.adapter.FragmentStateAdapter -import com.mocklets.pluto.R -import com.mocklets.pluto.modules.exceptions.ui.CrashesFragment -import com.mocklets.pluto.modules.logging.ui.LogsFragment -import com.mocklets.pluto.modules.network.ui.NetworkFragment -import com.mocklets.pluto.modules.preferences.ui.SharedPrefFragment - -internal val TAB_TITLES = arrayOf( - R.string.pluto___tab_network, - R.string.pluto___tab_logger, - R.string.pluto___tab_crash, - R.string.pluto___tab_shared_pref -) - -internal class SectionsPagerAdapter(fragment: Fragment) : FragmentStateAdapter(fragment) { - - override fun getItemCount(): Int { - return TAB_TITLES.size - } - - override fun createFragment(position: Int): Fragment { - return when (TAB_TITLES[position]) { - R.string.pluto___tab_logger -> LogsFragment() - R.string.pluto___tab_network -> NetworkFragment() - R.string.pluto___tab_crash -> CrashesFragment() - R.string.pluto___tab_shared_pref -> SharedPrefFragment() - else -> NetworkFragment() - } - } -} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/setup/SetupNotification.kt b/pluto/src/main/java/com/pluto/DebugNotification.kt similarity index 58% rename from pluto/src/main/java/com/mocklets/pluto/modules/setup/SetupNotification.kt rename to pluto/src/main/java/com/pluto/DebugNotification.kt index 44abd559..cf8420e6 100644 --- a/pluto/src/main/java/com/mocklets/pluto/modules/setup/SetupNotification.kt +++ b/pluto/src/main/java/com/pluto/DebugNotification.kt @@ -1,24 +1,21 @@ -package com.mocklets.pluto.modules.setup +package com.pluto import android.app.PendingIntent import android.content.Context import android.content.Intent -import android.content.pm.PackageManager import android.os.Build -import com.mocklets.pluto.R -import com.mocklets.pluto.core.notification.NotificationUtil -import com.mocklets.pluto.ui.PlutoActivity +import com.pluto.notification.NotificationUtil +import com.pluto.plugin.PluginSelectorActivity +import com.pluto.plugin.utilities.device.Device -internal class SetupNotification(private val context: Context) { +internal class DebugNotification(private val context: Context) { private val notificationUtil = NotificationUtil(context) - private val clientAppName: String = context.packageManager.getApplicationLabel( - context.packageManager.getApplicationInfo(context.packageName, PackageManager.GET_META_DATA) - ) as String + private val device = Device(context) fun add() { - val notificationIntent = Intent(context, PlutoActivity::class.java) + val notificationIntent = Intent(context, PluginSelectorActivity::class.java) val pendingIntent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { PendingIntent.getActivity(context, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE) } else { @@ -26,7 +23,7 @@ internal class SetupNotification(private val context: Context) { } notificationUtil.notify( - title = context.getString(R.string.pluto___notification_title, clientAppName), + title = context.getString(R.string.pluto___notification_title, device.app.name), text = context.getString(R.string.pluto___notification_subtitle), intent = pendingIntent, isOngoing = false, diff --git a/pluto/src/main/java/com/pluto/Pluto.kt b/pluto/src/main/java/com/pluto/Pluto.kt new file mode 100644 index 00000000..a78bf77b --- /dev/null +++ b/pluto/src/main/java/com/pluto/Pluto.kt @@ -0,0 +1,76 @@ +package com.pluto + +import android.app.Application +import android.content.Intent +import android.os.Bundle +import androidx.lifecycle.LiveData +import com.pluto.applifecycle.AppLifecycle +import com.pluto.applifecycle.AppState +import com.pluto.notch.Notch +import com.pluto.plugin.Plugin +import com.pluto.plugin.PluginHelper.Companion.BUNDLE_LABEL +import com.pluto.plugin.PluginHelper.Companion.ID_LABEL +import com.pluto.plugin.PluginManager +import com.pluto.plugin.PluginSelectorActivity +import com.pluto.plugin.utilities.extensions.toast +import com.pluto.settings.SettingsPreferences +import com.pluto.ui.PlutoActivity + +object Pluto { + + private lateinit var appLifecycle: AppLifecycle + internal var notch: Notch? = null + internal val appState: LiveData + get() = appLifecycle.state + + internal val pluginManager = PluginManager() + private lateinit var application: Application + internal val session = Session() + + private fun init(application: Application, plugins: LinkedHashSet) { + this.application = application + appLifecycle = AppLifecycle() + application.registerActivityLifecycleCallbacks(appLifecycle) + pluginManager.install(application, plugins) + SettingsPreferences.init(application.applicationContext) + notch = Notch(application, appLifecycle.shouldShowNotch) + } + + fun open(identifier: String? = null, bundle: Bundle? = null) { + val intent: Intent? + if (identifier != null) { + pluginManager.get(identifier)?.let { + intent = Intent(application.applicationContext, PlutoActivity::class.java) + intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + intent.putExtra(ID_LABEL, identifier) + intent.putExtra(BUNDLE_LABEL, bundle) + application.applicationContext.startActivity(intent) + return + } + application.applicationContext.toast("Plugin [$identifier] not installed") + } else { + intent = Intent(application.applicationContext, PluginSelectorActivity::class.java) + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + application.applicationContext.startActivity(intent) + } + } + + fun showNotch(state: Boolean) { + notch?.enable(state) + } + + class Installer(private val application: Application) { + + private val plugins = linkedSetOf() + + fun addPlugin(plugin: Plugin): Installer { + plugins.add(plugin) + return this + } + + fun install() { + init(application, plugins) + } + } +} diff --git a/pluto/src/main/java/com/mocklets/pluto/core/PlutoFileProvider.kt b/pluto/src/main/java/com/pluto/PlutoFileProvider.kt similarity index 74% rename from pluto/src/main/java/com/mocklets/pluto/core/PlutoFileProvider.kt rename to pluto/src/main/java/com/pluto/PlutoFileProvider.kt index b82956cd..22ce5030 100644 --- a/pluto/src/main/java/com/mocklets/pluto/core/PlutoFileProvider.kt +++ b/pluto/src/main/java/com/pluto/PlutoFileProvider.kt @@ -1,4 +1,4 @@ -package com.mocklets.pluto.core +package com.pluto import androidx.core.content.FileProvider diff --git a/pluto/src/main/java/com/pluto/Session.kt b/pluto/src/main/java/com/pluto/Session.kt new file mode 100644 index 00000000..0b4d22e7 --- /dev/null +++ b/pluto/src/main/java/com/pluto/Session.kt @@ -0,0 +1,5 @@ +package com.pluto + +internal class Session { + var isConsentAlreadyShown = false +} diff --git a/pluto/src/main/java/com/pluto/applifecycle/AppLifecycle.kt b/pluto/src/main/java/com/pluto/applifecycle/AppLifecycle.kt new file mode 100644 index 00000000..a6569942 --- /dev/null +++ b/pluto/src/main/java/com/pluto/applifecycle/AppLifecycle.kt @@ -0,0 +1,73 @@ +package com.pluto.applifecycle + +import android.app.Activity +import android.app.Application.ActivityLifecycleCallbacks +import android.os.Bundle +import androidx.lifecycle.LiveData +import androidx.lifecycle.MediatorLiveData +import androidx.lifecycle.MutableLiveData +import com.pluto.DebugNotification +import com.pluto.plugin.PluginSelectorActivity + +internal class AppLifecycle : ActivityLifecycleCallbacks { + + internal val state: LiveData + get() = _state + private val _state = MutableLiveData() + + private val showingSelector = MutableLiveData() + + internal val shouldShowNotch: LiveData + get() = _shouldShowNotch + private val _shouldShowNotch = MediatorLiveData() + + private var activityCount = 0 + + init { + _shouldShowNotch.addSource(showingSelector) { + _shouldShowNotch.postValue(getState(state.value, showingSelector.value)) + } + _shouldShowNotch.addSource(state) { + _shouldShowNotch.postValue(getState(state.value, showingSelector.value)) + } + } + + override fun onActivityStarted(activity: Activity) { + activityCount++ + if (activityCount == 1) { + _state.postValue(AppState.Foreground) + DebugNotification(activity.applicationContext).add() + } + if (activity is PluginSelectorActivity) { + showingSelector.postValue(true) + } + } + + override fun onActivityStopped(activity: Activity) { + if (activity is PluginSelectorActivity) { + showingSelector.postValue(false) + } + activityCount-- + if (activityCount == 0) { + _state.postValue(AppState.Background) + DebugNotification(activity.applicationContext).remove() + } + } + + override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {} + override fun onActivityResumed(activity: Activity) {} + override fun onActivityPaused(activity: Activity) {} + override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {} + override fun onActivityDestroyed(activity: Activity) {} + + private fun getState(state: AppState?, showing: Boolean?): Boolean { + val appState = state ?: AppState.Background + val showingSelector = showing ?: false + + return if (appState is AppState.Background) { + false + } else { + !showingSelector + } + } +} diff --git a/pluto/src/main/java/com/pluto/applifecycle/AppState.kt b/pluto/src/main/java/com/pluto/applifecycle/AppState.kt new file mode 100644 index 00000000..49317e0d --- /dev/null +++ b/pluto/src/main/java/com/pluto/applifecycle/AppState.kt @@ -0,0 +1,6 @@ +package com.pluto.applifecycle + +internal sealed class AppState { + object Foreground : AppState() + object Background : AppState() +} diff --git a/pluto/src/main/java/com/pluto/notch/Notch.kt b/pluto/src/main/java/com/pluto/notch/Notch.kt new file mode 100644 index 00000000..ad11f72b --- /dev/null +++ b/pluto/src/main/java/com/pluto/notch/Notch.kt @@ -0,0 +1,61 @@ +package com.pluto.notch + +import android.app.Application +import android.app.Service +import android.view.WindowManager +import androidx.lifecycle.LiveData +import com.pluto.Pluto +import com.pluto.applifecycle.AppState +import com.pluto.settings.canDrawOverlays + +internal class Notch(private val application: Application, shouldShowNotch: LiveData) { + + init { + shouldShowNotch.observeForever { + if (it) { + add() + } else { + remove() + } + } + } + + private val interactionListener = object : OnNotchInteractionListener { + override fun onClick() { + Pluto.open() + } + + override fun onLayoutParamsUpdated(params: WindowManager.LayoutParams) { + notchViewManager.view?.parent?.let { + windowManager.updateViewLayout(notchViewManager.view, params) + } + } + } + + internal var enabled = true + private set + private val notchViewManager: NotchViewManager = NotchViewManager(application.applicationContext, interactionListener) + private val windowManager: WindowManager = application.applicationContext.getSystemService(Service.WINDOW_SERVICE) as WindowManager + + private fun add() { + if (enabled) { + val context = application.applicationContext + if (context.canDrawOverlays()) { + notchViewManager.addView(context, windowManager) + } + } + } + + private fun remove() { + notchViewManager.removeView(windowManager) + } + + internal fun enable(state: Boolean) { + enabled = state + if (enabled && Pluto.appState.value is AppState.Foreground) { + add() + } else { + remove() + } + } +} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/setup/easyaccess/PopupViewManager.kt b/pluto/src/main/java/com/pluto/notch/NotchViewManager.kt similarity index 72% rename from pluto/src/main/java/com/mocklets/pluto/modules/setup/easyaccess/PopupViewManager.kt rename to pluto/src/main/java/com/pluto/notch/NotchViewManager.kt index d941c229..de37039d 100644 --- a/pluto/src/main/java/com/mocklets/pluto/modules/setup/easyaccess/PopupViewManager.kt +++ b/pluto/src/main/java/com/pluto/notch/NotchViewManager.kt @@ -1,4 +1,4 @@ -package com.mocklets.pluto.modules.setup.easyaccess +package com.pluto.notch import android.content.Context import android.graphics.PixelFormat @@ -7,23 +7,23 @@ import android.view.Gravity import android.view.MotionEvent import android.view.View import android.view.WindowManager -import com.mocklets.pluto.R -import com.mocklets.pluto.core.DeviceInfo -import com.mocklets.pluto.core.extensions.color -import com.mocklets.pluto.core.extensions.inflate -import com.mocklets.pluto.core.preferences.Preferences -import com.mocklets.pluto.core.ui.hapticFeedback -import com.mocklets.pluto.core.ui.soundFeedback -import com.mocklets.pluto.databinding.PlutoLayoutPopupBinding +import com.pluto.R +import com.pluto.databinding.PlutoLayoutNotchBinding +import com.pluto.plugin.utilities.device.Device +import com.pluto.plugin.utilities.extensions.color +import com.pluto.plugin.utilities.extensions.inflate +import com.pluto.plugin.utilities.hapticFeedback +import com.pluto.plugin.utilities.soundFeedback +import com.pluto.settings.SettingsPreferences import kotlin.math.abs -internal class PopupViewManager( +internal class NotchViewManager( context: Context, - private val listener: OnPopupInteractionListener + private val listener: OnNotchInteractionListener ) { - private val deviceInfo = DeviceInfo(context) - private val dragUpLimit = deviceInfo.height * DRAG_UP_THRESHOLD - private val dragDownLimit = deviceInfo.height * DRAG_DOWN_THRESHOLD + private val device = Device(context) + private val dragUpLimit = device.screen.heightPx * DRAG_UP_THRESHOLD + private val dragDownLimit = device.screen.heightPx * DRAG_DOWN_THRESHOLD var view: View? = null val layoutParams = getInitialLayoutParams(context) @@ -83,13 +83,23 @@ internal class PopupViewManager( view.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener { override fun onViewAttachedToWindow(v: View?) { v?.let { - val binding = PlutoLayoutPopupBinding.bind(it) - binding.card.setCardBackgroundColor( - context.color(if (Preferences(context).isDarkAccessPopup) R.color.pluto___dark else R.color.pluto___app_bg) - ) + PlutoLayoutNotchBinding.bind(it).apply { + card.setCardBackgroundColor( + context.color(if (SettingsPreferences.isDarkAccessPopup) R.color.pluto___notch_bg_dark else R.color.pluto___notch_bg_light) + ) + left.setTextColor( + context.color(if (SettingsPreferences.isDarkAccessPopup) R.color.pluto___white_80 else R.color.pluto___text_dark_80) + ) + right.setTextColor( + context.color(if (SettingsPreferences.isDarkAccessPopup) R.color.pluto___white_80 else R.color.pluto___text_dark_80) + ) + bottom.setBackgroundColor( + context.color(if (SettingsPreferences.isDarkAccessPopup) R.color.pluto___notch_accent_dark else R.color.pluto___notch_accent_light) + ) + } } val gravityHorizontal = - if (Preferences(context).isRightHandedAccessPopup) Gravity.END else Gravity.START + if (SettingsPreferences.isRightHandedAccessPopup) Gravity.END else Gravity.START layoutParams.gravity = gravityHorizontal or Gravity.TOP listener.onLayoutParamsUpdated(layoutParams) } @@ -125,17 +135,17 @@ internal class PopupViewManager( ) } - val gravityHorizontal = if (Preferences(context).isRightHandedAccessPopup) Gravity.END else Gravity.START + val gravityHorizontal = if (SettingsPreferences.isRightHandedAccessPopup) Gravity.END else Gravity.START params.gravity = gravityHorizontal or Gravity.TOP params.x = (context.resources.getDimension(R.dimen.pluto___popup_bubble_width) * INIT_THRESHOLD_X).toInt() - params.y = (deviceInfo.height * INIT_THRESHOLD_Y).toInt() + params.y = (device.screen.heightPx * INIT_THRESHOLD_Y).toInt() return params } fun addView(context: Context, windowManager: WindowManager) { if (view == null) { - view = context.inflate(R.layout.pluto___layout_popup) + view = context.inflate(R.layout.pluto___layout_notch) view?.let { initView(context, it) if (it.parent == null) { @@ -155,7 +165,7 @@ internal class PopupViewManager( companion object { const val DRAG_UP_THRESHOLD = 0.03 const val DRAG_DOWN_THRESHOLD = 0.9 - const val INIT_THRESHOLD_X = -0.75 + const val INIT_THRESHOLD_X = -0.55 const val INIT_THRESHOLD_Y = 0.65 } } diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/setup/easyaccess/OnPopupInteractionListener.kt b/pluto/src/main/java/com/pluto/notch/OnNotchInteractionListener.kt similarity index 54% rename from pluto/src/main/java/com/mocklets/pluto/modules/setup/easyaccess/OnPopupInteractionListener.kt rename to pluto/src/main/java/com/pluto/notch/OnNotchInteractionListener.kt index 3742ad68..ffdb340f 100644 --- a/pluto/src/main/java/com/mocklets/pluto/modules/setup/easyaccess/OnPopupInteractionListener.kt +++ b/pluto/src/main/java/com/pluto/notch/OnNotchInteractionListener.kt @@ -1,8 +1,8 @@ -package com.mocklets.pluto.modules.setup.easyaccess +package com.pluto.notch import android.view.WindowManager -internal interface OnPopupInteractionListener { +internal interface OnNotchInteractionListener { fun onClick() fun onLayoutParamsUpdated(params: WindowManager.LayoutParams) } diff --git a/pluto/src/main/java/com/mocklets/pluto/core/notification/NotificationUtil.kt b/pluto/src/main/java/com/pluto/notification/NotificationUtil.kt similarity index 96% rename from pluto/src/main/java/com/mocklets/pluto/core/notification/NotificationUtil.kt rename to pluto/src/main/java/com/pluto/notification/NotificationUtil.kt index 122473df..bd70c7e8 100644 --- a/pluto/src/main/java/com/mocklets/pluto/core/notification/NotificationUtil.kt +++ b/pluto/src/main/java/com/pluto/notification/NotificationUtil.kt @@ -1,4 +1,4 @@ -package com.mocklets.pluto.core.notification +package com.pluto.notification import android.app.Notification import android.app.NotificationChannel @@ -7,7 +7,7 @@ import android.app.PendingIntent import android.content.Context import android.os.Build import androidx.core.app.NotificationCompat -import com.mocklets.pluto.R +import com.pluto.R internal class NotificationUtil(private val context: Context) { diff --git a/pluto/src/main/java/com/pluto/plugin/PluginManager.kt b/pluto/src/main/java/com/pluto/plugin/PluginManager.kt new file mode 100644 index 00000000..9b5bd482 --- /dev/null +++ b/pluto/src/main/java/com/pluto/plugin/PluginManager.kt @@ -0,0 +1,38 @@ +package com.pluto.plugin + +import android.app.Application +import com.pluto.plugin.utilities.DebugLog +import com.pluto.ui.PlutoActivity + +internal class PluginManager { + + private var plugins: LinkedHashSet = linkedSetOf() + internal val installedPlugins: List + get() { + val list = arrayListOf() + list.addAll(plugins) + return list + } + + fun install(application: Application, plugins: LinkedHashSet) { + PluginUiBridge.create( + UiBridgeComponents( + activityClass = PlutoActivity::class.java + ) + ) + plugins.forEach { + if (it.shouldInstallPlugin()) { + it.install(application) + this.plugins.add(it) + } else { + DebugLog.e("pluto_plugin", "${it.getConfig().name} not installed (reason: condition mismatch).") + } + } + } + + fun get(identifier: String): Plugin? { + return plugins.firstOrNull { + it.devIdentifier == identifier + } + } +} diff --git a/pluto/src/main/java/com/pluto/plugin/PluginSelectorActivity.kt b/pluto/src/main/java/com/pluto/plugin/PluginSelectorActivity.kt new file mode 100644 index 00000000..49e82542 --- /dev/null +++ b/pluto/src/main/java/com/pluto/plugin/PluginSelectorActivity.kt @@ -0,0 +1,88 @@ +package com.pluto.plugin + +import android.os.Bundle +import androidx.activity.viewModels +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.Observer +import androidx.recyclerview.widget.GridLayoutManager +import com.pluto.Pluto +import com.pluto.R +import com.pluto.applifecycle.AppState +import com.pluto.databinding.PlutoActivityPluginSelectorBinding +import com.pluto.plugin.list.PluginAdapter +import com.pluto.plugin.utilities.extensions.color +import com.pluto.plugin.utilities.list.BaseAdapter +import com.pluto.plugin.utilities.list.DiffAwareAdapter +import com.pluto.plugin.utilities.list.DiffAwareHolder +import com.pluto.plugin.utilities.list.ListItem +import com.pluto.plugin.utilities.setDebounceClickListener +import com.pluto.plugin.utilities.spannable.setSpan +import com.pluto.settings.SettingsFragment +import com.pluto.settings.SettingsViewModel + +class PluginSelectorActivity : AppCompatActivity() { + + private val pluginsViewModel by viewModels() + private val pluginAdapter: BaseAdapter by lazy { PluginAdapter(onActionListener) } + private val settingsViewModel by viewModels() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + val binding = PlutoActivityPluginSelectorBinding.inflate(layoutInflater) + setContentView(binding.root) + overridePendingTransition(R.anim.pluto___slide_in_bottom, R.anim.pluto___slide_out_bottom) + + binding.list.apply { + adapter = pluginAdapter + layoutManager = GridLayoutManager(context, GRID_SPAN_COUNT) + } + + binding.root.setDebounceClickListener { + finish() + } + + binding.version.setSpan { + append(fontColor(light("v"), context.color(R.color.pluto___white_40))) + append(regular(com.pluto.BuildConfig.VERSION_NAME)) + } + + binding.settings.setDebounceClickListener { + SettingsFragment().show(supportFragmentManager, "settings") + } + + Pluto.appState.removeObserver(appStateListener) + Pluto.appState.observe(this, appStateListener) + + pluginsViewModel.plugins.removeObserver(pluginListObserver) + pluginsViewModel.plugins.observe(this, pluginListObserver) + + settingsViewModel.resetAll.observe(this) { + Pluto.pluginManager.installedPlugins.forEach { + it.onPluginDataCleared() + } + } + } + + private val pluginListObserver = Observer> { + pluginAdapter.list = it + } + + private val appStateListener = Observer { + if (it is AppState.Background) { + finish() + } + } + + private val onActionListener = object : DiffAwareAdapter.OnActionListener { + override fun onAction(action: String, data: ListItem, holder: DiffAwareHolder?) { + if (data is Plugin) { + Pluto.open(data.devIdentifier) + finish() + } + } + } + + private companion object { + const val GRID_SPAN_COUNT = 4 + } +} diff --git a/pluto/src/main/java/com/pluto/plugin/PluginsViewModel.kt b/pluto/src/main/java/com/pluto/plugin/PluginsViewModel.kt new file mode 100644 index 00000000..26184782 --- /dev/null +++ b/pluto/src/main/java/com/pluto/plugin/PluginsViewModel.kt @@ -0,0 +1,18 @@ +package com.pluto.plugin + +import android.app.Application +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import com.pluto.Pluto + +internal class PluginsViewModel(application: Application) : AndroidViewModel(application) { + + val plugins: LiveData> + get() = _plugins + private val _plugins = MutableLiveData>() + + init { + _plugins.postValue(Pluto.pluginManager.installedPlugins) + } +} diff --git a/pluto/src/main/java/com/pluto/plugin/list/PluginAdapter.kt b/pluto/src/main/java/com/pluto/plugin/list/PluginAdapter.kt new file mode 100644 index 00000000..1023a349 --- /dev/null +++ b/pluto/src/main/java/com/pluto/plugin/list/PluginAdapter.kt @@ -0,0 +1,27 @@ +package com.pluto.plugin.list + +import android.view.ViewGroup +import com.pluto.plugin.Plugin +import com.pluto.plugin.utilities.list.BaseAdapter +import com.pluto.plugin.utilities.list.DiffAwareHolder +import com.pluto.plugin.utilities.list.ListItem + +internal class PluginAdapter(private val listener: OnActionListener) : BaseAdapter() { + override fun getItemViewType(item: ListItem): Int? { + return when (item) { + is Plugin -> ITEM_TYPE_PLUGIN + else -> null + } + } + + override fun onViewHolderCreated(parent: ViewGroup, viewType: Int): DiffAwareHolder? { + return when (viewType) { + ITEM_TYPE_PLUGIN -> PluginItemHolder(parent, listener) + else -> null + } + } + + companion object { + const val ITEM_TYPE_PLUGIN = 1000 + } +} diff --git a/pluto/src/main/java/com/pluto/plugin/list/PluginItemHolder.kt b/pluto/src/main/java/com/pluto/plugin/list/PluginItemHolder.kt new file mode 100644 index 00000000..43889a99 --- /dev/null +++ b/pluto/src/main/java/com/pluto/plugin/list/PluginItemHolder.kt @@ -0,0 +1,53 @@ +package com.pluto.plugin.list + +import android.content.Context +import android.view.ViewGroup +import android.view.animation.Animation +import android.view.animation.AnimationUtils +import android.view.animation.OvershootInterpolator +import androidx.annotation.AnimRes +import com.pluto.R +import com.pluto.databinding.PlutoItemPluginBinding +import com.pluto.plugin.Plugin +import com.pluto.plugin.utilities.extensions.inflate +import com.pluto.plugin.utilities.extensions.setListener +import com.pluto.plugin.utilities.list.DiffAwareAdapter +import com.pluto.plugin.utilities.list.DiffAwareHolder +import com.pluto.plugin.utilities.list.ListItem +import com.pluto.plugin.utilities.setDebounceClickListener + +internal class PluginItemHolder(parent: ViewGroup, actionListener: DiffAwareAdapter.OnActionListener) : + DiffAwareHolder(parent.inflate(R.layout.pluto___item_plugin), actionListener) { + + private val binding = PlutoItemPluginBinding.bind(itemView) + private val name = binding.name + private val icon = binding.icon + + override fun onBind(item: ListItem) { + if (item is Plugin) { + icon.setImageResource(item.getConfig().icon) + name.text = item.getConfig().name + binding.root.setDebounceClickListener(haptic = true) { + val scale = context.loadAnimation(R.anim.pluto___click_bounce) + scale.duration = ANIMATION_DURATION + scale.interpolator = OvershootInterpolator() + scale.setListener { + onAnimationStart { + } + onAnimationEnd { + onAction("click") + } + } + it.startAnimation(scale) + } + } + } + + private companion object { + const val ANIMATION_DURATION = 250L + } +} + +fun Context.loadAnimation(@AnimRes id: Int): Animation { + return AnimationUtils.loadAnimation(this, id) +} diff --git a/pluto/src/main/java/com/pluto/plugin/options/PluginOptionAdapter.kt b/pluto/src/main/java/com/pluto/plugin/options/PluginOptionAdapter.kt new file mode 100644 index 00000000..7b3207c2 --- /dev/null +++ b/pluto/src/main/java/com/pluto/plugin/options/PluginOptionAdapter.kt @@ -0,0 +1,27 @@ +package com.pluto.plugin.options + +import android.view.ViewGroup +import com.pluto.plugin.PluginOption +import com.pluto.plugin.utilities.list.BaseAdapter +import com.pluto.plugin.utilities.list.DiffAwareHolder +import com.pluto.plugin.utilities.list.ListItem + +internal class PluginOptionAdapter(private val listener: OnActionListener) : BaseAdapter() { + override fun getItemViewType(item: ListItem): Int? { + return when (item) { + is PluginOption -> ITEM_TYPE_PLUGIN_OPTION + else -> null + } + } + + override fun onViewHolderCreated(parent: ViewGroup, viewType: Int): DiffAwareHolder? { + return when (viewType) { + ITEM_TYPE_PLUGIN_OPTION -> PluginOptionItemHolder(parent, listener) + else -> null + } + } + + companion object { + const val ITEM_TYPE_PLUGIN_OPTION = 1000 + } +} diff --git a/pluto/src/main/java/com/pluto/plugin/options/PluginOptionItemHolder.kt b/pluto/src/main/java/com/pluto/plugin/options/PluginOptionItemHolder.kt new file mode 100644 index 00000000..820843f3 --- /dev/null +++ b/pluto/src/main/java/com/pluto/plugin/options/PluginOptionItemHolder.kt @@ -0,0 +1,29 @@ +package com.pluto.plugin.options + +import android.view.ViewGroup +import com.pluto.R +import com.pluto.databinding.PlutoItemPluginOptionBinding +import com.pluto.plugin.PluginOption +import com.pluto.plugin.utilities.extensions.inflate +import com.pluto.plugin.utilities.list.DiffAwareAdapter +import com.pluto.plugin.utilities.list.DiffAwareHolder +import com.pluto.plugin.utilities.list.ListItem +import com.pluto.plugin.utilities.setDebounceClickListener + +internal class PluginOptionItemHolder(parent: ViewGroup, actionListener: DiffAwareAdapter.OnActionListener) : + DiffAwareHolder(parent.inflate(R.layout.pluto___item_plugin_option), actionListener) { + + private val binding = PlutoItemPluginOptionBinding.bind(itemView) + private val label = binding.label + private val icon = binding.icon + + override fun onBind(item: ListItem) { + if (item is PluginOption) { + icon.setImageResource(item.icon) + label.text = item.label + binding.root.setDebounceClickListener { + onAction("click") + } + } + } +} diff --git a/pluto/src/main/java/com/pluto/settings/DataModel.kt b/pluto/src/main/java/com/pluto/settings/DataModel.kt new file mode 100644 index 00000000..19237669 --- /dev/null +++ b/pluto/src/main/java/com/pluto/settings/DataModel.kt @@ -0,0 +1,15 @@ +package com.pluto.settings + +import com.pluto.plugin.utilities.list.ListItem + +internal data class SettingsEasyAccessEntity( + val label: String = "easy_access" +) : ListItem() + +internal data class SettingsEasyAccessPopupAppearanceEntity( + val type: String +) : ListItem() + +internal data class SettingsResetAllEntity( + val type: String = "rest all" +) : ListItem() diff --git a/pluto/src/main/java/com/pluto/settings/OverConsentFragment.kt b/pluto/src/main/java/com/pluto/settings/OverConsentFragment.kt new file mode 100644 index 00000000..ae65a150 --- /dev/null +++ b/pluto/src/main/java/com/pluto/settings/OverConsentFragment.kt @@ -0,0 +1,30 @@ +package com.pluto.settings + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.google.android.material.bottomsheet.BottomSheetDialogFragment +import com.pluto.Pluto +import com.pluto.R +import com.pluto.databinding.PlutoFragmentOverlayConsentBinding +import com.pluto.plugin.utilities.setDebounceClickListener +import com.pluto.plugin.utilities.viewBinding + +internal class OverConsentFragment : BottomSheetDialogFragment() { + + private val binding by viewBinding(PlutoFragmentOverlayConsentBinding::bind) + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = + inflater.inflate(R.layout.pluto___fragment_overlay_consent, container, false) + + override fun getTheme(): Int = R.style.PlutoBottomSheetDialogTheme + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + Pluto.session.isConsentAlreadyShown = true + binding.cta.setDebounceClickListener { + context?.openOverlaySettings() + } + } +} diff --git a/pluto/src/main/java/com/pluto/settings/SettingsAdapter.kt b/pluto/src/main/java/com/pluto/settings/SettingsAdapter.kt new file mode 100644 index 00000000..06a56805 --- /dev/null +++ b/pluto/src/main/java/com/pluto/settings/SettingsAdapter.kt @@ -0,0 +1,35 @@ +package com.pluto.settings + +import android.view.ViewGroup +import com.pluto.plugin.utilities.list.BaseAdapter +import com.pluto.plugin.utilities.list.DiffAwareHolder +import com.pluto.plugin.utilities.list.ListItem +import com.pluto.settings.holders.SettingsEasyAccessHolder +import com.pluto.settings.holders.SettingsEasyAccessPopupAppearanceHolder +import com.pluto.settings.holders.SettingsResetAllHolder + +internal class SettingsAdapter(private val listener: OnActionListener) : BaseAdapter() { + override fun getItemViewType(item: ListItem): Int? { + return when (item) { + is SettingsEasyAccessEntity -> ITEM_TYPE_EASY_ACCESS + is SettingsEasyAccessPopupAppearanceEntity -> ITEM_TYPE_EASY_ACCESS_APPEARANCE + is SettingsResetAllEntity -> ITEM_TYPE_RESET_ALL + else -> null + } + } + + override fun onViewHolderCreated(parent: ViewGroup, viewType: Int): DiffAwareHolder? { + return when (viewType) { + ITEM_TYPE_EASY_ACCESS -> SettingsEasyAccessHolder(parent, listener) + ITEM_TYPE_EASY_ACCESS_APPEARANCE -> SettingsEasyAccessPopupAppearanceHolder(parent, listener) + ITEM_TYPE_RESET_ALL -> SettingsResetAllHolder(parent, listener) + else -> null + } + } + + companion object { + const val ITEM_TYPE_EASY_ACCESS = 1000 + const val ITEM_TYPE_EASY_ACCESS_APPEARANCE = 1001 + const val ITEM_TYPE_RESET_ALL = 1002 + } +} diff --git a/pluto/src/main/java/com/pluto/settings/SettingsFragment.kt b/pluto/src/main/java/com/pluto/settings/SettingsFragment.kt new file mode 100644 index 00000000..07189eec --- /dev/null +++ b/pluto/src/main/java/com/pluto/settings/SettingsFragment.kt @@ -0,0 +1,82 @@ +package com.pluto.settings + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.activityViewModels +import androidx.lifecycle.Observer +import com.google.android.material.bottomsheet.BottomSheetDialogFragment +import com.pluto.BuildConfig +import com.pluto.R +import com.pluto.databinding.PlutoFragmentSettingsBinding +import com.pluto.plugin.utilities.extensions.dp +import com.pluto.plugin.utilities.extensions.toast +import com.pluto.plugin.utilities.list.BaseAdapter +import com.pluto.plugin.utilities.list.CustomItemDecorator +import com.pluto.plugin.utilities.list.DiffAwareAdapter +import com.pluto.plugin.utilities.list.DiffAwareHolder +import com.pluto.plugin.utilities.list.ListItem +import com.pluto.plugin.utilities.viewBinding + +internal class SettingsFragment : BottomSheetDialogFragment() { + + private val binding by viewBinding(PlutoFragmentSettingsBinding::bind) + private val settingsAdapter: BaseAdapter by lazy { SettingsAdapter(onActionListener) } + private val viewModel: SettingsViewModel by activityViewModels() + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = + inflater.inflate(R.layout.pluto___fragment_settings, container, false) + + override fun getTheme(): Int = R.style.PlutoBottomSheetDialogTheme + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.list.apply { + adapter = settingsAdapter + addItemDecoration(CustomItemDecorator(context, DECORATOR_DIVIDER_PADDING)) + } + viewModel.list.removeObserver(settingsObserver) + viewModel.list.observe(viewLifecycleOwner, settingsObserver) + } + + private val settingsObserver = Observer> { + settingsAdapter.list = it + } + + private val onActionListener = object : DiffAwareAdapter.OnActionListener { + override fun onAction(action: String, data: ListItem, holder: DiffAwareHolder?) { + when (data) { + is SettingsEasyAccessEntity -> context?.openOverlaySettings() + is SettingsEasyAccessPopupAppearanceEntity -> { + when (data.type) { + "mode" -> { + val current = SettingsPreferences.isDarkAccessPopup + SettingsPreferences.isDarkAccessPopup = !current + context?.toast(context!!.getString(R.string.pluto___notch_settings_updated)) + } + "handed" -> { + val current = SettingsPreferences.isRightHandedAccessPopup + SettingsPreferences.isRightHandedAccessPopup = !current + context?.toast(context!!.getString(R.string.pluto___notch_settings_updated)) + } + else -> { + check(!BuildConfig.DEBUG) { + "unsupported appearance type" + } + } + } + settingsAdapter.notifyItemChanged(holder?.adapterPosition ?: 0) + } + is SettingsResetAllEntity -> { + viewModel.resetAll() + context?.toast(context!!.getString(R.string.pluto___reset_all_requested)) + } + } + } + } + + private companion object { + val DECORATOR_DIVIDER_PADDING = 16f.dp.toInt() + } +} diff --git a/pluto/src/main/java/com/mocklets/pluto/core/extensions/SettingsKtx.kt b/pluto/src/main/java/com/pluto/settings/SettingsKtx.kt similarity index 93% rename from pluto/src/main/java/com/mocklets/pluto/core/extensions/SettingsKtx.kt rename to pluto/src/main/java/com/pluto/settings/SettingsKtx.kt index f2d2615c..7cb56ad3 100644 --- a/pluto/src/main/java/com/mocklets/pluto/core/extensions/SettingsKtx.kt +++ b/pluto/src/main/java/com/pluto/settings/SettingsKtx.kt @@ -1,4 +1,4 @@ -package com.mocklets.pluto.core.extensions +package com.pluto.settings import android.content.Context import android.content.Intent diff --git a/pluto/src/main/java/com/pluto/settings/SettingsPreferences.kt b/pluto/src/main/java/com/pluto/settings/SettingsPreferences.kt new file mode 100644 index 00000000..83879d14 --- /dev/null +++ b/pluto/src/main/java/com/pluto/settings/SettingsPreferences.kt @@ -0,0 +1,32 @@ +package com.pluto.settings + +import android.content.Context +import android.content.SharedPreferences + +internal object SettingsPreferences { + + private val settingsPrefs: SharedPreferences + get() = returnContext() + private var _settingsPrefs: SharedPreferences? = null + private fun returnContext(): SharedPreferences { + _settingsPrefs?.let { return it } + throw IllegalStateException("Settings preferences is not initialised yet.") + } + + fun init(context: Context) { + this._settingsPrefs = context.preferences("settings") + } + + internal var isDarkAccessPopup: Boolean + get() = settingsPrefs.getBoolean(IS_DARK_ACCESS_POPUP, true) + set(value) = settingsPrefs.edit().putBoolean(IS_DARK_ACCESS_POPUP, value).apply() + + internal var isRightHandedAccessPopup: Boolean + get() = settingsPrefs.getBoolean(IS_RIGHT_HANDED_ACCESS_POPUP, true) + set(value) = settingsPrefs.edit().putBoolean(IS_RIGHT_HANDED_ACCESS_POPUP, value).apply() + + private const val IS_DARK_ACCESS_POPUP = "is_dark_access_popup" + private const val IS_RIGHT_HANDED_ACCESS_POPUP = "is_right_handed_access_popup" +} + +private fun Context.preferences(name: String, mode: Int = Context.MODE_PRIVATE) = getSharedPreferences(name, mode) diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/settings/SettingsViewModel.kt b/pluto/src/main/java/com/pluto/settings/SettingsViewModel.kt similarity index 56% rename from pluto/src/main/java/com/mocklets/pluto/modules/settings/SettingsViewModel.kt rename to pluto/src/main/java/com/pluto/settings/SettingsViewModel.kt index a35a54a4..8c8f6ad0 100644 --- a/pluto/src/main/java/com/mocklets/pluto/modules/settings/SettingsViewModel.kt +++ b/pluto/src/main/java/com/pluto/settings/SettingsViewModel.kt @@ -1,4 +1,4 @@ -package com.mocklets.pluto.modules.settings +package com.pluto.settings import android.app.Application import android.content.Context @@ -6,13 +6,8 @@ import android.os.Build import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.viewModelScope -import com.mocklets.pluto.core.ui.list.ListItem -import com.mocklets.pluto.modules.logging.LogsRepo -import com.mocklets.pluto.modules.network.NetworkCallsRepo -import com.mocklets.pluto.modules.network.proxy.NetworkProxyRepo -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch +import com.pluto.plugin.utilities.SingleLiveEvent +import com.pluto.plugin.utilities.list.ListItem internal class SettingsViewModel(application: Application) : AndroidViewModel(application) { @@ -20,7 +15,13 @@ internal class SettingsViewModel(application: Application) : AndroidViewModel(ap get() = _list private val _list = MutableLiveData>() - fun generate(context: Context?) { + val resetAll = SingleLiveEvent() + + init { + generate(getApplication()) + } + + private fun generate(context: Context?) { context?.apply { val list = arrayListOf() @@ -30,20 +31,12 @@ internal class SettingsViewModel(application: Application) : AndroidViewModel(ap } list.add(SettingsEasyAccessPopupAppearanceEntity("mode")) list.add(SettingsEasyAccessPopupAppearanceEntity("handed")) - -// list.add(SettingsLinkMockletsEntity()) todo enable after Mocklets is integrated - - list.add(SettingsSharedPrefEntity()) list.add(SettingsResetAllEntity()) _list.postValue(list) } } fun resetAll() { - viewModelScope.launch(Dispatchers.IO) { - NetworkCallsRepo.deleteAll() - LogsRepo.deleteAll() - NetworkProxyRepo.deleteAll() - } + resetAll.postValue(true) } } diff --git a/pluto/src/main/java/com/pluto/settings/holders/SettingsEasyAccessHolder.kt b/pluto/src/main/java/com/pluto/settings/holders/SettingsEasyAccessHolder.kt new file mode 100644 index 00000000..4e0b747c --- /dev/null +++ b/pluto/src/main/java/com/pluto/settings/holders/SettingsEasyAccessHolder.kt @@ -0,0 +1,28 @@ +package com.pluto.settings.holders + +import android.view.ViewGroup +import com.pluto.R +import com.pluto.databinding.PlutoItemSettingsEasyAccessBinding +import com.pluto.plugin.utilities.extensions.inflate +import com.pluto.plugin.utilities.list.DiffAwareAdapter +import com.pluto.plugin.utilities.list.DiffAwareHolder +import com.pluto.plugin.utilities.list.ListItem +import com.pluto.plugin.utilities.setDebounceClickListener +import com.pluto.settings.SettingsEasyAccessEntity +import com.pluto.settings.canDrawOverlays + +internal class SettingsEasyAccessHolder(parent: ViewGroup, listener: DiffAwareAdapter.OnActionListener) : + DiffAwareHolder(parent.inflate(R.layout.pluto___item_settings_easy_access), listener) { + + private val binding = PlutoItemSettingsEasyAccessBinding.bind(itemView) + private val checkbox = binding.checkbox + + override fun onBind(item: ListItem) { + if (item is SettingsEasyAccessEntity) { + checkbox.isSelected = itemView.context.canDrawOverlays() + itemView.setDebounceClickListener { + onAction("click") + } + } + } +} diff --git a/pluto/src/main/java/com/mocklets/pluto/modules/settings/holders/SettingsEasyAccessPopupAppearanceHolder.kt b/pluto/src/main/java/com/pluto/settings/holders/SettingsEasyAccessPopupAppearanceHolder.kt similarity index 56% rename from pluto/src/main/java/com/mocklets/pluto/modules/settings/holders/SettingsEasyAccessPopupAppearanceHolder.kt rename to pluto/src/main/java/com/pluto/settings/holders/SettingsEasyAccessPopupAppearanceHolder.kt index 3f9bba26..fb94b23f 100644 --- a/pluto/src/main/java/com/mocklets/pluto/modules/settings/holders/SettingsEasyAccessPopupAppearanceHolder.kt +++ b/pluto/src/main/java/com/pluto/settings/holders/SettingsEasyAccessPopupAppearanceHolder.kt @@ -1,23 +1,21 @@ -package com.mocklets.pluto.modules.settings.holders +package com.pluto.settings.holders import android.view.View.GONE import android.view.View.VISIBLE import android.view.ViewGroup -import com.mocklets.pluto.R -import com.mocklets.pluto.core.extensions.canDrawOverlays -import com.mocklets.pluto.core.extensions.inflate -import com.mocklets.pluto.core.preferences.Preferences -import com.mocklets.pluto.core.ui.list.DiffAwareAdapter -import com.mocklets.pluto.core.ui.list.DiffAwareHolder -import com.mocklets.pluto.core.ui.list.ListItem -import com.mocklets.pluto.core.ui.setDebounceClickListener -import com.mocklets.pluto.databinding.PlutoItemSettingsEasyAccessAppearanceBinding -import com.mocklets.pluto.modules.settings.SettingsEasyAccessPopupAppearanceEntity +import com.pluto.R +import com.pluto.databinding.PlutoItemSettingsEasyAccessAppearanceBinding +import com.pluto.plugin.utilities.extensions.inflate +import com.pluto.plugin.utilities.list.DiffAwareAdapter +import com.pluto.plugin.utilities.list.DiffAwareHolder +import com.pluto.plugin.utilities.list.ListItem +import com.pluto.plugin.utilities.setDebounceClickListener +import com.pluto.settings.SettingsEasyAccessPopupAppearanceEntity +import com.pluto.settings.SettingsPreferences +import com.pluto.settings.canDrawOverlays -internal class SettingsEasyAccessPopupAppearanceHolder( - parent: ViewGroup, - listener: DiffAwareAdapter.OnActionListener -) : DiffAwareHolder(parent.inflate(R.layout.pluto___item_settings_easy_access_appearance), listener) { +internal class SettingsEasyAccessPopupAppearanceHolder(parent: ViewGroup, listener: DiffAwareAdapter.OnActionListener) : + DiffAwareHolder(parent.inflate(R.layout.pluto___item_settings_easy_access_appearance), listener) { private val binding = PlutoItemSettingsEasyAccessAppearanceBinding.bind(itemView) private val title = binding.title @@ -36,8 +34,8 @@ internal class SettingsEasyAccessPopupAppearanceHolder( ) checkbox.isSelected = when (item.type) { - "mode" -> Preferences(context).isDarkAccessPopup - "handed" -> Preferences(context).isRightHandedAccessPopup + "mode" -> SettingsPreferences.isDarkAccessPopup + "handed" -> SettingsPreferences.isRightHandedAccessPopup else -> error("unsupported appearance type") } diff --git a/pluto/src/main/java/com/pluto/settings/holders/SettingsResetAllHolder.kt b/pluto/src/main/java/com/pluto/settings/holders/SettingsResetAllHolder.kt new file mode 100644 index 00000000..11ee7388 --- /dev/null +++ b/pluto/src/main/java/com/pluto/settings/holders/SettingsResetAllHolder.kt @@ -0,0 +1,25 @@ +package com.pluto.settings.holders + +import android.view.ViewGroup +import com.pluto.R +import com.pluto.databinding.PlutoItemSettingsResetAllBinding +import com.pluto.plugin.utilities.extensions.inflate +import com.pluto.plugin.utilities.list.DiffAwareAdapter +import com.pluto.plugin.utilities.list.DiffAwareHolder +import com.pluto.plugin.utilities.list.ListItem +import com.pluto.plugin.utilities.setDebounceClickListener +import com.pluto.settings.SettingsResetAllEntity + +internal class SettingsResetAllHolder(parent: ViewGroup, listener: DiffAwareAdapter.OnActionListener) : + DiffAwareHolder(parent.inflate(R.layout.pluto___item_settings_reset_all), listener) { + + private val binding = PlutoItemSettingsResetAllBinding.bind(itemView) + + override fun onBind(item: ListItem) { + if (item is SettingsResetAllEntity) { + binding.root.setDebounceClickListener { + onAction("click") + } + } + } +} diff --git a/pluto/src/main/java/com/pluto/ui/PluginOptionsDialog.kt b/pluto/src/main/java/com/pluto/ui/PluginOptionsDialog.kt new file mode 100644 index 00000000..5d828bbc --- /dev/null +++ b/pluto/src/main/java/com/pluto/ui/PluginOptionsDialog.kt @@ -0,0 +1,103 @@ +package com.pluto.ui + +import android.content.Context +import android.content.Intent +import android.graphics.drawable.ColorDrawable +import android.net.Uri +import android.view.View +import android.view.View.GONE +import android.view.View.VISIBLE +import android.widget.FrameLayout +import androidx.core.content.ContextCompat +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.google.android.material.bottomsheet.BottomSheetDialog +import com.pluto.R +import com.pluto.databinding.PlutoLayoutPluginOptionsDialogBinding +import com.pluto.plugin.DeveloperDetails +import com.pluto.plugin.PluginOption +import com.pluto.plugin.options.PluginOptionAdapter +import com.pluto.plugin.utilities.device.Device +import com.pluto.plugin.utilities.extensions.dp +import com.pluto.plugin.utilities.extensions.inflate +import com.pluto.plugin.utilities.list.BaseAdapter +import com.pluto.plugin.utilities.list.CustomItemDecorator +import com.pluto.plugin.utilities.list.DiffAwareAdapter +import com.pluto.plugin.utilities.list.DiffAwareHolder +import com.pluto.plugin.utilities.list.ListItem +import com.pluto.plugin.utilities.setDebounceClickListener +import com.pluto.plugin.utilities.spannable.createSpan + +@Deprecated("global level plugin options are no longer supported") +internal class PluginOptionsDialog(context: Context, onOptionSelected: (String) -> Unit) : BottomSheetDialog(context, R.style.PlutoBottomSheetDialogTheme) { + + private val sheetView: View = context.inflate(R.layout.pluto___layout_plugin_options_dialog) + private val binding = PlutoLayoutPluginOptionsDialogBinding.bind(sheetView) + private val device = Device(context) + private val onActionListener = object : DiffAwareAdapter.OnActionListener { + override fun onAction(action: String, data: ListItem, holder: DiffAwareHolder?) { + if (data is PluginOption) { + onOptionSelected.invoke(data.id) + dismiss() + } + } + } + private val optionsAdapter: BaseAdapter by lazy { PluginOptionAdapter(onActionListener) } + + init { + setContentView(sheetView) + (sheetView.parent as View).background = + ColorDrawable(ContextCompat.getColor(context, R.color.pluto___transparent)) + + binding.optionsList.apply { + adapter = optionsAdapter + addItemDecoration(CustomItemDecorator(context, LIST_DIVIDER_OFFSET.dp.toInt())) + } + + binding.website.hint = context.createSpan { + append(italic(context.getString(R.string.pluto___website_url_hint))) + } + + binding.vcsLink.hint = context.createSpan { + append(italic(context.getString(R.string.pluto___vcs_url_hint))) + } + + setOnShowListener { dialog -> + if (dialog is BottomSheetDialog) { + val bottomSheet = dialog.findViewById(R.id.design_bottom_sheet) as FrameLayout? + val behavior = BottomSheetBehavior.from(bottomSheet!!) + behavior.apply { + state = BottomSheetBehavior.STATE_EXPANDED + isHideable = false + skipCollapsed = true + peekHeight = device.screen.heightPx + } + } + } + } + + fun show(pluginOptions: List, developerDetails: DeveloperDetails?) { + binding.noOptions.visibility = if (pluginOptions.isEmpty()) VISIBLE else GONE + optionsAdapter.list = pluginOptions + + developerDetails?.website?.let { url -> + binding.website.text = url + binding.website.setDebounceClickListener { + val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(url)) + context.startActivity(browserIntent) + } + } + developerDetails?.vcsLink?.let { url -> + binding.vcsLink.text = url + binding.vcsLink.setDebounceClickListener { + val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(url)) + context.startActivity(browserIntent) + } + } + + show() + } + + private companion object { + const val LIST_DIVIDER_OFFSET = 16f + } +} diff --git a/pluto/src/main/java/com/pluto/ui/PlutoActivity.kt b/pluto/src/main/java/com/pluto/ui/PlutoActivity.kt new file mode 100644 index 00000000..633b1eb9 --- /dev/null +++ b/pluto/src/main/java/com/pluto/ui/PlutoActivity.kt @@ -0,0 +1,95 @@ +package com.pluto.ui + +import android.content.Intent +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.lifecycleScope +import com.pluto.Pluto +import com.pluto.R +import com.pluto.databinding.PlutoActivityPlutoBinding +import com.pluto.plugin.DeveloperDetails +import com.pluto.plugin.PluginHelper.Companion.ID_LABEL +import com.pluto.plugin.utilities.extensions.delayedLaunchWhenResumed +import com.pluto.plugin.utilities.extensions.toast +import com.pluto.plugin.utilities.sharing.ContentShare +import com.pluto.settings.OverConsentFragment +import com.pluto.settings.canDrawOverlays + +class PlutoActivity : AppCompatActivity() { + +// private lateinit var pluginOptionsDialog: PluginOptionsDialog + private lateinit var contentShareHelper: ContentShare + + // private var pluginOptions: List = emptyList() + private var developerDetails: DeveloperDetails? = null +// private val pluginOptionsViewModel by viewModels() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + val binding = PlutoActivityPlutoBinding.inflate(layoutInflater) + setContentView(binding.root) + + contentShareHelper = ContentShare(this) +// pluginOptionsDialog = PluginOptionsDialog(this) { option -> +// pluginOptionsViewModel.select(option) +// } + +// Pluto.currentPlugin.removeObservers(this) +// Pluto.currentPlugin.observe(this) { +// val fragment = it.getView() +// // pluginOptions = it.getOptions() +// developerDetails = it.getDeveloperDetails() +// supportFragmentManager.beginTransaction().apply { +// this.runOnCommit { +// it.onPluginViewCreated(it.savedInstance) +// } +// this.replace(R.id.container, fragment).commit() +// } +// } + +// Pluto.appState.observe(this) { +// if (it is AppState.Background) { +// finish() +// } +// } + handleIntent(intent) + } + + private fun handleIntent(intent: Intent?) { + intent?.getStringExtra(ID_LABEL)?.let { id -> + Pluto.pluginManager.get(id)?.let { + val fragment = it.getView() + developerDetails = it.getDeveloperDetails() + supportFragmentManager.beginTransaction().apply { + this.runOnCommit { + it.onPluginViewCreated(it.savedInstance) + } + this.replace(R.id.container, fragment).commit() + } + return + } + applicationContext.toast("Plugin [$id] not installed") + finish() + } + } + + override fun onResume() { + super.onResume() + if (!Pluto.session.isConsentAlreadyShown && Pluto.notch?.enabled == true && !canDrawOverlays()) { + lifecycleScope.delayedLaunchWhenResumed(CONSENT_SHOW_DELAY) { + OverConsentFragment().show(supportFragmentManager, CONSENT_SHOW_TAG) + } + } + } + + override fun onNewIntent(intent: Intent?) { + super.onNewIntent(intent) + handleIntent(intent) +// pluginOptionsDialog.dismiss() + } + + private companion object { + const val CONSENT_SHOW_DELAY = 400L + const val CONSENT_SHOW_TAG = "overlay_consent" + } +} diff --git a/pluto/src/main/java/com/pluto/ui/RoundedFrameLayout.kt b/pluto/src/main/java/com/pluto/ui/RoundedFrameLayout.kt new file mode 100644 index 00000000..40f093c0 --- /dev/null +++ b/pluto/src/main/java/com/pluto/ui/RoundedFrameLayout.kt @@ -0,0 +1,116 @@ +package com.pluto.ui + +import android.annotation.SuppressLint +import android.content.Context +import android.graphics.Canvas +import android.graphics.Path +import android.graphics.RectF +import android.util.AttributeSet +import android.view.View +import android.widget.FrameLayout +import com.pluto.R + +/** + * A not-so-efficient rounded-rectangle-clipped FrameLayout, until Android-L's clipping Outlines are + * widely available. + * + * NOTE: This doesn't support anti-aliasing, if you just need a rounded ImageView use the more + * efficient BitmapDrawable method which does: http://evel.io/2013/07/21/rounded-avatars-in-android/ + * + * In my case the rounded corner was to mask an image as you scrolled them in a ViewPager, so by + * combining this with a RoundedImageView it appears anti-aliased unless actively scrolling. + * + * Created by richardleggett on 04/09/2014. + */ +internal class RoundedFrameLayout : FrameLayout { + + private var path: Path? = null + private var rect: RectF? = null + private var radiiArray = floatArrayOf(DEF_RADIUS, DEF_RADIUS, DEF_RADIUS, DEF_RADIUS, DEF_RADIUS, DEF_RADIUS, DEF_RADIUS, DEF_RADIUS) + + constructor(context: Context) : super(context, null, 0) { + initView(null) + } + + constructor(context: Context, attrs: AttributeSet) : super(context, attrs, 0) { + initView(attrs) + } + + constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super( + context, + attrs, + defStyle + ) { + initView(attrs) + } + + @SuppressLint("CustomViewStyleable") + private fun initView(attributes: AttributeSet?) { + if (attributes != null) { + val array = context.obtainStyledAttributes( + attributes, + R.styleable.pluto___RoundedFrameLayout + ) + val mCornerRadius = + array.getDimension(R.styleable.pluto___RoundedFrameLayout_corner_radius, -1f) + if (mCornerRadius != -1f) { + for (i in 0 until NO_OF_CORNERS) { + radiiArray[i] = mCornerRadius + } + } else { + val topLeft = + array.getDimension(R.styleable.pluto___RoundedFrameLayout_corner_radius_left_top, DEF_RADIUS) + val topRight = + array.getDimension(R.styleable.pluto___RoundedFrameLayout_corner_radius_right_top, DEF_RADIUS) + val bottomRight = + array.getDimension( + R.styleable.pluto___RoundedFrameLayout_corner_radius_right_bottom, + DEF_RADIUS + ) + val bottomLeft = + array.getDimension(R.styleable.pluto___RoundedFrameLayout_corner_radius_left_bottom, DEF_RADIUS) + radiiArray = floatArrayOf( + topLeft, + topLeft, + topRight, + topRight, + bottomRight, + bottomRight, + bottomLeft, + bottomLeft + ) + } + array.recycle() + } + + path = Path() + rect = RectF() + + setWillNotDraw(false) + } + + override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { + super.onSizeChanged(w, h, oldw, oldh) + path?.let { path -> + path.reset() + rect?.let { rect -> + rect.set(0f, 0f, w.toFloat(), h.toFloat()) + path.addRoundRect(rect, radiiArray, Path.Direction.CCW) + } + path.close() + } + } + + override fun drawChild(canvas: Canvas, child: View, drawingTime: Long): Boolean { + val count = canvas.save() + canvas.clipPath(path!!) + val result = super.drawChild(canvas, child, drawingTime) + canvas.restoreToCount(count) + return result + } + + private companion object { + const val DEF_RADIUS = 0f + const val NO_OF_CORNERS = 8 + } +} diff --git a/pluto/src/main/res/anim/pluto___click_bounce.xml b/pluto/src/main/res/anim/pluto___click_bounce.xml new file mode 100644 index 00000000..cc34ba93 --- /dev/null +++ b/pluto/src/main/res/anim/pluto___click_bounce.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/pluto/src/main/res/anim/pluto___fade_in_dialog.xml b/pluto/src/main/res/anim/pluto___fade_in_dialog.xml new file mode 100644 index 00000000..3a1a72d4 --- /dev/null +++ b/pluto/src/main/res/anim/pluto___fade_in_dialog.xml @@ -0,0 +1,7 @@ + + \ No newline at end of file diff --git a/pluto/src/main/res/anim/pluto___fade_out_dialog.xml b/pluto/src/main/res/anim/pluto___fade_out_dialog.xml new file mode 100644 index 00000000..9e462d2b --- /dev/null +++ b/pluto/src/main/res/anim/pluto___fade_out_dialog.xml @@ -0,0 +1,7 @@ + + \ No newline at end of file diff --git a/pluto/src/main/res/anim/pluto___slide_in_bottom.xml b/pluto/src/main/res/anim/pluto___slide_in_bottom.xml new file mode 100644 index 00000000..d9700b09 --- /dev/null +++ b/pluto/src/main/res/anim/pluto___slide_in_bottom.xml @@ -0,0 +1,5 @@ + + diff --git a/pluto/src/main/res/anim/pluto___slide_out_bottom.xml b/pluto/src/main/res/anim/pluto___slide_out_bottom.xml new file mode 100644 index 00000000..1540acf3 --- /dev/null +++ b/pluto/src/main/res/anim/pluto___slide_out_bottom.xml @@ -0,0 +1,5 @@ + + \ No newline at end of file diff --git a/pluto/src/main/res/color/pluto___bg_strip_button_dark.xml b/pluto/src/main/res/color/pluto___bg_strip_button_dark.xml deleted file mode 100644 index e17bf1e5..00000000 --- a/pluto/src/main/res/color/pluto___bg_strip_button_dark.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/pluto/src/main/res/drawable/pluto___bg_cta_dashed_blue.xml b/pluto/src/main/res/drawable/pluto___bg_cta_dashed_blue.xml deleted file mode 100644 index 938e26b1..00000000 --- a/pluto/src/main/res/drawable/pluto___bg_cta_dashed_blue.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/pluto/src/main/res/drawable/pluto___bg_section_body.xml b/pluto/src/main/res/drawable/pluto___bg_section_body.xml deleted file mode 100644 index 3a5f92c4..00000000 --- a/pluto/src/main/res/drawable/pluto___bg_section_body.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/pluto/src/main/res/drawable/pluto___bg_section_header.xml b/pluto/src/main/res/drawable/pluto___bg_section_header.xml deleted file mode 100644 index 6d661ff4..00000000 --- a/pluto/src/main/res/drawable/pluto___bg_section_header.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/pluto/src/main/res/drawable/pluto___bg_section_light.xml b/pluto/src/main/res/drawable/pluto___bg_section_light.xml deleted file mode 100644 index 57700bcd..00000000 --- a/pluto/src/main/res/drawable/pluto___bg_section_light.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/pluto/src/main/res/drawable/pluto___bg_shared_pref_file_badge.xml b/pluto/src/main/res/drawable/pluto___bg_shared_pref_file_badge.xml deleted file mode 100644 index 2e51c23a..00000000 --- a/pluto/src/main/res/drawable/pluto___bg_shared_pref_file_badge.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/pluto/src/main/res/drawable/pluto___ic_about.xml b/pluto/src/main/res/drawable/pluto___ic_about.xml new file mode 100644 index 00000000..8bdea57c --- /dev/null +++ b/pluto/src/main/res/drawable/pluto___ic_about.xml @@ -0,0 +1,9 @@ + + + diff --git a/pluto/src/main/res/drawable/pluto___ic_anr_warning.xml b/pluto/src/main/res/drawable/pluto___ic_anr_warning.xml deleted file mode 100644 index 6601a299..00000000 --- a/pluto/src/main/res/drawable/pluto___ic_anr_warning.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/pluto/src/main/res/drawable/pluto___ic_arrow_back.xml b/pluto/src/main/res/drawable/pluto___ic_arrow_back.xml deleted file mode 100644 index 93cdfc60..00000000 --- a/pluto/src/main/res/drawable/pluto___ic_arrow_back.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/pluto/src/main/res/drawable/pluto___ic_check_light.xml b/pluto/src/main/res/drawable/pluto___ic_back.xml similarity index 58% rename from pluto/src/main/res/drawable/pluto___ic_check_light.xml rename to pluto/src/main/res/drawable/pluto___ic_back.xml index 33bf8027..7b930456 100644 --- a/pluto/src/main/res/drawable/pluto___ic_check_light.xml +++ b/pluto/src/main/res/drawable/pluto___ic_back.xml @@ -4,6 +4,6 @@ android:viewportWidth="24" android:viewportHeight="24"> + android:fillColor="@color/pluto___white_80" + android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z"/> diff --git a/pluto/src/main/res/drawable/pluto___ic_chevron_right.xml b/pluto/src/main/res/drawable/pluto___ic_chevron_right.xml deleted file mode 100644 index 036b43be..00000000 --- a/pluto/src/main/res/drawable/pluto___ic_chevron_right.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/pluto/src/main/res/drawable/pluto___ic_close.xml b/pluto/src/main/res/drawable/pluto___ic_close.xml index 52ec74ab..82eaa9f9 100644 --- a/pluto/src/main/res/drawable/pluto___ic_close.xml +++ b/pluto/src/main/res/drawable/pluto___ic_close.xml @@ -4,6 +4,6 @@ android:viewportWidth="24.0" android:viewportHeight="24.0"> diff --git a/pluto/src/main/res/drawable/pluto___ic_close_gray.xml b/pluto/src/main/res/drawable/pluto___ic_close_gray.xml deleted file mode 100644 index 01ab3ef0..00000000 --- a/pluto/src/main/res/drawable/pluto___ic_close_gray.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/pluto/src/main/res/drawable/pluto___ic_copy.xml b/pluto/src/main/res/drawable/pluto___ic_copy.xml deleted file mode 100644 index afdd48a8..00000000 --- a/pluto/src/main/res/drawable/pluto___ic_copy.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/pluto/src/main/res/drawable/pluto___ic_debugger.xml b/pluto/src/main/res/drawable/pluto___ic_debugger.xml deleted file mode 100644 index c2ee660d..00000000 --- a/pluto/src/main/res/drawable/pluto___ic_debugger.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/pluto/src/main/res/drawable/pluto___ic_delete.xml b/pluto/src/main/res/drawable/pluto___ic_delete.xml deleted file mode 100644 index 3226ffc7..00000000 --- a/pluto/src/main/res/drawable/pluto___ic_delete.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/pluto/src/main/res/drawable/pluto___ic_done.xml b/pluto/src/main/res/drawable/pluto___ic_done.xml deleted file mode 100644 index ace73443..00000000 --- a/pluto/src/main/res/drawable/pluto___ic_done.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/pluto/src/main/res/drawable/pluto___ic_easy_access.xml b/pluto/src/main/res/drawable/pluto___ic_easy_access.xml index 12cf0d79..7d6a5bf5 100644 --- a/pluto/src/main/res/drawable/pluto___ic_easy_access.xml +++ b/pluto/src/main/res/drawable/pluto___ic_easy_access.xml @@ -1,9 +1,9 @@ diff --git a/pluto/src/main/res/drawable/pluto___ic_filter.xml b/pluto/src/main/res/drawable/pluto___ic_filter.xml deleted file mode 100644 index 549c9e0b..00000000 --- a/pluto/src/main/res/drawable/pluto___ic_filter.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - diff --git a/pluto/src/main/res/drawable/pluto___ic_grid.xml b/pluto/src/main/res/drawable/pluto___ic_grid.xml new file mode 100644 index 00000000..cab8add3 --- /dev/null +++ b/pluto/src/main/res/drawable/pluto___ic_grid.xml @@ -0,0 +1,9 @@ + + + diff --git a/pluto/src/main/res/drawable/pluto___ic_grid_dark.xml b/pluto/src/main/res/drawable/pluto___ic_grid_dark.xml new file mode 100644 index 00000000..8996a64f --- /dev/null +++ b/pluto/src/main/res/drawable/pluto___ic_grid_dark.xml @@ -0,0 +1,9 @@ + + + diff --git a/pluto/src/main/res/drawable/pluto___ic_key.xml b/pluto/src/main/res/drawable/pluto___ic_key.xml deleted file mode 100644 index 04434424..00000000 --- a/pluto/src/main/res/drawable/pluto___ic_key.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - diff --git a/pluto/src/main/res/drawable/pluto___ic_more_vertical.xml b/pluto/src/main/res/drawable/pluto___ic_more.xml similarity index 82% rename from pluto/src/main/res/drawable/pluto___ic_more_vertical.xml rename to pluto/src/main/res/drawable/pluto___ic_more.xml index c523ce8f..e5db6edf 100644 --- a/pluto/src/main/res/drawable/pluto___ic_more_vertical.xml +++ b/pluto/src/main/res/drawable/pluto___ic_more.xml @@ -1,10 +1,9 @@ diff --git a/pluto/src/main/res/drawable/pluto___ic_proxy_base_request.xml b/pluto/src/main/res/drawable/pluto___ic_proxy_base_request.xml deleted file mode 100644 index 93c1f509..00000000 --- a/pluto/src/main/res/drawable/pluto___ic_proxy_base_request.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/pluto/src/main/res/drawable/pluto___ic_proxy_base_request_dark.xml b/pluto/src/main/res/drawable/pluto___ic_proxy_base_request_dark.xml deleted file mode 100644 index 788d89ba..00000000 --- a/pluto/src/main/res/drawable/pluto___ic_proxy_base_request_dark.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/pluto/src/main/res/drawable/pluto___ic_proxy_indicator.xml b/pluto/src/main/res/drawable/pluto___ic_proxy_indicator.xml deleted file mode 100644 index 4601e3c8..00000000 --- a/pluto/src/main/res/drawable/pluto___ic_proxy_indicator.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - diff --git a/pluto/src/main/res/drawable/pluto___ic_proxy_settings.xml b/pluto/src/main/res/drawable/pluto___ic_proxy_settings.xml deleted file mode 100644 index 093f31d1..00000000 --- a/pluto/src/main/res/drawable/pluto___ic_proxy_settings.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - diff --git a/pluto/src/main/res/drawable/pluto___ic_proxy_settings_disabled.xml b/pluto/src/main/res/drawable/pluto___ic_proxy_settings_disabled.xml deleted file mode 100644 index 658cfbbc..00000000 --- a/pluto/src/main/res/drawable/pluto___ic_proxy_settings_disabled.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - diff --git a/pluto/src/main/res/drawable/pluto___ic_proxy_settings_enabled.xml b/pluto/src/main/res/drawable/pluto___ic_proxy_settings_enabled.xml deleted file mode 100644 index e075c0bb..00000000 --- a/pluto/src/main/res/drawable/pluto___ic_proxy_settings_enabled.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - diff --git a/pluto/src/main/res/drawable/pluto___ic_settings.xml b/pluto/src/main/res/drawable/pluto___ic_settings.xml index bebe968b..b6225667 100644 --- a/pluto/src/main/res/drawable/pluto___ic_settings.xml +++ b/pluto/src/main/res/drawable/pluto___ic_settings.xml @@ -1,9 +1,9 @@ - + diff --git a/pluto/src/main/res/drawable/pluto___ic_share.xml b/pluto/src/main/res/drawable/pluto___ic_share.xml deleted file mode 100644 index 0bcbd95e..00000000 --- a/pluto/src/main/res/drawable/pluto___ic_share.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/pluto/src/main/res/drawable/pluto___ic_success.xml b/pluto/src/main/res/drawable/pluto___ic_success.xml deleted file mode 100644 index 80531b09..00000000 --- a/pluto/src/main/res/drawable/pluto___ic_success.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/pluto/src/main/res/drawable/pluto___ic_user_properties.xml b/pluto/src/main/res/drawable/pluto___ic_user_properties.xml deleted file mode 100644 index f07c4778..00000000 --- a/pluto/src/main/res/drawable/pluto___ic_user_properties.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/pluto/src/main/res/drawable/pluto___ic_vcs.xml b/pluto/src/main/res/drawable/pluto___ic_vcs.xml new file mode 100644 index 00000000..0065317e --- /dev/null +++ b/pluto/src/main/res/drawable/pluto___ic_vcs.xml @@ -0,0 +1,9 @@ + + + diff --git a/pluto/src/main/res/drawable/pluto___ic_website.xml b/pluto/src/main/res/drawable/pluto___ic_website.xml new file mode 100644 index 00000000..57070aa5 --- /dev/null +++ b/pluto/src/main/res/drawable/pluto___ic_website.xml @@ -0,0 +1,14 @@ + + + + diff --git a/pluto/src/main/res/layout/pluto___activity_plugin_selector.xml b/pluto/src/main/res/layout/pluto___activity_plugin_selector.xml new file mode 100644 index 00000000..0c9f44bc --- /dev/null +++ b/pluto/src/main/res/layout/pluto___activity_plugin_selector.xml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pluto/src/main/res/layout/pluto___activity_pluto.xml b/pluto/src/main/res/layout/pluto___activity_pluto.xml index cd061494..088a67a8 100644 --- a/pluto/src/main/res/layout/pluto___activity_pluto.xml +++ b/pluto/src/main/res/layout/pluto___activity_pluto.xml @@ -5,15 +5,10 @@ android:layout_height="match_parent" tools:context=".ui.PlutoActivity"> - - + android:layout_height="match_parent" + android:background="@color/pluto___white_80" /> \ No newline at end of file diff --git a/pluto/src/main/res/layout/pluto___fragment_about.xml b/pluto/src/main/res/layout/pluto___fragment_about.xml deleted file mode 100644 index c1e24849..00000000 --- a/pluto/src/main/res/layout/pluto___fragment_about.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/pluto/src/main/res/layout/pluto___fragment_app_state.xml b/pluto/src/main/res/layout/pluto___fragment_app_state.xml deleted file mode 100644 index 53c3bbd5..00000000 --- a/pluto/src/main/res/layout/pluto___fragment_app_state.xml +++ /dev/null @@ -1,247 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/pluto/src/main/res/layout/pluto___fragment_base.xml b/pluto/src/main/res/layout/pluto___fragment_base.xml deleted file mode 100644 index 3a5d6223..00000000 --- a/pluto/src/main/res/layout/pluto___fragment_base.xml +++ /dev/null @@ -1,91 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/pluto/src/main/res/layout/pluto___fragment_crash_details.xml b/pluto/src/main/res/layout/pluto___fragment_crash_details.xml deleted file mode 100644 index 81c3059b..00000000 --- a/pluto/src/main/res/layout/pluto___fragment_crash_details.xml +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/pluto/src/main/res/layout/pluto___fragment_crashes.xml b/pluto/src/main/res/layout/pluto___fragment_crashes.xml deleted file mode 100644 index 03debdd0..00000000 --- a/pluto/src/main/res/layout/pluto___fragment_crashes.xml +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/pluto/src/main/res/layout/pluto___fragment_logs.xml b/pluto/src/main/res/layout/pluto___fragment_logs.xml deleted file mode 100644 index 3735a075..00000000 --- a/pluto/src/main/res/layout/pluto___fragment_logs.xml +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/pluto/src/main/res/layout/pluto___fragment_network.xml b/pluto/src/main/res/layout/pluto___fragment_network.xml deleted file mode 100644 index 3d19433b..00000000 --- a/pluto/src/main/res/layout/pluto___fragment_network.xml +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/pluto/src/main/res/layout/pluto___fragment_network_details_overview.xml b/pluto/src/main/res/layout/pluto___fragment_network_details_overview.xml deleted file mode 100644 index 239bb92a..00000000 --- a/pluto/src/main/res/layout/pluto___fragment_network_details_overview.xml +++ /dev/null @@ -1,282 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/pluto/src/main/res/layout/pluto___fragment_network_details_request.xml b/pluto/src/main/res/layout/pluto___fragment_network_details_request.xml deleted file mode 100644 index 28268b48..00000000 --- a/pluto/src/main/res/layout/pluto___fragment_network_details_request.xml +++ /dev/null @@ -1,188 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/pluto/src/main/res/layout/pluto___fragment_network_details_response.xml b/pluto/src/main/res/layout/pluto___fragment_network_details_response.xml deleted file mode 100644 index 23fff94e..00000000 --- a/pluto/src/main/res/layout/pluto___fragment_network_details_response.xml +++ /dev/null @@ -1,175 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/pluto/src/main/res/layout/pluto___fragment_network_proxy_settings.xml b/pluto/src/main/res/layout/pluto___fragment_network_proxy_settings.xml deleted file mode 100644 index 27b44a0c..00000000 --- a/pluto/src/main/res/layout/pluto___fragment_network_proxy_settings.xml +++ /dev/null @@ -1,380 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/pluto/src/main/res/layout/pluto___fragment_network_proxy_settings_list.xml b/pluto/src/main/res/layout/pluto___fragment_network_proxy_settings_list.xml deleted file mode 100644 index c7f47590..00000000 --- a/pluto/src/main/res/layout/pluto___fragment_network_proxy_settings_list.xml +++ /dev/null @@ -1,208 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/pluto/src/main/res/layout/pluto___fragment_overlay_consent.xml b/pluto/src/main/res/layout/pluto___fragment_overlay_consent.xml new file mode 100644 index 00000000..23f0e6f0 --- /dev/null +++ b/pluto/src/main/res/layout/pluto___fragment_overlay_consent.xml @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pluto/src/main/res/layout/pluto___fragment_settings.xml b/pluto/src/main/res/layout/pluto___fragment_settings.xml index 7e46dd66..cd7c3d04 100644 --- a/pluto/src/main/res/layout/pluto___fragment_settings.xml +++ b/pluto/src/main/res/layout/pluto___fragment_settings.xml @@ -1,109 +1,50 @@ - + android:background="@color/pluto___transparent" + android:layout_height="wrap_content"> - + android:layout_margin="@dimen/pluto___margin_medium" + android:background="@color/pluto___white" + app:cardCornerRadius="@dimen/pluto___margin_xsmall" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent"> - - - - - - - - - - - - - + app:layout_constraintTop_toTopOf="parent" /> - + app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" + app:layout_constraintTop_toBottomOf="@+id/title" + tools:listitem="@layout/pluto___item_plugin_option" /> - - - \ No newline at end of file + + \ No newline at end of file diff --git a/pluto/src/main/res/layout/pluto___fragment_shared_pref.xml b/pluto/src/main/res/layout/pluto___fragment_shared_pref.xml deleted file mode 100644 index 3151511c..00000000 --- a/pluto/src/main/res/layout/pluto___fragment_shared_pref.xml +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/pluto/src/main/res/layout/pluto___fragment_shared_pref_filter.xml b/pluto/src/main/res/layout/pluto___fragment_shared_pref_filter.xml deleted file mode 100644 index ae3da629..00000000 --- a/pluto/src/main/res/layout/pluto___fragment_shared_pref_filter.xml +++ /dev/null @@ -1,98 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/pluto/src/main/res/layout/pluto___item_anr_thread_state.xml b/pluto/src/main/res/layout/pluto___item_anr_thread_state.xml deleted file mode 100644 index 6aa64bfa..00000000 --- a/pluto/src/main/res/layout/pluto___item_anr_thread_state.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - diff --git a/pluto/src/main/res/layout/pluto___item_app_state.xml b/pluto/src/main/res/layout/pluto___item_app_state.xml deleted file mode 100644 index 25c45aea..00000000 --- a/pluto/src/main/res/layout/pluto___item_app_state.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/pluto/src/main/res/layout/pluto___item_crash.xml b/pluto/src/main/res/layout/pluto___item_crash.xml deleted file mode 100644 index 877838a2..00000000 --- a/pluto/src/main/res/layout/pluto___item_crash.xml +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/pluto/src/main/res/layout/pluto___item_crash_details_device.xml b/pluto/src/main/res/layout/pluto___item_crash_details_device.xml deleted file mode 100644 index 42be44b0..00000000 --- a/pluto/src/main/res/layout/pluto___item_crash_details_device.xml +++ /dev/null @@ -1,359 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/pluto/src/main/res/layout/pluto___item_crash_details_header.xml b/pluto/src/main/res/layout/pluto___item_crash_details_header.xml deleted file mode 100644 index 81f9f719..00000000 --- a/pluto/src/main/res/layout/pluto___item_crash_details_header.xml +++ /dev/null @@ -1,93 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/pluto/src/main/res/layout/pluto___item_crash_details_thread.xml b/pluto/src/main/res/layout/pluto___item_crash_details_thread.xml deleted file mode 100644 index 0f50b2a2..00000000 --- a/pluto/src/main/res/layout/pluto___item_crash_details_thread.xml +++ /dev/null @@ -1,118 +0,0 @@ - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/pluto/src/main/res/layout/pluto___item_crash_details_thread_states.xml b/pluto/src/main/res/layout/pluto___item_crash_details_thread_states.xml deleted file mode 100644 index a8c9c916..00000000 --- a/pluto/src/main/res/layout/pluto___item_crash_details_thread_states.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/pluto/src/main/res/layout/pluto___item_network.xml b/pluto/src/main/res/layout/pluto___item_network.xml deleted file mode 100644 index 7a234640..00000000 --- a/pluto/src/main/res/layout/pluto___item_network.xml +++ /dev/null @@ -1,111 +0,0 @@ - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/pluto/src/main/res/layout/pluto___item_network_proxy_settings.xml b/pluto/src/main/res/layout/pluto___item_network_proxy_settings.xml deleted file mode 100644 index 1064ae21..00000000 --- a/pluto/src/main/res/layout/pluto___item_network_proxy_settings.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/pluto/src/main/res/layout/pluto___item_plugin.xml b/pluto/src/main/res/layout/pluto___item_plugin.xml new file mode 100644 index 00000000..4e037084 --- /dev/null +++ b/pluto/src/main/res/layout/pluto___item_plugin.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/pluto/src/main/res/layout/pluto___item_shared_pref_filter.xml b/pluto/src/main/res/layout/pluto___item_plugin_option.xml similarity index 51% rename from pluto/src/main/res/layout/pluto___item_shared_pref_filter.xml rename to pluto/src/main/res/layout/pluto___item_plugin_option.xml index 3efa8457..ef9cdac6 100644 --- a/pluto/src/main/res/layout/pluto___item_shared_pref_filter.xml +++ b/pluto/src/main/res/layout/pluto___item_plugin_option.xml @@ -4,33 +4,30 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" - android:foreground="?android:attr/selectableItemBackground" - tools:background="@color/pluto___white"> + android:foreground="?android:attr/selectableItemBackground"> + app:layout_constraintTop_toTopOf="@+id/label" /> \ No newline at end of file diff --git a/pluto/src/main/res/layout/pluto___item_settings_clear_data.xml b/pluto/src/main/res/layout/pluto___item_settings_clear_data.xml deleted file mode 100644 index d8167604..00000000 --- a/pluto/src/main/res/layout/pluto___item_settings_clear_data.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/pluto/src/main/res/layout/pluto___item_settings_easy_access.xml b/pluto/src/main/res/layout/pluto___item_settings_easy_access.xml index 0730424a..ae330073 100644 --- a/pluto/src/main/res/layout/pluto___item_settings_easy_access.xml +++ b/pluto/src/main/res/layout/pluto___item_settings_easy_access.xml @@ -17,7 +17,7 @@ android:fontFamily="@font/muli_semibold" android:text="@string/pluto___settings_easy_access_title" android:textColor="@color/pluto___text_dark" - android:textSize="@dimen/pluto___text_xmedium" + android:textSize="@dimen/pluto___text_small" app:layout_constraintEnd_toStartOf="@+id/checkbox" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> @@ -32,7 +32,7 @@ android:fontFamily="@font/muli" android:text="@string/pluto___easy_access_setup_description" android:textColor="@color/pluto___text_dark_40" - android:textSize="@dimen/pluto___text_small" + android:textSize="@dimen/pluto___text_xxsmall" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="@+id/title" app:layout_constraintHorizontal_bias="0" diff --git a/pluto/src/main/res/layout/pluto___item_settings_easy_access_appearance.xml b/pluto/src/main/res/layout/pluto___item_settings_easy_access_appearance.xml index 6c7cdf0d..a9664550 100644 --- a/pluto/src/main/res/layout/pluto___item_settings_easy_access_appearance.xml +++ b/pluto/src/main/res/layout/pluto___item_settings_easy_access_appearance.xml @@ -15,7 +15,7 @@ android:layout_marginVertical="@dimen/pluto___margin_large" android:fontFamily="@font/muli" android:textColor="@color/pluto___text_dark" - android:textSize="@dimen/pluto___text_xmedium" + android:textSize="@dimen/pluto___text_small" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toStartOf="@+id/checkbox" app:layout_constraintStart_toStartOf="parent" diff --git a/pluto/src/main/res/layout/pluto___item_settings_link_mocklets.xml b/pluto/src/main/res/layout/pluto___item_settings_link_mocklets.xml deleted file mode 100644 index ca56d49a..00000000 --- a/pluto/src/main/res/layout/pluto___item_settings_link_mocklets.xml +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/pluto/src/main/res/layout/pluto___item_settings_reset_all.xml b/pluto/src/main/res/layout/pluto___item_settings_reset_all.xml index f7c40a17..b4996329 100644 --- a/pluto/src/main/res/layout/pluto___item_settings_reset_all.xml +++ b/pluto/src/main/res/layout/pluto___item_settings_reset_all.xml @@ -20,7 +20,7 @@ android:gravity="center_vertical" android:text="@string/pluto___settings_reset_all_title" android:textColor="@color/pluto___text_dark" - android:textSize="@dimen/pluto___text_xmedium" + android:textSize="@dimen/pluto___text_small" app:layout_constraintEnd_toStartOf="@+id/cta" app:layout_constraintStart_toStartOf="parent" android:drawableLeft="@drawable/pluto___ic_error" @@ -36,7 +36,7 @@ android:fontFamily="@font/muli" android:text="@string/pluto___settings_reset_all_description" android:textColor="@color/pluto___text_dark_40" - android:textSize="@dimen/pluto___text_small" + android:textSize="@dimen/pluto___text_xxsmall" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="@+id/title" app:layout_constraintHorizontal_bias="0" diff --git a/pluto/src/main/res/layout/pluto___item_settings_shared_pref.xml b/pluto/src/main/res/layout/pluto___item_settings_shared_pref.xml deleted file mode 100644 index 07790d92..00000000 --- a/pluto/src/main/res/layout/pluto___item_settings_shared_pref.xml +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/pluto/src/main/res/layout/pluto___item_shared_pref_key_value.xml b/pluto/src/main/res/layout/pluto___item_shared_pref_key_value.xml deleted file mode 100644 index 0b3f6e3c..00000000 --- a/pluto/src/main/res/layout/pluto___item_shared_pref_key_value.xml +++ /dev/null @@ -1,72 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/pluto/src/main/res/layout/pluto___layout_easy_access_setup.xml b/pluto/src/main/res/layout/pluto___layout_easy_access_setup.xml deleted file mode 100644 index 52b71088..00000000 --- a/pluto/src/main/res/layout/pluto___layout_easy_access_setup.xml +++ /dev/null @@ -1,94 +0,0 @@ - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/pluto/src/main/res/layout/pluto___layout_network_call_details.xml b/pluto/src/main/res/layout/pluto___layout_network_call_details.xml deleted file mode 100644 index da508711..00000000 --- a/pluto/src/main/res/layout/pluto___layout_network_call_details.xml +++ /dev/null @@ -1,200 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/pluto/src/main/res/layout/pluto___layout_notch.xml b/pluto/src/main/res/layout/pluto___layout_notch.xml new file mode 100644 index 00000000..323daa71 --- /dev/null +++ b/pluto/src/main/res/layout/pluto___layout_notch.xml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pluto/src/main/res/layout/pluto___layout_plugin_options_dialog.xml b/pluto/src/main/res/layout/pluto___layout_plugin_options_dialog.xml new file mode 100644 index 00000000..fcd4270e --- /dev/null +++ b/pluto/src/main/res/layout/pluto___layout_plugin_options_dialog.xml @@ -0,0 +1,139 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pluto/src/main/res/layout/pluto___layout_popup.xml b/pluto/src/main/res/layout/pluto___layout_popup.xml deleted file mode 100644 index f302e4de..00000000 --- a/pluto/src/main/res/layout/pluto___layout_popup.xml +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/pluto/src/main/res/layout/pluto___layout_shared_pref_edit.xml b/pluto/src/main/res/layout/pluto___layout_shared_pref_edit.xml deleted file mode 100644 index 4d0a902e..00000000 --- a/pluto/src/main/res/layout/pluto___layout_shared_pref_edit.xml +++ /dev/null @@ -1,165 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/pluto/src/main/res/layout/pluto___stub_copy_curl.xml b/pluto/src/main/res/layout/pluto___stub_copy_curl.xml deleted file mode 100644 index 9795a903..00000000 --- a/pluto/src/main/res/layout/pluto___stub_copy_curl.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - diff --git a/pluto/src/main/res/layout/pluto___stub_crash_report.xml b/pluto/src/main/res/layout/pluto___stub_crash_report.xml deleted file mode 100644 index 72fad1d1..00000000 --- a/pluto/src/main/res/layout/pluto___stub_crash_report.xml +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - - - - - diff --git a/pluto/src/main/res/layout/pluto___stub_network_settings.xml b/pluto/src/main/res/layout/pluto___stub_network_settings.xml deleted file mode 100644 index a7cf924a..00000000 --- a/pluto/src/main/res/layout/pluto___stub_network_settings.xml +++ /dev/null @@ -1,136 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/pluto/src/main/res/menu/pluto___popup_menu_crashes.xml b/pluto/src/main/res/menu/pluto___popup_menu_crashes.xml deleted file mode 100644 index 42d9c6b5..00000000 --- a/pluto/src/main/res/menu/pluto___popup_menu_crashes.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - \ No newline at end of file diff --git a/pluto/src/main/res/menu/pluto___popup_menu_network.xml b/pluto/src/main/res/menu/pluto___popup_menu_network.xml deleted file mode 100644 index 5086fb58..00000000 --- a/pluto/src/main/res/menu/pluto___popup_menu_network.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/pluto/src/main/res/menu/pluto___popup_menu_shared_pref.xml b/pluto/src/main/res/menu/pluto___popup_menu_shared_pref.xml deleted file mode 100644 index 7b46240c..00000000 --- a/pluto/src/main/res/menu/pluto___popup_menu_shared_pref.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - \ No newline at end of file diff --git a/pluto/src/main/res/values/attr.xml b/pluto/src/main/res/values/attr.xml index da2ff5b1..9e65b1b0 100644 --- a/pluto/src/main/res/values/attr.xml +++ b/pluto/src/main/res/values/attr.xml @@ -1,3 +1,12 @@ + + + + + + + + + \ No newline at end of file diff --git a/pluto/src/main/res/values/dimens.xml b/pluto/src/main/res/values/dimens.xml index 1c1d4a1b..dc91451e 100644 --- a/pluto/src/main/res/values/dimens.xml +++ b/pluto/src/main/res/values/dimens.xml @@ -1,36 +1,4 @@ - 50dp - 25dp - 140dp - 44dp - - 1dp - - 2dp - 4dp - 8dp - 12dp - 16dp - 20dp - 24dp - 28dp - 32dp - 36dp - 40dp - 72dp - - 10sp - 11sp - 12sp - 13sp - 14sp - 15sp - 16sp - 18sp - 20sp - 24sp - 30sp - - 48dp + 30dp \ No newline at end of file diff --git a/pluto/src/main/res/values/strings.xml b/pluto/src/main/res/values/strings.xml index 18c42815..5d3b04cc 100644 --- a/pluto/src/main/res/values/strings.xml +++ b/pluto/src/main/res/values/strings.xml @@ -1,144 +1,30 @@ - Pluto - - Logger - Network - Crashes & ANRs - Shared Preferences - Search log - Search key - Search API Call - Search Exceptions - Update Value - Delete - Share - Thread - Thread States - Priority - is_Daemon - App State - Device Info - App Version - Android OS - Android API Level - Orientation - is_Rooted - Height - Size - Density - Width - updating StringSet preferences is not supported. - Overview - Request - Response - showing response from - Headers - Query Params - Body - Exception - ~ No Headers - 200 - In Progress - Failed - URL - ~ Waiting for response - Method - is_SSL - Protocol - Requested at - Response received at - Delay (milliseconds) - ~ NA - Pluto is initialised successfully!\nTap on notification to view. - Enable Easy Access - Enable this settings to add an Easy Access floating handler over your application to access the library. - Enable settings - Dismiss - No Logs printed. - No API call detected. - No search result found. + Plugins Settings About - Enable Easy Access - Clear all crash logs - Update - Show Shared Preference for - Configure which Shared Preference files are displayed in the Shared Preferences tab. - Link Mocklets Account - Link - Connect you Mocklets account to manage your network proxy setting efficiently - See All - Network Proxy Settings - Delete All Calls - Delete All Logs - Delete All Crash logs - Filter Settings - No Crashes. - Request URL - Redirect call to - Save Settings - Choose API from Mocklets - Enter the https request URL - You can either enter the mock API url manually\nor - Setup API Proxy for future calls - Update Settings - App Properties - Click the property to copy the value. - No App Properties set - Learn more - Edit Preference - Log Details - Select Preference to see the data from it. - No Preference found.\nAdd a preference or check filter settings. - showing preference from - Event Attributes - Oops! this looks like our miss. - Tap here to report this crash to us, so that you don\'t have to face this crash again. - open Pluto - Exit Pluto - Open App Properties - Open Settings - exit settings - Exit app properties - Exit Crash details - open crash menu - exit network call details - open shared preferences menu - exit network proxy settings - exit network proxy edit - open network menu - open logs menu - Set Dark mode for Access popup - Enable Right handed mode for Access popup + Choose Pluto Plugin + More Options + Pluto is debugging %s Tap here to view complete debug report - \n\n--- Unexpected end of content --- - \n\n--- Content truncated --- + Close Pluto + no options available + Plugin Details + your website url goes here + your vcs url goes here + Set Dark mode for Access popup + Enable Right handed mode for Access popup + Plugin data reset requested + Enable Notch + Show a Notch over your application to access the library. Reset all data This is clear all the data and app state. Choose this if the library is stuck in an unexpected error. Reset - Library data reset successful - Pluto detected a crash in %s - Tap here to see crash details. - Share Request cURL - cURL code copied! - Clear - Preference selection cleared! - Search API content - Main thread unresponsive for +%d ms - ANR detected - Search App Property - exit network proxy list screen - No Network Proxy found - New - Changes will be reflected in future API calls - https:// - URL auto filled by Mocklets selection - Search proxy settings - Share as Text - Share as File - Copy to Clipboard - Prefer this option while sharing on Slack. - ~ Laaarge response body,\nworking hard to process it… - ~~ CONTENT TRUNCATED ~~ - + Notch settings updated + Enable Notch + Enable Draw over other Apps settings to show the notch for easy access to library. + Never ask again + Enable Now + Open Pluto + Pluto + \ No newline at end of file diff --git a/pluto/src/main/res/values/styles.xml b/pluto/src/main/res/values/styles.xml index f0939ffb..81d7afa9 100644 --- a/pluto/src/main/res/values/styles.xml +++ b/pluto/src/main/res/values/styles.xml @@ -1,45 +1,40 @@ - - + - - - - - + \ No newline at end of file diff --git a/pluto/src/test/java/com/mocklets/pluto/ExampleUnitTest.kt b/pluto/src/test/java/com/mocklets/pluto/ExampleUnitTest.kt deleted file mode 100644 index 9fafb315..00000000 --- a/pluto/src/test/java/com/mocklets/pluto/ExampleUnitTest.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.mocklets.pluto - -import org.junit.Assert.assertEquals -import org.junit.Test - -/** - * Example local unit test, which will execute on the development machine (host). - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -class ExampleUnitTest { - @Test - fun addition_isCorrect() { - assertEquals(4, 2 + 2) - } -} diff --git a/sample/build.gradle b/sample/build.gradle index e48adc89..63b419b4 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -2,8 +2,8 @@ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' android { - compileSdkVersion 29 - buildToolsVersion "29.0.3" + compileSdkVersion rootProject.compileSdkVersion + buildToolsVersion rootProject.buildToolsVersion buildFeatures { viewBinding true @@ -11,8 +11,8 @@ android { defaultConfig { applicationId "com.sampleapp" - minSdkVersion 19 - targetSdkVersion 29 + minSdkVersion rootProject.minSdkVersion + targetSdkVersion rootProject.targetSdkVersion versionCode 1 versionName "1.0" @@ -42,6 +42,9 @@ android { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation project(path: ':pluto') + // implementation project(path: ':plugin-network') + implementation project(path: ':plugin-logger') +// implementation project(path: ':plugin-shared-preferences') implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation 'androidx.appcompat:appcompat:1.3.0' diff --git a/sample/proguard-rules.pro b/sample/proguard-rules.pro index 76e097b1..a1bd10b1 100644 --- a/sample/proguard-rules.pro +++ b/sample/proguard-rules.pro @@ -32,4 +32,4 @@ -assumenosideeffects class kotlin.jvm.internal.Intrinsics { static void checkParameterIsNotNull(java.lang.Object, java.lang.String); } --keep class com.mocklets.pluto.** { *; } \ No newline at end of file +-keep class com.pluto.** { *; } \ No newline at end of file diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml index b7bcb162..b45a1745 100644 --- a/sample/src/main/AndroidManifest.xml +++ b/sample/src/main/AndroidManifest.xml @@ -14,10 +14,10 @@ - + - diff --git a/sample/src/main/java/com/sampleapp/MainActivity.kt b/sample/src/main/java/com/sampleapp/MainActivity.kt index aa55b01a..441fb536 100644 --- a/sample/src/main/java/com/sampleapp/MainActivity.kt +++ b/sample/src/main/java/com/sampleapp/MainActivity.kt @@ -1,11 +1,17 @@ package com.sampleapp import android.content.Intent +import android.net.Uri +import android.os.Build import android.os.Bundle +import android.provider.Settings import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.ViewModelProvider -import com.mocklets.pluto.PlutoLog +import com.pluto.Pluto +import com.pluto.logger.PlutoLog +import com.pluto.logger.event import com.sampleapp.databinding.ActivityMainBinding +import timber.log.Timber class MainActivity : AppCompatActivity() { @@ -16,7 +22,8 @@ class MainActivity : AppCompatActivity() { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) - PlutoLog.v(TAG_ACTION, "MainActivity onCreate") + Timber.v("MainActivity onCreate") +// PlutoLog.v(TAG_ACTION, "MainActivity onCreate") Test().javaTest() handleAPIManageCTAs() @@ -27,24 +34,34 @@ class MainActivity : AppCompatActivity() { private fun handleAppPropertiesCTAs() { binding.appPropertiesCta.setOnClickListener { - saveAppProperties() - startActivity(Intent(this, SecondActivity::class.java)) +// saveAppProperties() +// startActivity(Intent(this, SecondActivity::class.java)) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + val intent = Intent( + Settings.ACTION_MANAGE_OVERLAY_PERMISSION, + Uri.parse("package:$packageName") + ) + startActivity(intent) + } } } private fun handleSharedPrefCTAs() { binding.sharedPrefCta.setOnClickListener { resetSharedPreferences(this) + Pluto.open("sharedPref") } } private fun handleExceptionCTAs() { binding.exceptionCta.setOnClickListener { - throw NullPointerException("Custom Exception") + Pluto.showNotch(false) +// throw NullPointerException("Custom Exception") } binding.deadlockCta.setOnClickListener { - TestingThreadANR.testDeadLock() + Pluto.showNotch(true) +// TestingThreadANR.testDeadLock() } binding.sleepCta.setOnClickListener { @@ -58,42 +75,39 @@ class MainActivity : AppCompatActivity() { private fun handleAPIManageCTAs() { binding.postCall.setOnClickListener { - PlutoLog.event( - TAG_CLICK, "post_call_cta", getAttrMap() - ) - networkViewModel.post() +// Timber.e(NullPointerException("Custom Exception"), "post_call_cta") +// PlutoLog.e(TAG_CLICK, "post_call_cta", NullPointerException("Custom Exception")) + Timber.tag("analytics").event("post_call_cta_1", getAttrMap()) + PlutoLog.event(TAG_CLICK, "post_call_cta", getAttrMap()) +// networkViewModel.post() } binding.getCall.setOnClickListener { - PlutoLog.event( - TAG_CLICK, "get_call_cta", getAttrMap() - ) - networkViewModel.get() + PlutoLog.event(TAG_CLICK, "get_call_cta", getAttrMap()) +// networkViewModel.get() } binding.xmlCall.setOnClickListener { - PlutoLog.event( - TAG_CLICK, "xml_call_cta", getAttrMap() - ) + PlutoLog.event(TAG_CLICK, "xml_call_cta", getAttrMap()) networkViewModel.xml() } binding.formEncodedCall.setOnClickListener { - PlutoLog.event( - TAG_CLICK, "form_url_encoded_call_cta", getAttrMap() - ) + PlutoLog.event(TAG_CLICK, "form_url_encoded_call_cta", getAttrMap()) networkViewModel.form() } } override fun onStart() { super.onStart() - PlutoLog.v(TAG_ACTION, "MainActivity onStart") + Timber.e(NullPointerException("Custom Exception"), "MainActivity onStart") +// PlutoLog.v(TAG_ACTION, "MainActivity onStart") } override fun onResume() { super.onResume() - PlutoLog.v(TAG_ACTION, "MainActivity onResume") + Timber.tag(TAG_ACTION).d("MainActivity onResume") +// PlutoLog.v(TAG_ACTION, "MainActivity onResume") } private fun getAttrMap(): HashMap = hashMapOf( diff --git a/sample/src/main/java/com/sampleapp/SampleApp.kt b/sample/src/main/java/com/sampleapp/SampleApp.kt index 0dd17256..fdc33ba8 100644 --- a/sample/src/main/java/com/sampleapp/SampleApp.kt +++ b/sample/src/main/java/com/sampleapp/SampleApp.kt @@ -1,25 +1,21 @@ package com.sampleapp import android.app.Application -import android.util.Log -import com.mocklets.pluto.Pluto -import com.mocklets.pluto.PlutoLog -import com.mocklets.pluto.modules.exceptions.ANRException -import com.mocklets.pluto.modules.exceptions.ANRListener +import com.pluto.Pluto +import com.pluto.logger.PlutoLoggerPlugin +import com.pluto.logger.PlutoTimberTree +import timber.log.Timber class SampleApp : Application() { override fun onCreate() { super.onCreate() - Pluto.initialize(this) - Pluto.setANRListener(object : ANRListener { - override fun onAppNotResponding(exception: ANRException) { - exception.printStackTrace() - PlutoLog.e("anr-exception", exception.threadStateMap) - } - }) - Pluto.setExceptionHandler { thread, tr -> - Log.d("exception", "uncaught exception handled on thread: " + thread.name, tr) - } + Pluto.Installer(this) + .addPlugin(PlutoLoggerPlugin("logger")) +// .addPlugin(PlutoSharePreferencesPlugin("sharedPref")) + .install() + Pluto.showNotch(true) + + Timber.plant(PlutoTimberTree()) } } diff --git a/sample/src/main/java/com/sampleapp/Test.java b/sample/src/main/java/com/sampleapp/Test.java index 327435c3..de3ace1c 100644 --- a/sample/src/main/java/com/sampleapp/Test.java +++ b/sample/src/main/java/com/sampleapp/Test.java @@ -1,10 +1,9 @@ package com.sampleapp; -import com.mocklets.pluto.PlutoLog; public class Test { public void javaTest() { - PlutoLog.v("tag", "message", null); + android.util.Log.v("tag", "message", null); } } diff --git a/sample/src/main/java/com/sampleapp/TestingThreadANR.kt b/sample/src/main/java/com/sampleapp/TestingThreadANR.kt index 1b72cf13..83c16d9f 100644 --- a/sample/src/main/java/com/sampleapp/TestingThreadANR.kt +++ b/sample/src/main/java/com/sampleapp/TestingThreadANR.kt @@ -2,7 +2,6 @@ package com.sampleapp import android.os.Handler import android.util.Log -import com.mocklets.pluto.PlutoLog class TestingThreadANR : Thread() { @@ -37,7 +36,7 @@ class TestingThreadANR : Thread() { Handler().postDelayed( { synchronized(mutex) { - PlutoLog.e("ANR-Failed", "There should be a dead lock before this message") + Log.e("ANR-Failed", "There should be a dead lock before this message") } }, DEADLOCK_DURATION diff --git a/sample/src/main/java/com/sampleapp/Utils.kt b/sample/src/main/java/com/sampleapp/Utils.kt index c9e550fd..45d9a872 100644 --- a/sample/src/main/java/com/sampleapp/Utils.kt +++ b/sample/src/main/java/com/sampleapp/Utils.kt @@ -2,7 +2,6 @@ package com.sampleapp import android.content.Context import android.preference.PreferenceManager -import com.mocklets.pluto.Pluto @Suppress("MagicNumber") fun resetSharedPreferences(context: Context) { @@ -23,13 +22,13 @@ fun resetSharedPreferences(context: Context) { } fun saveAppProperties() { - Pluto.setAppProperties( - hashMapOf( - "user_name" to "John Smith", - "user_id" to "8060823b-ab8f-4f9b-bc4d-ec1acd290f23", - "user_location" to "Bangalore, India", - "user_email" to "john.smith@gmail.com", - "device_imei" to "49015420323751" - ) - ) +// Pluto.setAppProperties( +// hashMapOf( +// "user_name" to "John Smith", +// "user_id" to "8060823b-ab8f-4f9b-bc4d-ec1acd290f23", +// "user_location" to "Bangalore, India", +// "user_email" to "john.smith@gmail.com", +// "device_imei" to "49015420323751" +// ) +// ) } diff --git a/sample/src/main/java/com/sampleapp/network/Network.kt b/sample/src/main/java/com/sampleapp/network/Network.kt index 9250faf6..231938ba 100644 --- a/sample/src/main/java/com/sampleapp/network/Network.kt +++ b/sample/src/main/java/com/sampleapp/network/Network.kt @@ -1,7 +1,6 @@ package com.sampleapp.network import com.google.gson.GsonBuilder -import com.mocklets.pluto.PlutoInterceptor import java.util.concurrent.TimeUnit import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor @@ -39,6 +38,6 @@ object Network { private fun OkHttpClient.Builder.addInterceptors(): OkHttpClient.Builder { // addInterceptor(GzipRequestInterceptor()) addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)) - addInterceptor(PlutoInterceptor()) +// addInterceptor(PlutoInterceptor()) return this } diff --git a/sample/src/main/java/com/sampleapp/network/NetworkCalls.kt b/sample/src/main/java/com/sampleapp/network/NetworkCalls.kt index 11163e57..1b937e52 100644 --- a/sample/src/main/java/com/sampleapp/network/NetworkCalls.kt +++ b/sample/src/main/java/com/sampleapp/network/NetworkCalls.kt @@ -1,7 +1,7 @@ package com.sampleapp.network import com.google.gson.Gson -import com.mocklets.pluto.PlutoLog +import com.pluto.plugin.utilities.DebugLog import java.io.IOException import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers @@ -18,7 +18,7 @@ suspend fun enqueue( try { ResponseWrapper.Success(apiCall.invoke()) } catch (throwable: Throwable) { - PlutoLog.e(tag = "network_error", message = "network failure", tr = throwable) + DebugLog.e(tag = "network_error", message = "network failure", tr = throwable) when (throwable) { is IOException -> ResponseWrapper.Failure( ErrorResponse( @@ -45,7 +45,7 @@ private fun convertErrorBody(throwable: HttpException): ErrorResponse { error } } catch (exception: Exception) { - PlutoLog.e( + DebugLog.e( tag = "network_error", message = exception.message.toString(), tr = exception diff --git a/build-utils.gradle b/scripts/build/utils.gradle similarity index 100% rename from build-utils.gradle rename to scripts/build/utils.gradle diff --git a/scripts/publish-module.gradle b/scripts/publish/module.gradle similarity index 98% rename from scripts/publish-module.gradle rename to scripts/publish/module.gradle index 5d883508..c3c8f5dd 100644 --- a/scripts/publish-module.gradle +++ b/scripts/publish/module.gradle @@ -48,7 +48,7 @@ afterEvaluate { pom { name = PUBLISH_ARTIFACT_ID description = 'Pluto is a on-device debugger for Android applications, which helps in inspection of HTTP requests/responses, capture Crashes and ANRs and manipulating application data on-the-go.' - url = 'https://pluto.mocklets.com' + url = 'https://plutolib.com' licenses { license { name = 'The Apache Software License, Version 2.0' diff --git a/scripts/publish-root.gradle b/scripts/publish/root.gradle similarity index 100% rename from scripts/publish-root.gradle rename to scripts/publish/root.gradle diff --git a/static-analysis/.editorconfig b/scripts/static-analysis/.editorconfig similarity index 100% rename from static-analysis/.editorconfig rename to scripts/static-analysis/.editorconfig diff --git a/static-analysis/android-lint-config.xml b/scripts/static-analysis/android-lint-config.xml similarity index 100% rename from static-analysis/android-lint-config.xml rename to scripts/static-analysis/android-lint-config.xml diff --git a/static-analysis/code-analysis.gradle b/scripts/static-analysis/code-analysis.gradle similarity index 84% rename from static-analysis/code-analysis.gradle rename to scripts/static-analysis/code-analysis.gradle index ee9c33ae..a5613b0d 100644 --- a/static-analysis/code-analysis.gradle +++ b/scripts/static-analysis/code-analysis.gradle @@ -4,7 +4,7 @@ apply plugin: "org.jlleitschuh.gradle.ktlint" afterEvaluate { detekt { input = files("src/main/java") - config = files("$rootDir/static-analysis/detekt-config.yml") + config = files("$rootDir/scripts/static-analysis/detekt-config.yml") reports { xml { enabled = true @@ -24,7 +24,7 @@ afterEvaluate { outputColorName.set("RED") outputToConsole.set(true) ignoreFailures.set(false) - additionalEditorconfigFile.set(file("$rootDir/static-analysis/.editorconfig")) + additionalEditorconfigFile.set(file("$rootDir/scripts/static-analysis/.editorconfig")) reporters { reporter("html") diff --git a/static-analysis/detekt-config.yml b/scripts/static-analysis/detekt-config.yml similarity index 100% rename from static-analysis/detekt-config.yml rename to scripts/static-analysis/detekt-config.yml diff --git a/settings.gradle b/settings.gradle index 0ce18318..f747a6c7 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,4 +1,5 @@ rootProject.name='pluto-sample' include ':sample' include ':pluto' -include ':pluto-no-op' +include ':pluto-plugin' +include ':plugin-logger' From 46615d1d42610ef5f31ed979acc431039037b55a Mon Sep 17 00:00:00 2001 From: srtvprateek Date: Wed, 9 Feb 2022 23:36:22 +0530 Subject: [PATCH 002/223] pluto logger removed --- plugin-logger/.gitignore | 1 - plugin-logger/build.gradle | 70 -------- plugin-logger/src/main/AndroidManifest.xml | 5 - .../main/java/com/pluto/logger/PlutoLog.kt | 57 ------- .../com/pluto/logger/PlutoLoggerPlugin.kt | 32 ---- .../java/com/pluto/logger/PlutoTimberTree.kt | 77 --------- .../com/pluto/logger/internal/DataModel.kt | 32 ---- .../com/pluto/logger/internal/LogsFragment.kt | 6 - .../pluto/logger/internal/LogsProcessor.kt | 71 -------- .../com/pluto/logger/internal/LogsRepo.kt | 42 ----- .../pluto/logger/internal/LogsViewModel.kt | 18 -- .../java/com/pluto/logger/internal/Session.kt | 5 - .../com/pluto/logger/internal/ThrowableKtx.kt | 56 ------- .../logger/internal/ui/DetailsFragment.kt | 137 --------------- .../pluto/logger/internal/ui/ListFragment.kt | 104 ------------ .../logger/internal/ui/list/LogItemHolder.kt | 57 ------- .../logger/internal/ui/list/LogsAdapter.kt | 27 --- .../pluto_logger___bg_bottom_sheet.xml | 21 --- ...pluto_logger___bg_bottom_sheet_content.xml | 21 --- .../drawable/pluto_logger___ic_analytics.xml | 9 - .../drawable/pluto_logger___ic_clear_all.xml | 13 -- .../res/drawable/pluto_logger___ic_label.xml | 11 -- .../pluto_logger___ic_logger_icon.xml | 10 -- .../pluto_logger___fragment_details.xml | 156 ------------------ .../layout/pluto_logger___fragment_list.xml | 140 ---------------- .../layout/pluto_logger___fragment_logs.xml | 15 -- .../res/layout/pluto_logger___list_item.xml | 52 ------ .../menu/pluto_logger___menu_more_options.xml | 7 - .../navigation/pluto_logger___navigation.xml | 21 --- plugin-logger/src/main/res/values/strings.xml | 15 -- plugin-logger/src/main/res/values/styles.xml | 21 --- sample/build.gradle | 2 +- .../main/java/com/sampleapp/MainActivity.kt | 41 ++--- .../src/main/java/com/sampleapp/SampleApp.kt | 7 +- settings.gradle | 1 - 35 files changed, 16 insertions(+), 1344 deletions(-) delete mode 100644 plugin-logger/.gitignore delete mode 100644 plugin-logger/build.gradle delete mode 100644 plugin-logger/src/main/AndroidManifest.xml delete mode 100644 plugin-logger/src/main/java/com/pluto/logger/PlutoLog.kt delete mode 100644 plugin-logger/src/main/java/com/pluto/logger/PlutoLoggerPlugin.kt delete mode 100644 plugin-logger/src/main/java/com/pluto/logger/PlutoTimberTree.kt delete mode 100644 plugin-logger/src/main/java/com/pluto/logger/internal/DataModel.kt delete mode 100644 plugin-logger/src/main/java/com/pluto/logger/internal/LogsFragment.kt delete mode 100644 plugin-logger/src/main/java/com/pluto/logger/internal/LogsProcessor.kt delete mode 100644 plugin-logger/src/main/java/com/pluto/logger/internal/LogsRepo.kt delete mode 100644 plugin-logger/src/main/java/com/pluto/logger/internal/LogsViewModel.kt delete mode 100644 plugin-logger/src/main/java/com/pluto/logger/internal/Session.kt delete mode 100644 plugin-logger/src/main/java/com/pluto/logger/internal/ThrowableKtx.kt delete mode 100644 plugin-logger/src/main/java/com/pluto/logger/internal/ui/DetailsFragment.kt delete mode 100644 plugin-logger/src/main/java/com/pluto/logger/internal/ui/ListFragment.kt delete mode 100644 plugin-logger/src/main/java/com/pluto/logger/internal/ui/list/LogItemHolder.kt delete mode 100644 plugin-logger/src/main/java/com/pluto/logger/internal/ui/list/LogsAdapter.kt delete mode 100644 plugin-logger/src/main/res/drawable/pluto_logger___bg_bottom_sheet.xml delete mode 100644 plugin-logger/src/main/res/drawable/pluto_logger___bg_bottom_sheet_content.xml delete mode 100644 plugin-logger/src/main/res/drawable/pluto_logger___ic_analytics.xml delete mode 100644 plugin-logger/src/main/res/drawable/pluto_logger___ic_clear_all.xml delete mode 100644 plugin-logger/src/main/res/drawable/pluto_logger___ic_label.xml delete mode 100644 plugin-logger/src/main/res/drawable/pluto_logger___ic_logger_icon.xml delete mode 100644 plugin-logger/src/main/res/layout/pluto_logger___fragment_details.xml delete mode 100644 plugin-logger/src/main/res/layout/pluto_logger___fragment_list.xml delete mode 100644 plugin-logger/src/main/res/layout/pluto_logger___fragment_logs.xml delete mode 100644 plugin-logger/src/main/res/layout/pluto_logger___list_item.xml delete mode 100644 plugin-logger/src/main/res/menu/pluto_logger___menu_more_options.xml delete mode 100644 plugin-logger/src/main/res/navigation/pluto_logger___navigation.xml delete mode 100644 plugin-logger/src/main/res/values/strings.xml delete mode 100644 plugin-logger/src/main/res/values/styles.xml diff --git a/plugin-logger/.gitignore b/plugin-logger/.gitignore deleted file mode 100644 index 42afabfd..00000000 --- a/plugin-logger/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build \ No newline at end of file diff --git a/plugin-logger/build.gradle b/plugin-logger/build.gradle deleted file mode 100644 index c7a5673c..00000000 --- a/plugin-logger/build.gradle +++ /dev/null @@ -1,70 +0,0 @@ -plugins { - id 'com.android.library' - id 'kotlin-android' - id 'kotlin-kapt' -} - -apply from: "$rootDir/scripts/build/utils.gradle" -apply from: "$rootDir/scripts/publish/module.gradle" - -def verCode, verName, verBuild, verNameShort, verPublish -(verCode, verName, verBuild, verNameShort, verPublish) = genVersion() - -ext { - PUBLISH_GROUP_ID = "com.plutolib.plugins" - PUBLISH_VERSION = verPublish - PUBLISH_ARTIFACT_ID = 'logger' -} - -android { - compileSdkVersion rootProject.compileSdkVersion - buildToolsVersion rootProject.buildToolsVersion - - buildFeatures { - viewBinding true - } - - lintOptions { - abortOnError false - } - - defaultConfig { - minSdkVersion rootProject.minSdkVersion - targetSdkVersion rootProject.targetSdkVersion - versionCode verCode - versionName verName - } - - buildTypes { - release { - debuggable true - minifyEnabled false - shrinkResources false - } - } - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - - kotlinOptions { - jvmTarget = JavaVersion.VERSION_1_8.toString() - } - - resourcePrefix 'pluto_logger___' -} - -dependencies { - implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation project(path: ':pluto-plugin') - implementation 'androidx.navigation:navigation-ui-ktx:2.4.0' - api 'com.jakewharton.timber:timber:5.0.1' - implementation "com.squareup.moshi:moshi:$moshiVersion" - kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshiVersion" -} - -task validateChanges { - dependsOn 'ktlintFormat' - dependsOn 'detekt' -} \ No newline at end of file diff --git a/plugin-logger/src/main/AndroidManifest.xml b/plugin-logger/src/main/AndroidManifest.xml deleted file mode 100644 index 7e2e2b31..00000000 --- a/plugin-logger/src/main/AndroidManifest.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/plugin-logger/src/main/java/com/pluto/logger/PlutoLog.kt b/plugin-logger/src/main/java/com/pluto/logger/PlutoLog.kt deleted file mode 100644 index ae70b605..00000000 --- a/plugin-logger/src/main/java/com/pluto/logger/PlutoLog.kt +++ /dev/null @@ -1,57 +0,0 @@ -package com.pluto.logger - -import android.util.Log -import androidx.annotation.Keep -import com.pluto.logger.internal.LogsProcessor.Companion.process -import com.pluto.logger.internal.LogsProcessor.Companion.processEvent -import com.pluto.logger.internal.LogsProcessor.Companion.stackTraceElement - -@Keep -class PlutoLog private constructor() { - - companion object { - - @JvmStatic - fun v(tag: String, message: String, tr: Throwable? = null) { - process(Log.VERBOSE, tag, message, tr, Thread.currentThread().getStackTraceElement()) - } - - @JvmStatic - fun d(tag: String, message: String, tr: Throwable? = null) { - process(Log.DEBUG, tag, message, tr, Thread.currentThread().getStackTraceElement()) - } - - @JvmStatic - fun i(tag: String, message: String, tr: Throwable? = null) { - process(Log.INFO, tag, message, tr, Thread.currentThread().getStackTraceElement()) - } - - @JvmStatic - fun w(tag: String, message: String, tr: Throwable? = null) { - process(Log.WARN, tag, message, tr, Thread.currentThread().getStackTraceElement()) - } - - @JvmStatic - fun e(tag: String, message: String, tr: Throwable? = null) { - process(Log.ERROR, tag, message, tr, Thread.currentThread().getStackTraceElement()) - } - - @JvmStatic - fun wtf(tag: String, message: String, tr: Throwable? = null) { - process(Log.ASSERT, tag, message, tr, Thread.currentThread().getStackTraceElement()) - } - - @JvmStatic - fun event(tag: String, event: String, attributes: HashMap?) { - processEvent(tag, event, attributes, Thread.currentThread().getStackTraceElement()) - } - - private fun Thread.getStackTraceElement(): StackTraceElement { - val index = if (name == "main") MAIN_THREAD_INDEX else DAEMON_THREAD_INDEX - return stackTraceElement(index) - } - - private const val MAIN_THREAD_INDEX = 6 - private const val DAEMON_THREAD_INDEX = 5 - } -} diff --git a/plugin-logger/src/main/java/com/pluto/logger/PlutoLoggerPlugin.kt b/plugin-logger/src/main/java/com/pluto/logger/PlutoLoggerPlugin.kt deleted file mode 100644 index 6e9ed915..00000000 --- a/plugin-logger/src/main/java/com/pluto/logger/PlutoLoggerPlugin.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.pluto.logger - -import androidx.fragment.app.Fragment -import com.pluto.logger.internal.LogsFragment -import com.pluto.logger.internal.LogsRepo -import com.pluto.plugin.DeveloperDetails -import com.pluto.plugin.Plugin -import com.pluto.plugin.PluginConfiguration - -class PlutoLoggerPlugin(identifier: String) : Plugin(identifier) { - - override fun getConfig(): PluginConfiguration = PluginConfiguration( - name = context.getString(R.string.pluto_logger___plugin_name), - icon = R.drawable.pluto_logger___ic_logger_icon - ) - - override fun getView(): Fragment = LogsFragment() - - override fun getDeveloperDetails(): DeveloperDetails { - return DeveloperDetails( - website = "https://plutolib.com", - vcsLink = "https://github.com/plutolib/plugin-logger" - ) - } - - override fun onPluginInstalled() { - } - - override fun onPluginDataCleared() { - LogsRepo.deleteAll() - } -} diff --git a/plugin-logger/src/main/java/com/pluto/logger/PlutoTimberTree.kt b/plugin-logger/src/main/java/com/pluto/logger/PlutoTimberTree.kt deleted file mode 100644 index ce8fa6e2..00000000 --- a/plugin-logger/src/main/java/com/pluto/logger/PlutoTimberTree.kt +++ /dev/null @@ -1,77 +0,0 @@ -package com.pluto.logger - -import com.pluto.logger.internal.LogsProcessor -import com.pluto.logger.internal.LogsProcessor.Companion.LOG_EVENT_PRIORITY -import com.pluto.logger.internal.LogsProcessor.Companion.stackTraceElement -import com.squareup.moshi.JsonAdapter -import com.squareup.moshi.Moshi -import com.squareup.moshi.Types -import java.math.BigDecimal -import timber.log.Timber - -class PlutoTimberTree : Timber.Tree() { - - override fun log(priority: Int, tag: String?, message: String, t: Throwable?) { - if (priority == LOG_EVENT_PRIORITY) { - val eventData = eventExtractor(message) - LogsProcessor.processEvent(tag ?: "pluto_timber", eventData.first, eventData.second, Thread.currentThread().getStackTraceElement()) - } else { - LogsProcessor.process(priority, tag ?: "pluto_timber", messageExtractor(message), t, Thread.currentThread().getStackTraceElement()) - } - } - - @SuppressWarnings("NestedBlockDepth") - private fun eventExtractor(message: String): Pair?> { - val length = message.length - var newline = message.indexOf('\t', 0) - newline = if (newline != -1) newline else length - val end = newline.coerceAtMost(MAX_LOG_LENGTH) - - val event = message.substring(0, end) - val attrString = if (end < length) { - val moshi = Moshi.Builder().build() - val moshiAdapter: JsonAdapter?> = moshi.adapter(Types.newParameterizedType(Map::class.java, String::class.java, Any::class.java)) - val map = moshiAdapter.fromJson(message.substring(end + 1, length)) - val hashMap = HashMap() - if (!map.isNullOrEmpty()) { - map.entries.forEach { - hashMap[it.key] = when (it.value) { - is Double -> BigDecimal(it.value.toString()).longValueExact() - else -> it.value - } - } - hashMap - } else { - null - } - } else { - null - } - return Pair(event, attrString) - } - - private fun messageExtractor(message: String): String { - val length = message.length - var newline = message.indexOf('\n', 0) - newline = if (newline != -1) newline else length - val end = newline.coerceAtMost(MAX_LOG_LENGTH) - return message.substring(0, end) - } - - private fun Thread.getStackTraceElement(): StackTraceElement { - val index = if (name == "main") MAIN_THREAD_INDEX else DAEMON_THREAD_INDEX - return stackTraceElement(index) - } - - companion object { - private const val MAX_LOG_LENGTH = 4000 - private const val MAIN_THREAD_INDEX = 9 - private const val DAEMON_THREAD_INDEX = 8 - } -} - -fun Timber.Tree.event(event: String, attr: HashMap?) { - val moshi = Moshi.Builder().build() - val moshiAdapter: JsonAdapter?> = moshi.adapter(Types.newParameterizedType(Map::class.java, String::class.java, Any::class.java)) - log(LOG_EVENT_PRIORITY, "$event\t${moshiAdapter.toJson(attr)}") -} diff --git a/plugin-logger/src/main/java/com/pluto/logger/internal/DataModel.kt b/plugin-logger/src/main/java/com/pluto/logger/internal/DataModel.kt deleted file mode 100644 index a0094a08..00000000 --- a/plugin-logger/src/main/java/com/pluto/logger/internal/DataModel.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.pluto.logger.internal - -import androidx.annotation.DrawableRes -import androidx.annotation.Keep -import com.pluto.logger.R -import com.pluto.plugin.utilities.list.ListItem - -internal sealed class Level( - val label: String, - val color: Int = R.color.pluto___transparent, - val textColor: Int = R.color.pluto___text_dark_60, - @DrawableRes val iconRes: Int = 0 -) { - object Verbose : Level("verbose") - object Debug : Level("debug") - object Info : Level("info") - object Warning : Level("warning") - object WTF : Level("wtf") - object Error : Level("error", R.color.pluto___red_05, R.color.pluto___red_80) - object Event : Level(label = "event", iconRes = R.drawable.pluto_logger___ic_analytics, textColor = R.color.pluto___blue) -} - -@Keep -internal data class LogData( - val level: Level, - val tag: String, - val message: String, - val tr: Throwable? = null, - val stackTraceElement: StackTraceElement, - val eventAttributes: HashMap? = null, - val timeStamp: Long = System.currentTimeMillis() -) : ListItem() diff --git a/plugin-logger/src/main/java/com/pluto/logger/internal/LogsFragment.kt b/plugin-logger/src/main/java/com/pluto/logger/internal/LogsFragment.kt deleted file mode 100644 index 8916de06..00000000 --- a/plugin-logger/src/main/java/com/pluto/logger/internal/LogsFragment.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.pluto.logger.internal - -import androidx.fragment.app.Fragment -import com.pluto.logger.R - -internal class LogsFragment : Fragment(R.layout.pluto_logger___fragment_logs) diff --git a/plugin-logger/src/main/java/com/pluto/logger/internal/LogsProcessor.kt b/plugin-logger/src/main/java/com/pluto/logger/internal/LogsProcessor.kt deleted file mode 100644 index 0e4a793c..00000000 --- a/plugin-logger/src/main/java/com/pluto/logger/internal/LogsProcessor.kt +++ /dev/null @@ -1,71 +0,0 @@ -package com.pluto.logger.internal - -import android.util.Log -import androidx.annotation.Keep -import com.pluto.logger.BuildConfig -import com.squareup.moshi.JsonAdapter -import com.squareup.moshi.Moshi -import com.squareup.moshi.Types - -internal class LogsProcessor private constructor() { - - companion object { - - fun process(priority: Int, tag: String, message: String, tr: Throwable?, stackTrace: StackTraceElement) { - LogsRepo.save(priority2Level(priority), tag, message, tr, stackTrace) - consolePrint(priority2Level(priority), tag, message, tr, stackTrace) - } - - fun processEvent(tag: String, event: String, attr: HashMap?, stackTrace: StackTraceElement) { - val moshi = Moshi.Builder().build() - val moshiAdapter: JsonAdapter?> = moshi.adapter(Types.newParameterizedType(Map::class.java, String::class.java, Any::class.java)) - LogsRepo.saveEvent(Level.Event, tag, event, attr, stackTrace) - consolePrint(Level.Event, tag, "$event => ${moshiAdapter.toJson(attr)}", null, stackTrace) - } - - @SuppressWarnings("ComplexCondition") - fun Thread.stackTraceElement(index: Int): StackTraceElement { - stackTrace.forEach { - if (!it.className.startsWith(BuildConfig.LIBRARY_PACKAGE_NAME) && - !it.className.startsWith("java.lang.") && - !it.className.startsWith("dalvik.system.") && - !it.fileName.startsWith("Timber.kt") - ) { - return it - } - } - return stackTrace[index] - } - - private fun priority2Level(priority: Int): Level { - return when (priority) { - Log.DEBUG -> Level.Debug - Log.ERROR -> Level.Error - Log.INFO -> Level.Info - Log.VERBOSE -> Level.Verbose - Log.WARN -> Level.Warning - Log.ASSERT -> Level.WTF - LOG_EVENT_PRIORITY -> Level.Event - else -> Level.Debug - } - } - - private fun consolePrint(level: Level, tag: String, message: String, tr: Throwable?, trace: StackTraceElement) { - val logTag = "${trace.formattedStack()} | $tag" - when (level) { - is Level.Debug -> Log.v(logTag, message, tr) - is Level.Error -> Log.e(logTag, message, tr) - is Level.Info -> Log.i(logTag, message, tr) - is Level.Warning -> Log.w(logTag, message, tr) - is Level.Verbose -> Log.v(logTag, message, tr) - is Level.WTF -> Log.wtf(logTag, message, tr) - is Level.Event -> Log.d(logTag, message) - } - } - - @Keep - fun StackTraceElement.formattedStack(): String = "$methodName($fileName:$lineNumber)" - - const val LOG_EVENT_PRIORITY = 101 - } -} diff --git a/plugin-logger/src/main/java/com/pluto/logger/internal/LogsRepo.kt b/plugin-logger/src/main/java/com/pluto/logger/internal/LogsRepo.kt deleted file mode 100644 index dc130f27..00000000 --- a/plugin-logger/src/main/java/com/pluto/logger/internal/LogsRepo.kt +++ /dev/null @@ -1,42 +0,0 @@ -package com.pluto.logger.internal - -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData - -internal object LogsRepo { - - internal val logs: LiveData> - get() = _logs - private val _logs = MutableLiveData>() - private val logsList = arrayListOf() - private const val MAX_LIMIT = 256 - - fun save(level: Level, tag: String, message: String?, tr: Throwable?, ele: StackTraceElement) { - synchronized(logsList) { - logsList.add(0, LogData(level, tag, message ?: "", tr, ele)) - val temp = logsList.take(MAX_LIMIT) - logsList.clear() - logsList.addAll(temp) - _logs.postValue(logsList) - } - } - - fun saveEvent( - level: Level, - tag: String, - event: String?, - attributes: HashMap?, - ele: StackTraceElement - ) { - logsList.add(0, LogData(level, tag, event ?: "", null, ele, attributes)) - val temp = logsList.take(MAX_LIMIT) - logsList.clear() - logsList.addAll(temp) - _logs.postValue(logsList) - } - - fun deleteAll() { - logsList.clear() - _logs.postValue(logsList) - } -} diff --git a/plugin-logger/src/main/java/com/pluto/logger/internal/LogsViewModel.kt b/plugin-logger/src/main/java/com/pluto/logger/internal/LogsViewModel.kt deleted file mode 100644 index 446a8797..00000000 --- a/plugin-logger/src/main/java/com/pluto/logger/internal/LogsViewModel.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.pluto.logger.internal - -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel - -internal class LogsViewModel : ViewModel() { - val logs: LiveData> - get() = LogsRepo.logs - - val current: LiveData - get() = _current - private val _current = MutableLiveData() - - internal fun updateCurrentLog(data: LogData) { - _current.postValue(data) - } -} diff --git a/plugin-logger/src/main/java/com/pluto/logger/internal/Session.kt b/plugin-logger/src/main/java/com/pluto/logger/internal/Session.kt deleted file mode 100644 index 555c92cf..00000000 --- a/plugin-logger/src/main/java/com/pluto/logger/internal/Session.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.pluto.logger.internal - -internal object Session { - var loggerSearchText: String? = null -} diff --git a/plugin-logger/src/main/java/com/pluto/logger/internal/ThrowableKtx.kt b/plugin-logger/src/main/java/com/pluto/logger/internal/ThrowableKtx.kt deleted file mode 100644 index 3d51bcf2..00000000 --- a/plugin-logger/src/main/java/com/pluto/logger/internal/ThrowableKtx.kt +++ /dev/null @@ -1,56 +0,0 @@ -package com.pluto.logger.internal - -import android.content.Context -import androidx.annotation.Keep -import com.pluto.logger.R -import com.pluto.plugin.utilities.extensions.color -import com.pluto.plugin.utilities.list.ListItem -import com.pluto.plugin.utilities.spannable.createSpan - -internal fun Throwable.asExceptionData(isANR: Boolean = false): ExceptionData { - return ExceptionData( - name = this.toString().replace(": $message", "", true), - message = message, - stackTrace = stackTrace.asStringArray(), - file = stackTrace.getOrNull(0)?.fileName, - lineNumber = stackTrace.getOrNull(0)?.lineNumber ?: Int.MIN_VALUE, - isANRException = isANR - ) -} - -@Keep -internal data class ExceptionData( - val message: String?, - val name: String?, - val file: String?, - val lineNumber: Int, - val stackTrace: ArrayList, - val timeStamp: Long = System.currentTimeMillis(), - val isANRException: Boolean = false -) : ListItem() - -internal fun Array.asStringArray(): ArrayList { - val array = arrayListOf() - forEach { - if (it.isNativeMethod) { - array.add("${it.className}.${it.methodName}(Native Method)") - } else { - array.add("${it.className}.${it.methodName}(${it.fileName}:${it.lineNumber})") - } - } - return array -} - -internal fun Context?.beautifyAttributes(data: Map): CharSequence? { - return this?.createSpan { - data.forEach { - append("${it.key} : ") - if (it.value != null) { - append(fontColor(semiBold("${it.value}"), context.color(R.color.pluto___text_dark_80))) - } else { - append(fontColor(light(italic("null")), context.color(R.color.pluto___text_dark_40))) - } - append("\n") - } - }?.trim() -} diff --git a/plugin-logger/src/main/java/com/pluto/logger/internal/ui/DetailsFragment.kt b/plugin-logger/src/main/java/com/pluto/logger/internal/ui/DetailsFragment.kt deleted file mode 100644 index 6314e497..00000000 --- a/plugin-logger/src/main/java/com/pluto/logger/internal/ui/DetailsFragment.kt +++ /dev/null @@ -1,137 +0,0 @@ -package com.pluto.logger.internal.ui - -import android.content.Context -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.activity.OnBackPressedCallback -import androidx.fragment.app.activityViewModels -import androidx.lifecycle.Observer -import androidx.navigation.fragment.findNavController -import com.google.android.material.bottomsheet.BottomSheetDialogFragment -import com.pluto.logger.R -import com.pluto.logger.databinding.PlutoLoggerFragmentDetailsBinding -import com.pluto.logger.internal.LogData -import com.pluto.logger.internal.LogsViewModel -import com.pluto.logger.internal.asExceptionData -import com.pluto.logger.internal.beautifyAttributes -import com.pluto.logger.internal.ui.DetailsFragment.Companion.MAX_STACK_TRACE_LINES -import com.pluto.plugin.utilities.extensions.color -import com.pluto.plugin.utilities.setDebounceClickListener -import com.pluto.plugin.utilities.sharing.Shareable -import com.pluto.plugin.utilities.sharing.lazyContentSharer -import com.pluto.plugin.utilities.spannable.setSpan -import com.pluto.plugin.utilities.viewBinding - -internal class DetailsFragment : BottomSheetDialogFragment() { - - private val binding by viewBinding(PlutoLoggerFragmentDetailsBinding::bind) - private val viewModel: LogsViewModel by activityViewModels() - private val contentSharer by lazyContentSharer() - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = - inflater.inflate(R.layout.pluto_logger___fragment_details, container, false) - - override fun getTheme(): Int = R.style.PlutoLoggerBottomSheetDialog - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - requireActivity().onBackPressedDispatcher.addCallback( - viewLifecycleOwner, - object : OnBackPressedCallback(true) { - override fun handleOnBackPressed() { - findNavController().navigateUp() - } - } - ) - -// binding.text.setDebounceClickListener { -// findNavController().popBackStack() -// } - - viewModel.current.removeObserver(detailsObserver) - viewModel.current.observe(viewLifecycleOwner, detailsObserver) - } - - private val detailsObserver = Observer { data -> - binding.title.setSpan { - append("${context.getString(R.string.pluto_logger___log_details)} ") - append(italic(fontColor(data.level.label.uppercase(), context.color(data.level.textColor)))) - } - - binding.cta.setDebounceClickListener { - context?.let { - contentSharer.share(Shareable(title = "Share Log details", content = data.toShareText(it))) - } - } - - binding.tag.text = data.tag - binding.filename.setSpan { - append(fontColor("called from\n", context.color(R.color.pluto___text_dark_40))) - append(data.stackTraceElement.methodName) - append(fontColor(" (", context.color(R.color.pluto___text_dark_40))) - append(data.stackTraceElement.fileName) - append(fontColor(", line:", context.color(R.color.pluto___text_dark_60))) - append(fontColor("${data.stackTraceElement.lineNumber}", context.color(R.color.pluto___text_dark_80))) - append(fontColor(")", context.color(R.color.pluto___text_dark_40))) - } - binding.message.text = data.message - binding.stackTraceContainer.visibility = View.GONE - data.tr?.asExceptionData()?.let { - binding.stackTraceContainer.visibility = View.VISIBLE - binding.stackTrace.setSpan { - append(fontColor("${it.name}: ${it.message}", context.color(R.color.pluto___text_dark_80))) - it.stackTrace.take(MAX_STACK_TRACE_LINES).forEach { - append("\n\t\t\t") - append(fontColor(" at ", context.color(R.color.pluto___text_dark_40))) - append(it) - } - val extraTrace = it.stackTrace.size - MAX_STACK_TRACE_LINES - if (extraTrace > 0) { - append( - fontColor( - "\n\t\t\t + $extraTrace more lines", context.color(R.color.pluto___text_dark_40) - ) - ) - } - } - } - - if (!data.eventAttributes.isNullOrEmpty()) { - binding.stackTraceContainer.visibility = View.VISIBLE - binding.stackTraceTitle.setSpan { - append(context.getString(R.string.pluto_logger___event_attributes)) - append(fontColor(" (${data.eventAttributes.size})", context.color(R.color.pluto___text_dark_40))) - } - binding.stackTrace.text = context.beautifyAttributes(data.eventAttributes) - } - } - - companion object { - const val MAX_STACK_TRACE_LINES = 15 - } -} - -private fun LogData.toShareText(context: Context): String { - val text = StringBuilder() - text.append("$tag : $message\n") - - tr?.asExceptionData()?.let { - text.append("\n${it.name}: ${it.message}\n") - it.stackTrace.take(MAX_STACK_TRACE_LINES).forEach { trace -> - text.append("\t at $trace\n") - } - if (it.stackTrace.size - MAX_STACK_TRACE_LINES > 0) { - text.append("\t + ${it.stackTrace.size - MAX_STACK_TRACE_LINES} more lines\n\n") - } - } - - if (!eventAttributes.isNullOrEmpty()) { - text.append("\n${context.getString(R.string.pluto_logger___event_attributes).lowercase()} - ") - eventAttributes.entries.forEach { - text.append("\n\t ${it.key} : ${it.value}") - } - } - return text.toString() -} diff --git a/plugin-logger/src/main/java/com/pluto/logger/internal/ui/ListFragment.kt b/plugin-logger/src/main/java/com/pluto/logger/internal/ui/ListFragment.kt deleted file mode 100644 index e6a3eb65..00000000 --- a/plugin-logger/src/main/java/com/pluto/logger/internal/ui/ListFragment.kt +++ /dev/null @@ -1,104 +0,0 @@ -package com.pluto.logger.internal.ui - -import android.os.Bundle -import android.view.View -import androidx.core.widget.doOnTextChanged -import androidx.fragment.app.Fragment -import androidx.fragment.app.activityViewModels -import androidx.lifecycle.Observer -import androidx.lifecycle.lifecycleScope -import androidx.navigation.fragment.findNavController -import com.pluto.logger.R -import com.pluto.logger.databinding.PlutoLoggerFragmentListBinding -import com.pluto.logger.internal.LogData -import com.pluto.logger.internal.LogsRepo -import com.pluto.logger.internal.LogsViewModel -import com.pluto.logger.internal.Session -import com.pluto.logger.internal.ui.list.LogsAdapter -import com.pluto.plugin.utilities.extensions.hideKeyboard -import com.pluto.plugin.utilities.extensions.linearLayoutManager -import com.pluto.plugin.utilities.extensions.showMoreOptions -import com.pluto.plugin.utilities.list.BaseAdapter -import com.pluto.plugin.utilities.list.CustomItemDecorator -import com.pluto.plugin.utilities.list.DiffAwareAdapter -import com.pluto.plugin.utilities.list.DiffAwareHolder -import com.pluto.plugin.utilities.list.ListItem -import com.pluto.plugin.utilities.setDebounceClickListener -import com.pluto.plugin.utilities.viewBinding - -internal class ListFragment : Fragment(R.layout.pluto_logger___fragment_list) { - - private val binding by viewBinding(PlutoLoggerFragmentListBinding::bind) - private val viewModel: LogsViewModel by activityViewModels() - private val logsAdapter: BaseAdapter by lazy { LogsAdapter(onActionListener) } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - binding.logList.apply { - adapter = logsAdapter - addItemDecoration(CustomItemDecorator(requireContext())) - } - binding.search.doOnTextChanged { text, _, _, _ -> - viewLifecycleOwner.lifecycleScope.launchWhenResumed { - text?.toString()?.let { - Session.loggerSearchText = it - logsAdapter.list = filteredLogs(it) - if (it.isEmpty()) { - binding.logList.linearLayoutManager()?.scrollToPositionWithOffset(0, 0) - } - } - } - } - binding.search.setText(Session.loggerSearchText) - viewModel.logs.removeObserver(logsObserver) - viewModel.logs.observe(viewLifecycleOwner, logsObserver) - - binding.close.setDebounceClickListener { - activity?.finish() - } - binding.options.setDebounceClickListener { - context?.showMoreOptions(it, R.menu.pluto_logger___menu_more_options) { item -> - when (item.itemId) { - R.id.clear -> LogsRepo.deleteAll() - } - } - } - } - - private fun filteredLogs(search: String): List { - var list = emptyList() - viewModel.logs.value?.let { - list = it.filter { log -> - log.tag.contains(search, true) || - log.message.contains(search, true) || - log.stackTraceElement.fileName.contains(search, true) - } - } - binding.noItemText.text = getString( - if (search.isNotEmpty()) { - R.string.pluto_logger___no_search_result - } else { - R.string.pluto_logger___no_logs_text - } - ) - binding.noItemText.visibility = if (list.isEmpty()) View.VISIBLE else View.GONE - return list - } - - private val logsObserver = Observer> { - logsAdapter.list = filteredLogs(binding.search.text.toString()) - } - - private val onActionListener = object : DiffAwareAdapter.OnActionListener { - override fun onAction(action: String, data: ListItem, holder: DiffAwareHolder?) { - if (data is LogData) { - activity?.let { - it.hideKeyboard(viewLifecycleOwner.lifecycleScope) { - viewModel.updateCurrentLog(data) - findNavController().navigate(R.id.openDetails) - } - } - } - } - } -} diff --git a/plugin-logger/src/main/java/com/pluto/logger/internal/ui/list/LogItemHolder.kt b/plugin-logger/src/main/java/com/pluto/logger/internal/ui/list/LogItemHolder.kt deleted file mode 100644 index 0a57bda5..00000000 --- a/plugin-logger/src/main/java/com/pluto/logger/internal/ui/list/LogItemHolder.kt +++ /dev/null @@ -1,57 +0,0 @@ -package com.pluto.logger.internal.ui.list - -import android.view.ViewGroup -import com.pluto.logger.R -import com.pluto.logger.databinding.PlutoLoggerListItemBinding -import com.pluto.logger.internal.LogData -import com.pluto.plugin.utilities.extensions.asTimeElapsed -import com.pluto.plugin.utilities.extensions.color -import com.pluto.plugin.utilities.extensions.dp -import com.pluto.plugin.utilities.extensions.inflate -import com.pluto.plugin.utilities.list.DiffAwareAdapter -import com.pluto.plugin.utilities.list.DiffAwareHolder -import com.pluto.plugin.utilities.list.ListItem -import com.pluto.plugin.utilities.setDebounceClickListener -import com.pluto.plugin.utilities.spannable.setSpan - -internal class LogItemHolder(parent: ViewGroup, actionListener: DiffAwareAdapter.OnActionListener) : - DiffAwareHolder(parent.inflate(R.layout.pluto_logger___list_item), actionListener) { - - private val binding = PlutoLoggerListItemBinding.bind(itemView) - private val logTag = binding.logtag - private val message = binding.message - private val timestamp = binding.timestamp - - override fun onBind(item: ListItem) { - if (item is LogData) { - logTag.setSpan { - append(fontColor(semiBold(item.tag.trim()), context.color(R.color.pluto___text_dark_40))) - append( - fontColor( - " | ${item.stackTraceElement.fileName}:${item.stackTraceElement.lineNumber}", - context.color(R.color.pluto___text_dark_40) - ) - ) - } - logTag.setCompoundDrawablesWithIntrinsicBounds(item.level.iconRes, 0, 0, 0) - logTag.compoundDrawablePadding = DRAWABLE_PADDING - message.setSpan { - append(semiBold(item.message.trim())) - item.eventAttributes?.let { - append( - regular( - fontColor(" (${it.size} attributes)", context.color(R.color.pluto___text_dark_60)) - ) - ) - } - } - timestamp.text = item.timeStamp.asTimeElapsed() - itemView.setBackgroundColor(itemView.context.color(item.level.color)) - itemView.setDebounceClickListener { onAction("click") } - } - } - - private companion object { - val DRAWABLE_PADDING = 4f.dp.toInt() - } -} diff --git a/plugin-logger/src/main/java/com/pluto/logger/internal/ui/list/LogsAdapter.kt b/plugin-logger/src/main/java/com/pluto/logger/internal/ui/list/LogsAdapter.kt deleted file mode 100644 index 946ea531..00000000 --- a/plugin-logger/src/main/java/com/pluto/logger/internal/ui/list/LogsAdapter.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.pluto.logger.internal.ui.list - -import android.view.ViewGroup -import com.pluto.logger.internal.LogData -import com.pluto.plugin.utilities.list.BaseAdapter -import com.pluto.plugin.utilities.list.DiffAwareHolder -import com.pluto.plugin.utilities.list.ListItem - -internal class LogsAdapter(private val listener: OnActionListener) : BaseAdapter() { - override fun getItemViewType(item: ListItem): Int? { - return when (item) { - is LogData -> ITEM_TYPE_LOG - else -> null - } - } - - override fun onViewHolderCreated(parent: ViewGroup, viewType: Int): DiffAwareHolder? { - return when (viewType) { - ITEM_TYPE_LOG -> LogItemHolder(parent, listener) - else -> null - } - } - - companion object { - const val ITEM_TYPE_LOG = 1000 - } -} diff --git a/plugin-logger/src/main/res/drawable/pluto_logger___bg_bottom_sheet.xml b/plugin-logger/src/main/res/drawable/pluto_logger___bg_bottom_sheet.xml deleted file mode 100644 index 48e20428..00000000 --- a/plugin-logger/src/main/res/drawable/pluto_logger___bg_bottom_sheet.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/plugin-logger/src/main/res/drawable/pluto_logger___bg_bottom_sheet_content.xml b/plugin-logger/src/main/res/drawable/pluto_logger___bg_bottom_sheet_content.xml deleted file mode 100644 index 387102e1..00000000 --- a/plugin-logger/src/main/res/drawable/pluto_logger___bg_bottom_sheet_content.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/plugin-logger/src/main/res/drawable/pluto_logger___ic_analytics.xml b/plugin-logger/src/main/res/drawable/pluto_logger___ic_analytics.xml deleted file mode 100644 index 5706a4d4..00000000 --- a/plugin-logger/src/main/res/drawable/pluto_logger___ic_analytics.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/plugin-logger/src/main/res/drawable/pluto_logger___ic_clear_all.xml b/plugin-logger/src/main/res/drawable/pluto_logger___ic_clear_all.xml deleted file mode 100644 index bb96fcb2..00000000 --- a/plugin-logger/src/main/res/drawable/pluto_logger___ic_clear_all.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - diff --git a/plugin-logger/src/main/res/drawable/pluto_logger___ic_label.xml b/plugin-logger/src/main/res/drawable/pluto_logger___ic_label.xml deleted file mode 100644 index 21a45e7e..00000000 --- a/plugin-logger/src/main/res/drawable/pluto_logger___ic_label.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - diff --git a/plugin-logger/src/main/res/drawable/pluto_logger___ic_logger_icon.xml b/plugin-logger/src/main/res/drawable/pluto_logger___ic_logger_icon.xml deleted file mode 100644 index de4e9724..00000000 --- a/plugin-logger/src/main/res/drawable/pluto_logger___ic_logger_icon.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/plugin-logger/src/main/res/layout/pluto_logger___fragment_details.xml b/plugin-logger/src/main/res/layout/pluto_logger___fragment_details.xml deleted file mode 100644 index 0a0ffcf0..00000000 --- a/plugin-logger/src/main/res/layout/pluto_logger___fragment_details.xml +++ /dev/null @@ -1,156 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/plugin-logger/src/main/res/layout/pluto_logger___fragment_list.xml b/plugin-logger/src/main/res/layout/pluto_logger___fragment_list.xml deleted file mode 100644 index f0943e90..00000000 --- a/plugin-logger/src/main/res/layout/pluto_logger___fragment_list.xml +++ /dev/null @@ -1,140 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/plugin-logger/src/main/res/layout/pluto_logger___fragment_logs.xml b/plugin-logger/src/main/res/layout/pluto_logger___fragment_logs.xml deleted file mode 100644 index bdd3af91..00000000 --- a/plugin-logger/src/main/res/layout/pluto_logger___fragment_logs.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/plugin-logger/src/main/res/layout/pluto_logger___list_item.xml b/plugin-logger/src/main/res/layout/pluto_logger___list_item.xml deleted file mode 100644 index d9d34656..00000000 --- a/plugin-logger/src/main/res/layout/pluto_logger___list_item.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/plugin-logger/src/main/res/menu/pluto_logger___menu_more_options.xml b/plugin-logger/src/main/res/menu/pluto_logger___menu_more_options.xml deleted file mode 100644 index 9ec03511..00000000 --- a/plugin-logger/src/main/res/menu/pluto_logger___menu_more_options.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - \ No newline at end of file diff --git a/plugin-logger/src/main/res/navigation/pluto_logger___navigation.xml b/plugin-logger/src/main/res/navigation/pluto_logger___navigation.xml deleted file mode 100644 index 8479f345..00000000 --- a/plugin-logger/src/main/res/navigation/pluto_logger___navigation.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/plugin-logger/src/main/res/values/strings.xml b/plugin-logger/src/main/res/values/strings.xml deleted file mode 100644 index b9d7c7fe..00000000 --- a/plugin-logger/src/main/res/values/strings.xml +++ /dev/null @@ -1,15 +0,0 @@ - - Pluto Logger - No Logs printed. - Search logs - open logs menu - Exit logs debugger - Logger - Show more options - No search result found. - Log Details - Share - Exception - Event Attributes - Clear All logs - \ No newline at end of file diff --git a/plugin-logger/src/main/res/values/styles.xml b/plugin-logger/src/main/res/values/styles.xml deleted file mode 100644 index ab6b4938..00000000 --- a/plugin-logger/src/main/res/values/styles.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/sample/build.gradle b/sample/build.gradle index 63b419b4..3e22e961 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -43,7 +43,7 @@ dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation project(path: ':pluto') // implementation project(path: ':plugin-network') - implementation project(path: ':plugin-logger') +// implementation project(path: ':plugin-logger') // implementation project(path: ':plugin-shared-preferences') implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" diff --git a/sample/src/main/java/com/sampleapp/MainActivity.kt b/sample/src/main/java/com/sampleapp/MainActivity.kt index 441fb536..20a8466d 100644 --- a/sample/src/main/java/com/sampleapp/MainActivity.kt +++ b/sample/src/main/java/com/sampleapp/MainActivity.kt @@ -8,10 +8,7 @@ import android.provider.Settings import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.ViewModelProvider import com.pluto.Pluto -import com.pluto.logger.PlutoLog -import com.pluto.logger.event import com.sampleapp.databinding.ActivityMainBinding -import timber.log.Timber class MainActivity : AppCompatActivity() { @@ -22,7 +19,6 @@ class MainActivity : AppCompatActivity() { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) - Timber.v("MainActivity onCreate") // PlutoLog.v(TAG_ACTION, "MainActivity onCreate") Test().javaTest() @@ -75,45 +71,36 @@ class MainActivity : AppCompatActivity() { private fun handleAPIManageCTAs() { binding.postCall.setOnClickListener { -// Timber.e(NullPointerException("Custom Exception"), "post_call_cta") -// PlutoLog.e(TAG_CLICK, "post_call_cta", NullPointerException("Custom Exception")) - Timber.tag("analytics").event("post_call_cta_1", getAttrMap()) - PlutoLog.event(TAG_CLICK, "post_call_cta", getAttrMap()) // networkViewModel.post() } binding.getCall.setOnClickListener { - PlutoLog.event(TAG_CLICK, "get_call_cta", getAttrMap()) // networkViewModel.get() } binding.xmlCall.setOnClickListener { - PlutoLog.event(TAG_CLICK, "xml_call_cta", getAttrMap()) networkViewModel.xml() } binding.formEncodedCall.setOnClickListener { - PlutoLog.event(TAG_CLICK, "form_url_encoded_call_cta", getAttrMap()) networkViewModel.form() } } - override fun onStart() { - super.onStart() - Timber.e(NullPointerException("Custom Exception"), "MainActivity onStart") -// PlutoLog.v(TAG_ACTION, "MainActivity onStart") - } - - override fun onResume() { - super.onResume() - Timber.tag(TAG_ACTION).d("MainActivity onResume") -// PlutoLog.v(TAG_ACTION, "MainActivity onResume") - } - - private fun getAttrMap(): HashMap = hashMapOf( - "screen" to "MainActivity", - "timestamp" to System.currentTimeMillis() - ) +// override fun onStart() { +// super.onStart() +// // PlutoLog.v(TAG_ACTION, "MainActivity onStart") +// } +// +// override fun onResume() { +// super.onResume() +// // PlutoLog.v(TAG_ACTION, "MainActivity onResume") +// } +// +// private fun getAttrMap(): HashMap = hashMapOf( +// "screen" to "MainActivity", +// "timestamp" to System.currentTimeMillis() +// ) companion object { const val TAG_ACTION = "action" diff --git a/sample/src/main/java/com/sampleapp/SampleApp.kt b/sample/src/main/java/com/sampleapp/SampleApp.kt index fdc33ba8..8d367bb0 100644 --- a/sample/src/main/java/com/sampleapp/SampleApp.kt +++ b/sample/src/main/java/com/sampleapp/SampleApp.kt @@ -2,20 +2,15 @@ package com.sampleapp import android.app.Application import com.pluto.Pluto -import com.pluto.logger.PlutoLoggerPlugin -import com.pluto.logger.PlutoTimberTree -import timber.log.Timber class SampleApp : Application() { override fun onCreate() { super.onCreate() Pluto.Installer(this) - .addPlugin(PlutoLoggerPlugin("logger")) +// .addPlugin(PlutoLoggerPlugin("logger")) // .addPlugin(PlutoSharePreferencesPlugin("sharedPref")) .install() Pluto.showNotch(true) - - Timber.plant(PlutoTimberTree()) } } diff --git a/settings.gradle b/settings.gradle index f747a6c7..4348fd07 100644 --- a/settings.gradle +++ b/settings.gradle @@ -2,4 +2,3 @@ rootProject.name='pluto-sample' include ':sample' include ':pluto' include ':pluto-plugin' -include ':plugin-logger' From e9ab308197244cf5bf42c83bcd292160d4725ed6 Mon Sep 17 00:00:00 2001 From: srtvprateek Date: Wed, 9 Feb 2022 23:59:06 +0530 Subject: [PATCH 003/223] no plugin state handled --- .../pluto___activity_plugin_selector.xml | 18 ++++++++++++++++++ pluto/src/main/res/values/strings.xml | 3 ++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/pluto/src/main/res/layout/pluto___activity_plugin_selector.xml b/pluto/src/main/res/layout/pluto___activity_plugin_selector.xml index 0c9f44bc..d4b0a047 100644 --- a/pluto/src/main/res/layout/pluto___activity_plugin_selector.xml +++ b/pluto/src/main/res/layout/pluto___activity_plugin_selector.xml @@ -48,6 +48,24 @@ app:layoutManager="androidx.recyclerview.widget.GridLayoutManager" app:layout_constraintBottom_toTopOf="@+id/bottomDivider" /> + + Never ask again Enable Now Open Pluto - Pluto + Pluto. + No Plugins installed yet. \ No newline at end of file From 52ee72a6a9317822911d36c674de99fb813493a7 Mon Sep 17 00:00:00 2001 From: srtvprateek Date: Thu, 10 Feb 2022 00:41:26 +0530 Subject: [PATCH 004/223] no plugin state handled --- .../main/java/com/pluto/plugin/PluginSelectorActivity.kt | 8 +++++++- .../main/res/layout/pluto___activity_plugin_selector.xml | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/pluto/src/main/java/com/pluto/plugin/PluginSelectorActivity.kt b/pluto/src/main/java/com/pluto/plugin/PluginSelectorActivity.kt index 49e82542..f0d20250 100644 --- a/pluto/src/main/java/com/pluto/plugin/PluginSelectorActivity.kt +++ b/pluto/src/main/java/com/pluto/plugin/PluginSelectorActivity.kt @@ -1,6 +1,8 @@ package com.pluto.plugin import android.os.Bundle +import android.view.View.GONE +import android.view.View.VISIBLE import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.Observer @@ -25,10 +27,11 @@ class PluginSelectorActivity : AppCompatActivity() { private val pluginsViewModel by viewModels() private val pluginAdapter: BaseAdapter by lazy { PluginAdapter(onActionListener) } private val settingsViewModel by viewModels() + private lateinit var binding: PlutoActivityPluginSelectorBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - val binding = PlutoActivityPluginSelectorBinding.inflate(layoutInflater) + binding = PlutoActivityPluginSelectorBinding.inflate(layoutInflater) setContentView(binding.root) overridePendingTransition(R.anim.pluto___slide_in_bottom, R.anim.pluto___slide_out_bottom) @@ -65,6 +68,9 @@ class PluginSelectorActivity : AppCompatActivity() { private val pluginListObserver = Observer> { pluginAdapter.list = it + if (it.isNullOrEmpty()) { + binding.noPluginView.visibility = if (it.isNullOrEmpty()) VISIBLE else GONE + } } private val appStateListener = Observer { diff --git a/pluto/src/main/res/layout/pluto___activity_plugin_selector.xml b/pluto/src/main/res/layout/pluto___activity_plugin_selector.xml index d4b0a047..f0fd77be 100644 --- a/pluto/src/main/res/layout/pluto___activity_plugin_selector.xml +++ b/pluto/src/main/res/layout/pluto___activity_plugin_selector.xml @@ -60,6 +60,8 @@ android:paddingHorizontal="@dimen/pluto___margin_xsmall" android:textColor="@color/pluto___white_40" android:textSize="@dimen/pluto___text_small" + android:visibility="gone" + tools:visibility="visible" app:layout_constraintBottom_toBottomOf="@+id/list" app:layout_constraintEnd_toEndOf="@+id/list" app:layout_constraintStart_toStartOf="@+id/list" From 5147ef2feaeb89c58e6bee93a1d43efe38b16e03 Mon Sep 17 00:00:00 2001 From: srtvprateek Date: Thu, 10 Feb 2022 01:44:19 +0530 Subject: [PATCH 005/223] code refactor --- .../com/pluto/plugin/utilities/sharing/ContentShare.kt | 9 +++------ .../pluto/plugin/utilities/sharing/ShareOptionsDialog.kt | 1 + 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/pluto-plugin/src/main/java/com/pluto/plugin/utilities/sharing/ContentShare.kt b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/sharing/ContentShare.kt index e19d1f4e..7f794fdb 100644 --- a/pluto-plugin/src/main/java/com/pluto/plugin/utilities/sharing/ContentShare.kt +++ b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/sharing/ContentShare.kt @@ -21,12 +21,9 @@ class ContentShare(activity: FragmentActivity) { } init { - sharer.data.observe( - activity, - { - dialog.show(it) - } - ) + sharer.data.observe(activity) { + dialog.show(it) + } } } diff --git a/pluto-plugin/src/main/java/com/pluto/plugin/utilities/sharing/ShareOptionsDialog.kt b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/sharing/ShareOptionsDialog.kt index 78684395..c4ee4ae3 100644 --- a/pluto-plugin/src/main/java/com/pluto/plugin/utilities/sharing/ShareOptionsDialog.kt +++ b/pluto-plugin/src/main/java/com/pluto/plugin/utilities/sharing/ShareOptionsDialog.kt @@ -13,6 +13,7 @@ import com.pluto.plugin.utilities.device.Device import com.pluto.plugin.utilities.extensions.inflate import com.pluto.plugin.utilities.setDebounceClickListener +// todo move to dialog fragment class ShareOptionsDialog( context: Context, private val onAction: (ShareAction) -> Unit From 85c86819fe0312fc9fa4b5f796a691cb7ad56932 Mon Sep 17 00:00:00 2001 From: srtvprateek Date: Thu, 10 Feb 2022 01:56:08 +0530 Subject: [PATCH 006/223] demo plugin setup --- build.gradle | 1 - demo-plugin/.gitignore | 1 + demo-plugin/build.gradle | 52 ++++++++++++ demo-plugin/src/main/AndroidManifest.xml | 5 ++ .../main/java/com/demo/plugin/DemoFragment.kt | 44 ++++++++++ .../main/java/com/demo/plugin/DemoPlugin.kt | 27 ++++++ .../src/main/java/com/demo/plugin/Session.kt | 5 ++ .../src/main/res/drawable/demo___ic_close.xml | 9 ++ .../main/res/drawable/demo___ic_menu_icon.xml | 9 ++ .../src/main/res/drawable/demo___ic_more.xml | 9 ++ .../res/drawable/demo___ic_plugin_icon.xml | 9 ++ .../main/res/layout/demo___fragment_demo.xml | 84 +++++++++++++++++++ .../res/menu/demo___menu_more_options.xml | 11 +++ demo-plugin/src/main/res/values/colors.xml | 4 + demo-plugin/src/main/res/values/strings.xml | 7 ++ sample/build.gradle | 3 +- .../src/main/java/com/sampleapp/SampleApp.kt | 2 + settings.gradle | 1 + 18 files changed, 280 insertions(+), 3 deletions(-) create mode 100644 demo-plugin/.gitignore create mode 100644 demo-plugin/build.gradle create mode 100644 demo-plugin/src/main/AndroidManifest.xml create mode 100644 demo-plugin/src/main/java/com/demo/plugin/DemoFragment.kt create mode 100644 demo-plugin/src/main/java/com/demo/plugin/DemoPlugin.kt create mode 100644 demo-plugin/src/main/java/com/demo/plugin/Session.kt create mode 100644 demo-plugin/src/main/res/drawable/demo___ic_close.xml create mode 100644 demo-plugin/src/main/res/drawable/demo___ic_menu_icon.xml create mode 100644 demo-plugin/src/main/res/drawable/demo___ic_more.xml create mode 100644 demo-plugin/src/main/res/drawable/demo___ic_plugin_icon.xml create mode 100644 demo-plugin/src/main/res/layout/demo___fragment_demo.xml create mode 100644 demo-plugin/src/main/res/menu/demo___menu_more_options.xml create mode 100644 demo-plugin/src/main/res/values/colors.xml create mode 100644 demo-plugin/src/main/res/values/strings.xml diff --git a/build.gradle b/build.gradle index 0bff117c..b5da5c0f 100644 --- a/build.gradle +++ b/build.gradle @@ -48,7 +48,6 @@ ext { buildToolsVersion = '32.0.0' androidXCoreVersion = '1.7.0' - moshiVersion = '1.13.0' } task installGitHook(type: Copy) { diff --git a/demo-plugin/.gitignore b/demo-plugin/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/demo-plugin/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/demo-plugin/build.gradle b/demo-plugin/build.gradle new file mode 100644 index 00000000..783d4ed1 --- /dev/null +++ b/demo-plugin/build.gradle @@ -0,0 +1,52 @@ +plugins { + id 'com.android.library' + id 'kotlin-android' +} + +apply from: "$rootDir/scripts/build/utils.gradle" + +def verCode, verName, verBuild, verNameShort, verPublish +(verCode, verName, verBuild, verNameShort, verPublish) = genVersion() + +android { + compileSdkVersion rootProject.compileSdkVersion + buildToolsVersion rootProject.buildToolsVersion + + buildFeatures { + viewBinding true + } + + lintOptions { + abortOnError false + } + + defaultConfig { + minSdkVersion rootProject.minSdkVersion + targetSdkVersion rootProject.targetSdkVersion + versionCode verCode + versionName verName + } + + buildTypes { + release { + debuggable true + minifyEnabled false + shrinkResources false + } + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = JavaVersion.VERSION_1_8.toString() + } + + resourcePrefix 'demo___' +} + +dependencies { + implementation project(path: ':pluto-plugin') +} \ No newline at end of file diff --git a/demo-plugin/src/main/AndroidManifest.xml b/demo-plugin/src/main/AndroidManifest.xml new file mode 100644 index 00000000..eded6a28 --- /dev/null +++ b/demo-plugin/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/demo-plugin/src/main/java/com/demo/plugin/DemoFragment.kt b/demo-plugin/src/main/java/com/demo/plugin/DemoFragment.kt new file mode 100644 index 00000000..be0f351c --- /dev/null +++ b/demo-plugin/src/main/java/com/demo/plugin/DemoFragment.kt @@ -0,0 +1,44 @@ +package com.demo.plugin + +import android.os.Bundle +import android.view.View +import androidx.fragment.app.Fragment +import com.demo.plugin.databinding.DemoFragmentDemoBinding +import com.pluto.plugin.utilities.extensions.showMoreOptions +import com.pluto.plugin.utilities.extensions.toast +import com.pluto.plugin.utilities.setDebounceClickListener +import com.pluto.plugin.utilities.sharing.Shareable +import com.pluto.plugin.utilities.sharing.lazyContentSharer +import com.pluto.plugin.utilities.viewBinding + +class DemoFragment : Fragment(R.layout.demo___fragment_demo) { + + private val binding by viewBinding(DemoFragmentDemoBinding::bind) + private val contentSharer by lazyContentSharer() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.close.setDebounceClickListener { + activity?.finish() + } + binding.options.setDebounceClickListener { + context?.showMoreOptions(it, R.menu.demo___menu_more_options) { item -> + when (item.itemId) { + R.id.action1 -> requireContext().toast("Demo Action 1 clicked") + R.id.action2 -> requireContext().toast("Demo Action 2 clicked") + } + } + } + binding.share.setDebounceClickListener { + contentSharer.share( + Shareable( + title = "Demo share", + content = "demo content", + fileName = "demo file" + ) + ) + } + binding.notification.setDebounceClickListener { + } + } +} diff --git a/demo-plugin/src/main/java/com/demo/plugin/DemoPlugin.kt b/demo-plugin/src/main/java/com/demo/plugin/DemoPlugin.kt new file mode 100644 index 00000000..681c2b7c --- /dev/null +++ b/demo-plugin/src/main/java/com/demo/plugin/DemoPlugin.kt @@ -0,0 +1,27 @@ +package com.demo.plugin + +import androidx.fragment.app.Fragment +import com.pluto.plugin.Plugin +import com.pluto.plugin.PluginConfiguration +import com.pluto.plugin.utilities.DebugLog + +class DemoPlugin(devIdentifier: String) : Plugin(devIdentifier) { + + init { + Session.devIdentifier = devIdentifier + } + + override fun getConfig(): PluginConfiguration = PluginConfiguration( + name = context.getString(R.string.demo___plugin_name), + icon = R.drawable.demo___ic_plugin_icon + ) + + override fun getView(): Fragment = DemoFragment() + + override fun onPluginInstalled() { + DebugLog.d("demo_plugin", "$devIdentifier installed") + } + + override fun onPluginDataCleared() { + } +} diff --git a/demo-plugin/src/main/java/com/demo/plugin/Session.kt b/demo-plugin/src/main/java/com/demo/plugin/Session.kt new file mode 100644 index 00000000..e4d844e8 --- /dev/null +++ b/demo-plugin/src/main/java/com/demo/plugin/Session.kt @@ -0,0 +1,5 @@ +package com.demo.plugin + +internal object Session { + var devIdentifier: String? = null +} diff --git a/demo-plugin/src/main/res/drawable/demo___ic_close.xml b/demo-plugin/src/main/res/drawable/demo___ic_close.xml new file mode 100644 index 00000000..66773188 --- /dev/null +++ b/demo-plugin/src/main/res/drawable/demo___ic_close.xml @@ -0,0 +1,9 @@ + + + diff --git a/demo-plugin/src/main/res/drawable/demo___ic_menu_icon.xml b/demo-plugin/src/main/res/drawable/demo___ic_menu_icon.xml new file mode 100644 index 00000000..dbfcc197 --- /dev/null +++ b/demo-plugin/src/main/res/drawable/demo___ic_menu_icon.xml @@ -0,0 +1,9 @@ + + + diff --git a/demo-plugin/src/main/res/drawable/demo___ic_more.xml b/demo-plugin/src/main/res/drawable/demo___ic_more.xml new file mode 100644 index 00000000..d9ee5961 --- /dev/null +++ b/demo-plugin/src/main/res/drawable/demo___ic_more.xml @@ -0,0 +1,9 @@ + + + diff --git a/demo-plugin/src/main/res/drawable/demo___ic_plugin_icon.xml b/demo-plugin/src/main/res/drawable/demo___ic_plugin_icon.xml new file mode 100644 index 00000000..68a958e2 --- /dev/null +++ b/demo-plugin/src/main/res/drawable/demo___ic_plugin_icon.xml @@ -0,0 +1,9 @@ + + + diff --git a/demo-plugin/src/main/res/layout/demo___fragment_demo.xml b/demo-plugin/src/main/res/layout/demo___fragment_demo.xml new file mode 100644 index 00000000..28cab3ba --- /dev/null +++ b/demo-plugin/src/main/res/layout/demo___fragment_demo.xml @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + +