├── .editorconfig ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ └── feature_request.yml ├── PULL_REQUEST_TEMPLATE └── workflows │ ├── code-analysis.yml │ └── gradle-wrapper-validation.yml ├── .gitignore ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── SECURITY.md ├── apiComparison.gradle ├── build.gradle ├── buildSrc ├── .gitignore ├── build.gradle └── src │ └── main │ └── groovy │ └── com │ └── infinum │ └── maven │ ├── SonatypeConfiguration.groovy │ └── shared │ └── Configuration.groovy ├── config.gradle ├── cpd.gradle ├── deploy.gradle ├── detekt.gradle ├── dokka.gradle ├── gradle.properties ├── gradle ├── libs.versions.toml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── ktlint.gradle ├── logo.svg ├── maven.gradle ├── renovate.json ├── sample ├── .gitignore ├── build.gradle ├── dummy.jks ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── assets │ ├── selfsigned_knobtviker.cert │ └── stackexchange.pem │ ├── kotlin │ └── com │ │ └── infinum │ │ └── sentinel │ │ └── sample │ │ ├── ANRTester.kt │ │ ├── BundleActivity.kt │ │ ├── BundleTree.kt │ │ ├── JavaMainActivity.java │ │ ├── MainActivity.kt │ │ └── SampleApplication.kt │ └── res │ ├── drawable-v24 │ └── ic_launcher_foreground.xml │ ├── drawable │ └── ic_launcher_background.xml │ ├── layout │ ├── activity_bundle.xml │ ├── activity_java_main.xml │ └── activity_main.xml │ ├── mipmap-anydpi-v26 │ ├── ic_launcher.xml │ └── ic_launcher_round.xml │ ├── mipmap-hdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-mdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ └── values │ ├── colors.xml │ ├── strings.xml │ └── styles.xml ├── sentinel-no-op ├── .gitignore ├── api │ └── sentinel-no-op.api ├── build.gradle ├── proguard-rules.txt ├── publish.gradle └── src │ └── main │ ├── AndroidManifest.xml │ ├── kotlin │ └── com │ │ └── infinum │ │ └── sentinel │ │ ├── Sentinel.kt │ │ ├── SentinelInitializer.kt │ │ ├── data │ │ └── models │ │ │ └── local │ │ │ └── crash │ │ │ └── ProcessThread.kt │ │ └── ui │ │ └── tools │ │ ├── AppGalleryTool.kt │ │ ├── CertificateTool.kt │ │ ├── ChuckerTool.kt │ │ ├── CollarTool.kt │ │ ├── DbInspectorTool.kt │ │ ├── GooglePlayTool.kt │ │ ├── LeakCanaryTool.kt │ │ ├── ThimbleTool.kt │ │ └── TimberTool.kt │ └── res │ └── values │ ├── metadata.xml │ └── public.xml ├── sentinel ├── .gitignore ├── api │ └── sentinel.api ├── build.gradle ├── jacoco.gradle ├── proguard-rules.txt ├── publish.gradle └── src │ ├── androidTest │ ├── AndroidManifest.xml │ ├── kotlin │ │ └── com │ │ │ └── infinum │ │ │ └── sentinel │ │ │ ├── SentinelTests.kt │ │ │ ├── data │ │ │ └── sources │ │ │ │ └── raw │ │ │ │ ├── collectors │ │ │ │ ├── ApplicationCollectorTests.kt │ │ │ │ ├── CollectorsTestSuite.kt │ │ │ │ ├── DeviceCollectorDeviceTests.kt │ │ │ │ ├── DeviceCollectorEmulatorTests.kt │ │ │ │ ├── DeviceCollectorTestSuite.kt │ │ │ │ ├── PermissionsCollectorTests.kt │ │ │ │ ├── PreferencesCollectorTests.kt │ │ │ │ └── ToolsCollectorTests.kt │ │ │ │ └── formatters │ │ │ │ ├── FormattersTestSuite.kt │ │ │ │ ├── HtmlStringBuilderTests.kt │ │ │ │ ├── JsonStringBuilderTests.kt │ │ │ │ ├── MarkdownStringBuilderTests.kt │ │ │ │ ├── PlainStringBuilderTests.kt │ │ │ │ └── XmlStringBuilderTests.kt │ │ │ └── ui │ │ │ ├── SentinelFragmentTests.kt │ │ │ ├── SentinelTestApplication.kt │ │ │ └── tools │ │ │ ├── DummyTool.kt │ │ │ └── NoNameTool.kt │ └── resources │ │ ├── expected_html.html │ │ ├── expected_html_no_preferences.html │ │ ├── expected_json.json │ │ ├── expected_json_no_preferences.json │ │ ├── expected_markdown.md │ │ ├── expected_markdown_no_preferences.md │ │ ├── expected_plain.txt │ │ ├── expected_plain_no_preferences.txt │ │ ├── expected_xml.xml │ │ └── expected_xml_no_preferences.xml │ └── main │ ├── AndroidManifest.xml │ ├── kotlin │ └── com │ │ └── infinum │ │ └── sentinel │ │ ├── Sentinel.kt │ │ ├── SentinelInitializer.kt │ │ ├── data │ │ ├── models │ │ │ ├── local │ │ │ │ ├── BundleMonitorEntity.kt │ │ │ │ ├── CertificateMonitorEntity.kt │ │ │ │ ├── CrashEntity.kt │ │ │ │ ├── CrashMonitorEntity.kt │ │ │ │ ├── FormatEntity.kt │ │ │ │ ├── TriggerEntity.kt │ │ │ │ └── crash │ │ │ │ │ ├── CrashData.kt │ │ │ │ │ ├── ExceptionData.kt │ │ │ │ │ ├── ProcessThread.kt │ │ │ │ │ ├── ThreadData.kt │ │ │ │ │ └── ThreadGroupData.kt │ │ │ ├── memory │ │ │ │ ├── bundles │ │ │ │ │ └── BundleTree.kt │ │ │ │ ├── formats │ │ │ │ │ └── FormatType.kt │ │ │ │ └── triggers │ │ │ │ │ ├── Trigger.kt │ │ │ │ │ ├── TriggerType.kt │ │ │ │ │ ├── airplanemode │ │ │ │ │ └── AirplaneModeOnTrigger.kt │ │ │ │ │ ├── foreground │ │ │ │ │ └── ForegroundTrigger.kt │ │ │ │ │ ├── manual │ │ │ │ │ └── ManualTrigger.kt │ │ │ │ │ ├── proximity │ │ │ │ │ └── ProximityTrigger.kt │ │ │ │ │ ├── shake │ │ │ │ │ └── ShakeTrigger.kt │ │ │ │ │ ├── shared │ │ │ │ │ ├── AbstractTrigger.kt │ │ │ │ │ ├── BroadcastReceiverTrigger.kt │ │ │ │ │ ├── SensorTrigger.kt │ │ │ │ │ ├── receiver │ │ │ │ │ │ ├── BroadcastReceiver.kt │ │ │ │ │ │ ├── Builder.kt │ │ │ │ │ │ └── Instructions.kt │ │ │ │ │ └── samples │ │ │ │ │ │ ├── Sample.kt │ │ │ │ │ │ ├── SamplePool.kt │ │ │ │ │ │ └── SampleQueue.kt │ │ │ │ │ └── usb │ │ │ │ │ └── UsbConnectedTrigger.kt │ │ │ └── raw │ │ │ │ ├── ApplicationData.kt │ │ │ │ ├── CertificateData.kt │ │ │ │ ├── DeviceData.kt │ │ │ │ ├── PreferenceType.kt │ │ │ │ ├── PreferencesData.kt │ │ │ │ └── certificates │ │ │ │ ├── CertificateType.kt │ │ │ │ ├── FingerprintData.kt │ │ │ │ ├── PublicKeyData.kt │ │ │ │ └── SignatureData.kt │ │ └── sources │ │ │ ├── local │ │ │ └── room │ │ │ │ ├── SentinelDatabase.kt │ │ │ │ ├── callbacks │ │ │ │ └── SentinelDefaultValuesCallback.kt │ │ │ │ ├── dao │ │ │ │ ├── BundleMonitorDao.kt │ │ │ │ ├── CertificateMonitorDao.kt │ │ │ │ ├── CrashMonitorDao.kt │ │ │ │ ├── CrashesDao.kt │ │ │ │ ├── FormatsDao.kt │ │ │ │ └── TriggersDao.kt │ │ │ │ └── typeconverters │ │ │ │ ├── ChronoUnitConverter.kt │ │ │ │ ├── CrashDataConverter.kt │ │ │ │ ├── FormatTypeConverter.kt │ │ │ │ └── TriggerTypeConverter.kt │ │ │ ├── memory │ │ │ ├── bundles │ │ │ │ ├── BundlesCache.kt │ │ │ │ └── InMemoryBundlesCache.kt │ │ │ ├── certificate │ │ │ │ ├── CertificateCache.kt │ │ │ │ └── InMemoryCertificateCache.kt │ │ │ ├── preference │ │ │ │ ├── InMemoryPreferenceCache.kt │ │ │ │ └── PreferenceCache.kt │ │ │ └── triggers │ │ │ │ ├── TriggersCache.kt │ │ │ │ └── TriggersCacheFactory.kt │ │ │ └── raw │ │ │ ├── collectors │ │ │ ├── ApplicationCollector.kt │ │ │ ├── CertificateCollector.kt │ │ │ ├── Collector.kt │ │ │ ├── DeviceCollector.kt │ │ │ ├── PermissionsCollector.kt │ │ │ ├── PreferencesCollector.kt │ │ │ └── ToolsCollector.kt │ │ │ └── formatters │ │ │ ├── Formatter.kt │ │ │ ├── HtmlFormatter.kt │ │ │ ├── JsonFormatter.kt │ │ │ ├── MarkdownFormatter.kt │ │ │ ├── PlainFormatter.kt │ │ │ ├── XmlFormatter.kt │ │ │ └── shared │ │ │ └── StringBuilderFormatter.kt │ │ ├── di │ │ ├── LibraryComponents.kt │ │ ├── WorkManagerInitializer.kt │ │ ├── component │ │ │ ├── DataComponent.kt │ │ │ ├── DomainComponent.kt │ │ │ ├── PresentationComponent.kt │ │ │ └── ViewModelComponent.kt │ │ └── scope │ │ │ ├── DataScope.kt │ │ │ ├── DomainScope.kt │ │ │ └── PresentationScope.kt │ │ ├── domain │ │ ├── Factories.kt │ │ ├── Repositories.kt │ │ ├── bundle │ │ │ ├── descriptor │ │ │ │ ├── BundlesRepository.kt │ │ │ │ └── models │ │ │ │ │ ├── BundleDescriptor.kt │ │ │ │ │ └── BundleParameters.kt │ │ │ ├── monitor │ │ │ │ ├── BundleMonitorRepository.kt │ │ │ │ └── models │ │ │ │ │ └── BundleMonitorParameters.kt │ │ │ └── shared │ │ │ │ └── BundlesParameters.kt │ │ ├── certificate │ │ │ ├── CertificateRepository.kt │ │ │ ├── models │ │ │ │ └── CertificateParameters.kt │ │ │ └── monitor │ │ │ │ ├── CertificateMonitorRepository.kt │ │ │ │ └── models │ │ │ │ └── CertificateMonitorParameters.kt │ │ ├── collectors │ │ │ ├── CollectorFactory.kt │ │ │ └── Collectors.kt │ │ ├── crash │ │ │ ├── models │ │ │ │ └── CrashParameters.kt │ │ │ └── monitor │ │ │ │ ├── CrashMonitorRepository.kt │ │ │ │ └── models │ │ │ │ └── CrashMonitorParameters.kt │ │ ├── formats │ │ │ ├── FormatsRepository.kt │ │ │ └── models │ │ │ │ └── FormatsParameters.kt │ │ ├── formatters │ │ │ ├── FormatterFactory.kt │ │ │ └── Formatters.kt │ │ ├── preference │ │ │ ├── PreferenceRepository.kt │ │ │ └── models │ │ │ │ └── PreferenceParameters.kt │ │ ├── shared │ │ │ └── base │ │ │ │ ├── BaseParameters.kt │ │ │ │ └── BaseRepository.kt │ │ └── triggers │ │ │ ├── TriggersRepository.kt │ │ │ └── models │ │ │ └── TriggerParameters.kt │ │ ├── extensions │ │ ├── Activity.kt │ │ ├── Array.kt │ │ ├── Bundle.kt │ │ ├── ByteArray.kt │ │ ├── ChronoUnit.kt │ │ ├── Context.kt │ │ ├── FormatType.kt │ │ ├── IntentBuilder.kt │ │ ├── Menu.kt │ │ ├── SearchView.kt │ │ ├── String.kt │ │ ├── Thread.kt │ │ ├── ThreadGroup.kt │ │ ├── Throwable.kt │ │ └── ViewModel.kt │ │ ├── ui │ │ ├── bundles │ │ │ ├── BundleViewHolder.kt │ │ │ ├── BundlesActivity.kt │ │ │ ├── BundlesAdapter.kt │ │ │ ├── BundlesDiffUtil.kt │ │ │ ├── BundlesEvent.kt │ │ │ ├── BundlesFragment.kt │ │ │ ├── BundlesViewModel.kt │ │ │ ├── callbacks │ │ │ │ ├── BundleCallSite.kt │ │ │ │ ├── BundleMonitorActivityCallbacks.kt │ │ │ │ ├── BundleMonitorFragmentCallbacks.kt │ │ │ │ └── BundleMonitorNotificationCallbacks.kt │ │ │ └── details │ │ │ │ ├── BundleDetailsActivity.kt │ │ │ │ ├── BundleDetailsAdapter.kt │ │ │ │ ├── BundleDetailsDiffUtil.kt │ │ │ │ ├── BundleDetailsFragment.kt │ │ │ │ ├── BundleDetailsState.kt │ │ │ │ ├── BundleDetailsViewHolder.kt │ │ │ │ └── BundleDetailsViewModel.kt │ │ ├── certificates │ │ │ ├── CertificateViewHolder.kt │ │ │ ├── CertificatesActivity.kt │ │ │ ├── CertificatesAdapter.kt │ │ │ ├── CertificatesDiffUtil.kt │ │ │ ├── CertificatesEvent.kt │ │ │ ├── CertificatesFragment.kt │ │ │ ├── CertificatesState.kt │ │ │ ├── CertificatesViewModel.kt │ │ │ ├── HeaderAdapter.kt │ │ │ ├── HeaderViewHolder.kt │ │ │ ├── details │ │ │ │ ├── CertificateDetailsActivity.kt │ │ │ │ ├── CertificateDetailsFragment.kt │ │ │ │ ├── CertificateDetailsState.kt │ │ │ │ └── CertificateDetailsViewModel.kt │ │ │ └── observer │ │ │ │ ├── CertificateCheckWorker.kt │ │ │ │ ├── CertificateCount.kt │ │ │ │ ├── CertificatesObserver.kt │ │ │ │ ├── DelegateWorker.kt │ │ │ │ ├── SentinelWorkManager.kt │ │ │ │ └── SentinelWorkerFactory.kt │ │ ├── crash │ │ │ ├── CrashViewHolder.kt │ │ │ ├── CrashesActivity.kt │ │ │ ├── CrashesAdapter.kt │ │ │ ├── CrashesDiffUtil.kt │ │ │ ├── CrashesEvent.kt │ │ │ ├── CrashesFragment.kt │ │ │ ├── CrashesViewModel.kt │ │ │ ├── anr │ │ │ │ ├── SentinelAnrObserver.kt │ │ │ │ ├── SentinelAnrObserverRunnable.kt │ │ │ │ └── SentinelUiAnrObserver.kt │ │ │ ├── details │ │ │ │ ├── CrashDetailsActivity.kt │ │ │ │ ├── CrashDetailsEvent.kt │ │ │ │ ├── CrashDetailsFragment.kt │ │ │ │ ├── CrashDetailsState.kt │ │ │ │ └── CrashDetailsViewModel.kt │ │ │ └── handler │ │ │ │ ├── SentinelExceptionHandler.kt │ │ │ │ └── SentinelUncaughtExceptionHandler.kt │ │ ├── main │ │ │ ├── SentinelActivity.kt │ │ │ ├── SentinelEvent.kt │ │ │ ├── SentinelFragment.kt │ │ │ ├── SentinelState.kt │ │ │ ├── SentinelViewModel.kt │ │ │ ├── application │ │ │ │ ├── ApplicationFragment.kt │ │ │ │ ├── ApplicationState.kt │ │ │ │ └── ApplicationViewModel.kt │ │ │ ├── device │ │ │ │ ├── DeviceFragment.kt │ │ │ │ ├── DeviceState.kt │ │ │ │ └── DeviceViewModel.kt │ │ │ ├── permissions │ │ │ │ ├── PermissionsFragment.kt │ │ │ │ ├── PermissionsState.kt │ │ │ │ └── PermissionsViewModel.kt │ │ │ ├── preferences │ │ │ │ ├── PreferencesEvent.kt │ │ │ │ ├── PreferencesFragment.kt │ │ │ │ ├── PreferencesState.kt │ │ │ │ ├── PreferencesViewModel.kt │ │ │ │ ├── all │ │ │ │ │ └── AllPreferencesActivity.kt │ │ │ │ ├── editor │ │ │ │ │ ├── PreferenceEditorActivity.kt │ │ │ │ │ ├── PreferenceEditorContract.kt │ │ │ │ │ ├── PreferenceEditorEvent.kt │ │ │ │ │ ├── PreferenceEditorFragment.kt │ │ │ │ │ ├── PreferenceEditorState.kt │ │ │ │ │ └── PreferenceEditorViewModel.kt │ │ │ │ └── shared │ │ │ │ │ ├── adapter │ │ │ │ │ ├── PreferenceChildViewHolder.kt │ │ │ │ │ ├── PreferenceParentViewHolder.kt │ │ │ │ │ └── PreferencesAdapter.kt │ │ │ │ │ └── model │ │ │ │ │ └── PreferencesItem.kt │ │ │ └── tools │ │ │ │ ├── ToolsFragment.kt │ │ │ │ ├── ToolsState.kt │ │ │ │ └── ToolsViewModel.kt │ │ ├── settings │ │ │ ├── SettingsActivity.kt │ │ │ ├── SettingsEvent.kt │ │ │ ├── SettingsFragment.kt │ │ │ └── SettingsViewModel.kt │ │ ├── shared │ │ │ ├── Constants.kt │ │ │ ├── base │ │ │ │ ├── BaseActivity.kt │ │ │ │ ├── BaseChildActivity.kt │ │ │ │ ├── BaseChildFragment.kt │ │ │ │ ├── BaseChildViewModel.kt │ │ │ │ ├── BaseFragment.kt │ │ │ │ ├── BaseView.kt │ │ │ │ └── BaseViewModel.kt │ │ │ ├── delegates │ │ │ │ └── ViewBindingDelegate.kt │ │ │ ├── edgefactories │ │ │ │ └── bounce │ │ │ │ │ ├── BounceEdgeEffect.kt │ │ │ │ │ └── BounceEdgeEffectFactory.kt │ │ │ ├── edgetreatment │ │ │ │ ├── CradleTopEdgeTreatment.kt │ │ │ │ └── ScissorsEdgeTreatment.kt │ │ │ ├── notification │ │ │ │ ├── IntentFactory.kt │ │ │ │ ├── NotificationFactory.kt │ │ │ │ ├── NotificationIntentFactory.kt │ │ │ │ └── SystemNotificationFactory.kt │ │ │ ├── search │ │ │ │ └── SimpleQueryTextListener.kt │ │ │ ├── span │ │ │ │ └── SpanDSL.kt │ │ │ └── views │ │ │ │ └── SentinelItemTextView.kt │ │ └── tools │ │ │ ├── AppInfoTool.kt │ │ │ ├── BundleMonitorTool.kt │ │ │ ├── CertificateTool.kt │ │ │ └── CrashMonitorTool.kt │ │ └── utils │ │ └── ChronoUnit.kt │ └── res │ ├── color │ ├── sentinel_selector_bottomnavigation_icon_tint.xml │ └── sentinel_selector_bottomnavigation_text_color.xml │ ├── drawable │ ├── sentinel_ic_activity_intent_extras.xml │ ├── sentinel_ic_activity_saved_state.xml │ ├── sentinel_ic_anr.xml │ ├── sentinel_ic_application.xml │ ├── sentinel_ic_back.xml │ ├── sentinel_ic_bundle_monitor_empty.xml │ ├── sentinel_ic_certificates_empty.xml │ ├── sentinel_ic_checked_filter.xml │ ├── sentinel_ic_clear.xml │ ├── sentinel_ic_clear_search.xml │ ├── sentinel_ic_close.xml │ ├── sentinel_ic_crash.xml │ ├── sentinel_ic_crashes_empty.xml │ ├── sentinel_ic_device.xml │ ├── sentinel_ic_fragment_arguments.xml │ ├── sentinel_ic_fragment_saved_state.xml │ ├── sentinel_ic_logger_empty.xml │ ├── sentinel_ic_minus.xml │ ├── sentinel_ic_notification_anr.xml │ ├── sentinel_ic_notification_certificate_invalid.xml │ ├── sentinel_ic_notification_crash.xml │ ├── sentinel_ic_permissions.xml │ ├── sentinel_ic_plus.xml │ ├── sentinel_ic_preferences.xml │ ├── sentinel_ic_save.xml │ ├── sentinel_ic_settings.xml │ ├── sentinel_ic_share.xml │ ├── sentinel_ic_sort.xml │ └── sentinel_ic_tools.xml │ ├── layout │ ├── sentinel_fragment.xml │ ├── sentinel_fragment_application.xml │ ├── sentinel_fragment_bundle_details.xml │ ├── sentinel_fragment_bundles.xml │ ├── sentinel_fragment_certificate_details.xml │ ├── sentinel_fragment_certificates.xml │ ├── sentinel_fragment_crash_details.xml │ ├── sentinel_fragment_crashes.xml │ ├── sentinel_fragment_device.xml │ ├── sentinel_fragment_permissions.xml │ ├── sentinel_fragment_preference_editor.xml │ ├── sentinel_fragment_preferences.xml │ ├── sentinel_fragment_settings.xml │ ├── sentinel_fragment_tools.xml │ ├── sentinel_item_bundle.xml │ ├── sentinel_item_bundle_key.xml │ ├── sentinel_item_certificate.xml │ ├── sentinel_item_crash.xml │ ├── sentinel_item_header.xml │ ├── sentinel_view_item_button.xml │ ├── sentinel_view_item_checkable.xml │ ├── sentinel_view_item_input.xml │ ├── sentinel_view_item_preference.xml │ ├── sentinel_view_item_text.xml │ └── sentinel_view_item_thread_state.xml │ ├── menu │ ├── sentinel_bottom_navigation.xml │ ├── sentinel_bundle_monitor_menu.xml │ ├── sentinel_crash_menu.xml │ ├── sentinel_crash_monitor_menu.xml │ ├── sentinel_menu.xml │ └── sentinel_preference_editor.xml │ ├── values-night │ └── colors.xml │ └── values │ ├── attrs.xml │ ├── colors.xml │ ├── dimens.xml │ ├── integers.xml │ ├── metadata.xml │ ├── public.xml │ ├── strings.xml │ └── themes.xml ├── settings.gradle ├── tasks.gradle ├── tool-appgallery ├── api │ └── tool-appgallery.api ├── build.gradle ├── proguard-rules.txt ├── publish.gradle └── src │ └── main │ ├── AndroidManifest.xml │ ├── kotlin │ └── com │ │ └── infinum │ │ └── sentinel │ │ └── ui │ │ └── tools │ │ └── AppGalleryTool.kt │ └── res │ └── values │ └── strings.xml ├── tool-chucker ├── api │ └── tool-chucker.api ├── build.gradle ├── proguard-rules.txt ├── publish.gradle └── src │ └── main │ ├── AndroidManifest.xml │ ├── kotlin │ └── com │ │ └── infinum │ │ └── sentinel │ │ └── ui │ │ └── tools │ │ └── ChuckerTool.kt │ └── res │ └── values │ └── strings.xml ├── tool-collar ├── api │ └── tool-collar.api ├── build.gradle ├── proguard-rules.txt ├── publish.gradle └── src │ └── main │ ├── AndroidManifest.xml │ ├── kotlin │ └── com │ │ └── infinum │ │ └── sentinel │ │ └── ui │ │ └── tools │ │ └── CollarTool.kt │ └── res │ └── values │ └── strings.xml ├── tool-dbinspector ├── api │ └── tool-dbinspector.api ├── build.gradle ├── proguard-rules.txt ├── publish.gradle └── src │ └── main │ ├── AndroidManifest.xml │ ├── kotlin │ └── com │ │ └── infinum │ │ └── sentinel │ │ └── ui │ │ └── tools │ │ └── DbInspectorTool.kt │ └── res │ └── values │ └── strings.xml ├── tool-googleplay ├── api │ └── tool-googleplay.api ├── build.gradle ├── proguard-rules.txt ├── publish.gradle └── src │ └── main │ ├── AndroidManifest.xml │ ├── kotlin │ └── com │ │ └── infinum │ │ └── sentinel │ │ └── ui │ │ └── tools │ │ └── GooglePlayTool.kt │ └── res │ └── values │ └── strings.xml ├── tool-leakcanary ├── api │ └── tool-leakcanary.api ├── build.gradle ├── proguard-rules.txt ├── publish.gradle └── src │ └── main │ ├── AndroidManifest.xml │ ├── kotlin │ └── com │ │ └── infinum │ │ └── sentinel │ │ └── ui │ │ └── tools │ │ └── LeakCanaryTool.kt │ └── res │ └── values │ └── strings.xml ├── tool-thimble ├── api │ └── tool-thimble.api ├── build.gradle ├── proguard-rules.txt ├── publish.gradle └── src │ └── main │ ├── AndroidManifest.xml │ ├── kotlin │ └── com │ │ └── infinum │ │ └── sentinel │ │ └── ui │ │ └── tools │ │ └── ThimbleTool.kt │ └── res │ └── values │ └── strings.xml ├── tool-timber ├── api │ └── tool-timber.api ├── build.gradle ├── proguard-rules.txt ├── publish.gradle └── src │ └── main │ ├── AndroidManifest.xml │ ├── kotlin │ └── com │ │ └── infinum │ │ └── sentinel │ │ ├── SentinelFileTree.kt │ │ ├── SentinelLogsProvider.kt │ │ ├── TimberInitializer.kt │ │ └── ui │ │ ├── logger │ │ ├── LoggerActivity.kt │ │ ├── LoggerAdapter.kt │ │ ├── LoggerDiffUtil.kt │ │ ├── LoggerViewHolder.kt │ │ ├── models │ │ │ ├── BaseEntry.kt │ │ │ ├── FlowBuffer.kt │ │ │ └── Level.kt │ │ └── storage │ │ │ └── AllowedTags.kt │ │ ├── logs │ │ ├── LogsActivity.kt │ │ ├── LogsAdapter.kt │ │ ├── LogsDiffUtil.kt │ │ └── LogsViewHolder.kt │ │ ├── shared │ │ ├── BounceEdgeEffect.kt │ │ ├── BounceEdgeEffectFactory.kt │ │ ├── LogFileResolver.kt │ │ ├── SearchViewKtx.kt │ │ ├── SimpleQueryTextListener.kt │ │ └── TimberToolConstants.kt │ │ └── tools │ │ └── TimberTool.kt │ └── res │ ├── drawable │ ├── sentinel_ic_clear_search.xml │ └── sentinel_ic_logs.xml │ ├── layout │ ├── sentinel_activity_logger.xml │ ├── sentinel_activity_logs.xml │ ├── sentinel_item_log.xml │ └── sentinel_item_log_file.xml │ ├── menu │ └── sentinel_logger_menu.xml │ ├── values │ ├── colors.xml │ └── strings.xml │ └── xml │ └── sentinel_file_paths.xml └── ui.png /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | [*.{kt,kts}] 3 | max_line_length = 140 4 | indent_style = space 5 | indent_size = 4 6 | ij_kotlin_imports_layout = * 7 | disabled_rules=import-ordering -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | name: Feature request 2 | description: Propose a new feature or an idea for this project 3 | labels: enhancement 4 | body: 5 | - type: textarea 6 | id: feature-description 7 | attributes: 8 | label: 💡 Feature description 9 | description: A clear and concise description of the feature request, including what problem it solves. 10 | validations: 11 | required: true 12 | - type: textarea 13 | id: additional-information 14 | attributes: 15 | label: ℹ️ Additional information 16 | description: An additional information or screenshots about the feature request. 17 | validations: 18 | required: false 19 | - type: textarea 20 | id: alternatives 21 | attributes: 22 | label: 🤔 Describe alternatives you've considered 23 | description: Can you think of alternative approaches to achieve same goal? 24 | validations: 25 | required: false -------------------------------------------------------------------------------- /.github/workflows/gradle-wrapper-validation.yml: -------------------------------------------------------------------------------- 1 | name: Validate Gradle wrapper 2 | on: 3 | push: 4 | branches: 5 | - master 6 | pull_request: 7 | branches: 8 | - '*' 9 | 10 | jobs: 11 | validation: 12 | name: Validation 13 | 14 | runs-on: macOS-latest 15 | 16 | steps: 17 | - name: Checkout latest code 18 | uses: actions/checkout@v2 19 | 20 | - name: Validate Gradle Wrapper 21 | uses: gradle/wrapper-validation-action@v1 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | /.idea/ 16 | /tool-chucker/build/ 17 | /tool-collar/build/ 18 | /tool-dbinspector/build/ 19 | /tool-googleplay/build/ 20 | /tool-thimble/build/ 21 | /tool-appgallery/build/ 22 | /tool-leakcanary/build/ 23 | /tool-timber/build/ 24 | 25 | publish.properties 26 | *.gpg -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security 2 | 3 | ## Reporting security issues 4 | 5 | At Infinum we are committed to ensuring the security of our software. If you have discovered a security vulnerability or have concerns regarding the security of our project, we encourage you to report it to us in a responsible manner. 6 | 7 | If you discover a security vulnerability, please report it to us by emailing us at opensource@infinum.com. We will review your report, and if the issue is confirmed, we will work to resolve the issue as soon as possible and coordinate the release of a security patch. 8 | 9 | ## Responsible disclosure 10 | 11 | We request that you practice responsible disclosure by allowing us time to investigate and address any reported vulnerabilities before making them public. We believe this approach helps protect our users and provides a better outcome for everyone involved. 12 | 13 | ## Preferred languages 14 | 15 | We prefer all communication to be in English. 16 | 17 | ## Contributions 18 | 19 | We greatly appreciate your help in keeping Infinum projects secure. Your efforts contribute to the ongoing improvement of our project's security. 20 | -------------------------------------------------------------------------------- /buildSrc/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /buildSrc/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "groovy" 3 | } -------------------------------------------------------------------------------- /buildSrc/src/main/groovy/com/infinum/maven/SonatypeConfiguration.groovy: -------------------------------------------------------------------------------- 1 | package com.infinum.maven 2 | 3 | import com.infinum.maven.shared.Configuration 4 | 5 | class SonatypeConfiguration implements Configuration { 6 | 7 | private Properties properties = new Properties() 8 | 9 | @Override 10 | void load() { 11 | File file = new File("publish.properties") 12 | if (file.exists()) { 13 | properties.load(new FileInputStream(file)) 14 | } else { 15 | properties.setProperty("sonatype.name", "") 16 | properties.setProperty("sonatype.url", "") 17 | properties.setProperty("sonatype.user", "") 18 | properties.setProperty("sonatype.password", "") 19 | } 20 | } 21 | 22 | @Override 23 | String name() { 24 | return properties.getProperty("sonatype.name").toString() 25 | } 26 | 27 | @Override 28 | String url() { 29 | return properties.getProperty("sonatype.url").toString() 30 | } 31 | 32 | @Override 33 | String username() { 34 | return properties.getProperty("sonatype.user").toString() 35 | } 36 | 37 | @Override 38 | String password() { 39 | return properties.getProperty("sonatype.password").toString() 40 | } 41 | } -------------------------------------------------------------------------------- /buildSrc/src/main/groovy/com/infinum/maven/shared/Configuration.groovy: -------------------------------------------------------------------------------- 1 | package com.infinum.maven.shared 2 | 3 | interface Configuration { 4 | 5 | void load() 6 | 7 | String name() 8 | 9 | String url() 10 | 11 | String username() 12 | 13 | String password() 14 | } -------------------------------------------------------------------------------- /config.gradle: -------------------------------------------------------------------------------- 1 | ext { 2 | def major = 1 3 | def minor = 5 4 | def patch = 1 5 | 6 | buildConfig = [ 7 | "minSdk" : 21, 8 | "compileSdk": 35, 9 | "targetSdk" : 35, 10 | "buildTools": "34.0.0" 11 | ] 12 | releaseConfig = [ 13 | "group" : "com.infinum.sentinel", 14 | "version" : "$major.$minor.$patch", 15 | "versionCode": major * 100 * 100 + minor * 100 + patch 16 | ] 17 | } -------------------------------------------------------------------------------- /cpd.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: "de.aaschmid.cpd" 2 | 3 | cpd { 4 | language = 'kotlin' 5 | } 6 | 7 | cpdCheck { 8 | reports { 9 | xml.required = true 10 | } 11 | source = allprojects*.file("src/main/kotlin") 12 | } 13 | -------------------------------------------------------------------------------- /detekt.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: "io.gitlab.arturbosch.detekt" 2 | 3 | detekt { 4 | toolVersion = libs.versions.detekt.get() 5 | } 6 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx1536m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app's APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Automatically convert third-party libraries to use AndroidX 19 | android.enableJetifier=false 20 | # Kotlin code style for this project: "official" or "obsolete": 21 | kotlin.code.style=official 22 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infinum/android-sentinel/ed10229ac3bcf4fedc4117d3469861e687626628/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /ktlint.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: "org.jlleitschuh.gradle.ktlint" 2 | 3 | ktlint { 4 | version = libs.versions.ktlint.get() 5 | debug = false 6 | android = true 7 | } -------------------------------------------------------------------------------- /maven.gradle: -------------------------------------------------------------------------------- 1 | import com.infinum.maven.SonatypeConfiguration 2 | 3 | SonatypeConfiguration.metaClass.constructor = { -> 4 | def constructor = SonatypeConfiguration.class.getConstructor() 5 | def instance = constructor.newInstance() 6 | instance.load() 7 | instance 8 | } 9 | 10 | ext { 11 | sonatype = new SonatypeConfiguration() 12 | } -------------------------------------------------------------------------------- /sample/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /release/ 3 | -------------------------------------------------------------------------------- /sample/dummy.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infinum/android-sentinel/ed10229ac3bcf4fedc4117d3469861e687626628/sample/dummy.jks -------------------------------------------------------------------------------- /sample/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /sample/src/main/assets/selfsigned_knobtviker.cert: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIC0jCCAbqgAwIBAgIJAIp9dDpc7eCDMA0GCSqGSIb3DQEBBQUAMBkxFzAVBgNV 3 | BAMTDmtub2J0dmlrZXIuY29tMB4XDTIyMDEwOTIyNDczMloXDTMyMDEwNzIyNDcz 4 | MlowGTEXMBUGA1UEAxMOa25vYnR2aWtlci5jb20wggEiMA0GCSqGSIb3DQEBAQUA 5 | A4IBDwAwggEKAoIBAQC2F89qzvG3pdh/pNPJra+Nf/9YRrsZDZuGysRFcgFwc9E/ 6 | fKcDZLWChF1C1GIoAJtOAZLiF91r8r4BGJVRahUIlRS149Rf3eH87im2siQH8NzK 7 | 1pjSRRXPAaUQueGAz1vwl+5BLFzcLExo3w8WI5AMHYZW9Z91Wmccxn4Ub/MBkKg1 8 | M3S4yxCuRf2Fe1p0E2DGL4NoI0bRS+WPPv8Hba/CbS0sdk42Hw3XZaMmJPumf7m9 9 | OG02/gy4ZiTa/YHDCoUhNItA7VUkTxDWHEd8oXOnwR3zR7WHzL1KJwOEkH12I3// 10 | 1oHM4/ss+wewC4hqgLSIiaaKX1MkNT38EetF3EcnAgMBAAGjHTAbMBkGA1UdEQQS 11 | MBCCDmtub2J0dmlrZXIuY29tMA0GCSqGSIb3DQEBBQUAA4IBAQAcsx5QDyQE1dqd 12 | BYvszW6h9Z9LtMydMkmfIWX+5WWcwGDPIRL4kC6lpyDT82FFrKcyggSOvjSIpvpB 13 | gjNcVZXlDu0ggj8RvJsD6mL1EGBFn549ICXS+edfyAmUGaQzSqCuG2r+swuumcXw 14 | UXGpS3tHOMpziWTTeCY/PLvL051Rm3dZbNLVCOji9jlyIxj+dca4TbOrP3uvTG9M 15 | OoYsawxyCNI+E2bu6xOVQWYwru/7/yTjVPK3DnFQqrOuv1OKMjeh8NaD8CQ0ArR+ 16 | OLeOpaZOUjwcTLQsfmIEB12Zn0tP6qsHVHWHZtMHK9I2e/9wZPF/gOzHvFPq9Q1O 17 | +APQx4rM 18 | -----END CERTIFICATE----- 19 | -------------------------------------------------------------------------------- /sample/src/main/kotlin/com/infinum/sentinel/sample/BundleActivity.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.sample 2 | 3 | import android.os.Bundle 4 | import androidx.activity.enableEdgeToEdge 5 | import androidx.appcompat.app.AppCompatActivity 6 | import com.infinum.sentinel.sample.databinding.ActivityBundleBinding 7 | 8 | class BundleActivity : AppCompatActivity() { 9 | 10 | private lateinit var viewBinding: ActivityBundleBinding 11 | 12 | override fun onCreate(savedInstanceState: Bundle?) { 13 | enableEdgeToEdge() 14 | super.onCreate(savedInstanceState) 15 | 16 | viewBinding = ActivityBundleBinding.inflate(layoutInflater) 17 | setContentView(viewBinding.root) 18 | 19 | intent.extras?.let { 20 | viewBinding.bundleContent.text = it.toString() 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /sample/src/main/kotlin/com/infinum/sentinel/sample/BundleTree.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.sample 2 | 3 | data class BundleTree( 4 | val id: String, 5 | val size: Int, 6 | val subTrees: List = emptyList() 7 | ) 8 | -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infinum/android-sentinel/ed10229ac3bcf4fedc4117d3469861e687626628/sample/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infinum/android-sentinel/ed10229ac3bcf4fedc4117d3469861e687626628/sample/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infinum/android-sentinel/ed10229ac3bcf4fedc4117d3469861e687626628/sample/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infinum/android-sentinel/ed10229ac3bcf4fedc4117d3469861e687626628/sample/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infinum/android-sentinel/ed10229ac3bcf4fedc4117d3469861e687626628/sample/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infinum/android-sentinel/ed10229ac3bcf4fedc4117d3469861e687626628/sample/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infinum/android-sentinel/ed10229ac3bcf4fedc4117d3469861e687626628/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infinum/android-sentinel/ed10229ac3bcf4fedc4117d3469861e687626628/sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infinum/android-sentinel/ed10229ac3bcf4fedc4117d3469861e687626628/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infinum/android-sentinel/ed10229ac3bcf4fedc4117d3469861e687626628/sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /sample/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3DDC84 4 | #538A6B 5 | #AC8CD4 6 | 7 | -------------------------------------------------------------------------------- /sample/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Sentinel Sample 3 | 4 | -------------------------------------------------------------------------------- /sample/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /sentinel-no-op/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /sentinel-no-op/proguard-rules.txt: -------------------------------------------------------------------------------- 1 | -keeppackagenames 2 | -keep public class com.infinum.sentinel.* { 3 | public protected *; 4 | } 5 | -keep public class com.infinum.sentinel.Sentinel { 6 | public protected *; 7 | } 8 | -keep public class com.infinum.sentinel.ui.tools.ChuckerTool { 9 | public protected *; 10 | } 11 | -keep public class com.infinum.sentinel.ui.tools.CollarTool { 12 | public protected *; 13 | } 14 | -keep public class com.infinum.sentinel.ui.tools.DbInspectorTool { 15 | public protected *; 16 | } 17 | -keep public class com.infinum.sentinel.ui.tools.LeakCanaryTool { 18 | public protected *; 19 | } 20 | -keep public class com.infinum.sentinel.ui.tools.AppGalleryTool { 21 | public protected *; 22 | } 23 | -keep public class com.infinum.sentinel.ui.tools.GooglePlayTool { 24 | public protected *; 25 | } 26 | -keep public class com.infinum.sentinel.ui.tools.ThimbleTool { 27 | public protected *; 28 | } 29 | -keep public class com.infinum.sentinel.ui.tools.TimberTool { 30 | public protected *; 31 | } 32 | -keep public class com.infinum.sentinel.ui.tools.CertificateTool { 33 | public protected *; 34 | } 35 | -keep public class com.infinum.sentinel.databinding.** -------------------------------------------------------------------------------- /sentinel-no-op/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 11 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /sentinel-no-op/src/main/kotlin/com/infinum/sentinel/SentinelInitializer.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel 2 | 3 | import android.content.Context 4 | import androidx.startup.Initializer 5 | 6 | public class SentinelInitializer : Initializer> { 7 | 8 | override fun create(context: Context): Class { 9 | // no-op implementation 10 | return SentinelInitializer::class.java 11 | } 12 | 13 | override fun dependencies(): List>> = 14 | listOf() 15 | } 16 | -------------------------------------------------------------------------------- /sentinel-no-op/src/main/kotlin/com/infinum/sentinel/data/models/local/crash/ProcessThread.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.models.local.crash 2 | 3 | internal data class ProcessThread( 4 | 5 | val name: String, 6 | 7 | val state: String, 8 | 9 | val stackTrace: List 10 | ) 11 | -------------------------------------------------------------------------------- /sentinel-no-op/src/main/kotlin/com/infinum/sentinel/ui/tools/AppGalleryTool.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.tools 2 | 3 | import android.view.View 4 | import com.infinum.sentinel.Sentinel 5 | 6 | /** 7 | * Specific wrapper tool around Huawei AppGallery in no - op. 8 | * 9 | */ 10 | @Suppress("UnusedPrivateMember") 11 | public data class AppGalleryTool @JvmOverloads constructor( 12 | private val appId: String = "", 13 | private val listener: View.OnClickListener = View.OnClickListener { 14 | // no - op 15 | } 16 | ) : Sentinel.Tool { 17 | 18 | override fun name(): Int = 0 19 | 20 | override fun listener(): View.OnClickListener = View.OnClickListener { 21 | // no - op 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /sentinel-no-op/src/main/kotlin/com/infinum/sentinel/ui/tools/CertificateTool.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.tools 2 | 3 | import android.view.View 4 | import com.infinum.sentinel.Sentinel 5 | import java.security.cert.X509Certificate 6 | 7 | /** 8 | * Specific wrapper tool that previews X.509 certificates from system and application. 9 | */ 10 | @Suppress("UnusedPrivateMember") 11 | public data class CertificateTool @JvmOverloads constructor( 12 | private val userCertificates: List = listOf(), 13 | private val listener: View.OnClickListener = View.OnClickListener { 14 | // no - op 15 | } 16 | ) : Sentinel.Tool { 17 | 18 | override fun name(): Int = 0 19 | 20 | override fun listener(): View.OnClickListener = View.OnClickListener { 21 | // no - op 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /sentinel-no-op/src/main/kotlin/com/infinum/sentinel/ui/tools/ChuckerTool.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.tools 2 | 3 | import android.view.View 4 | import com.infinum.sentinel.Sentinel 5 | 6 | /** 7 | * Specific wrapper tool around Chucker in no-op. 8 | * 9 | */ 10 | public class ChuckerTool : Sentinel.Tool { 11 | 12 | override fun name(): Int = 0 13 | 14 | override fun listener(): View.OnClickListener = View.OnClickListener { 15 | // no - op 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /sentinel-no-op/src/main/kotlin/com/infinum/sentinel/ui/tools/CollarTool.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.tools 2 | 3 | import android.view.View 4 | import com.infinum.sentinel.Sentinel 5 | 6 | /** 7 | * Specific wrapper tool around Collar in no - op. 8 | * 9 | */ 10 | public class CollarTool : Sentinel.Tool { 11 | 12 | override fun name(): Int = 0 13 | 14 | override fun listener(): View.OnClickListener = View.OnClickListener { 15 | // no - op 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /sentinel-no-op/src/main/kotlin/com/infinum/sentinel/ui/tools/DbInspectorTool.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.tools 2 | 3 | import android.view.View 4 | import com.infinum.sentinel.Sentinel 5 | 6 | /** 7 | * Specific wrapper tool around DbInspector in no - op. 8 | * 9 | */ 10 | public class DbInspectorTool : Sentinel.Tool { 11 | 12 | override fun name(): Int = 0 13 | 14 | override fun listener(): View.OnClickListener = View.OnClickListener { 15 | // no - op 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /sentinel-no-op/src/main/kotlin/com/infinum/sentinel/ui/tools/GooglePlayTool.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.tools 2 | 3 | import android.view.View 4 | import com.infinum.sentinel.Sentinel 5 | 6 | /** 7 | * Specific wrapper tool around Google Play in no - op. 8 | * 9 | */ 10 | public class GooglePlayTool : Sentinel.Tool { 11 | 12 | override fun name(): Int = 0 13 | 14 | override fun listener(): View.OnClickListener = View.OnClickListener { 15 | // no - op 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /sentinel-no-op/src/main/kotlin/com/infinum/sentinel/ui/tools/LeakCanaryTool.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.tools 2 | 3 | import android.view.View 4 | import com.infinum.sentinel.Sentinel 5 | 6 | /** 7 | * Specific wrapper tool around LeakCanary in no - op. 8 | * 9 | */ 10 | public class LeakCanaryTool : Sentinel.Tool { 11 | 12 | override fun name(): Int = 0 13 | 14 | override fun listener(): View.OnClickListener = View.OnClickListener { 15 | // no - op 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /sentinel-no-op/src/main/kotlin/com/infinum/sentinel/ui/tools/ThimbleTool.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.tools 2 | 3 | import android.view.View 4 | import com.infinum.sentinel.Sentinel 5 | 6 | /** 7 | * Specific wrapper tool for Thimble in no - op. 8 | * 9 | */ 10 | public class ThimbleTool : Sentinel.Tool { 11 | 12 | override fun name(): Int = 0 13 | 14 | override fun listener(): View.OnClickListener = View.OnClickListener { 15 | // no - op 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /sentinel-no-op/src/main/kotlin/com/infinum/sentinel/ui/tools/TimberTool.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.tools 2 | 3 | import android.view.View 4 | import com.infinum.sentinel.Sentinel 5 | 6 | /** 7 | * Specific wrapper tool around Timber. 8 | * 9 | * Tool Activity will launch with no additional flags. 10 | */ 11 | public class TimberTool : Sentinel.Tool { 12 | 13 | override fun name(): Int = 0 14 | 15 | override fun listener(): View.OnClickListener = View.OnClickListener { 16 | // no - op 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /sentinel-no-op/src/main/res/values/metadata.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | infinum.sentinel.monitored 4 | -------------------------------------------------------------------------------- /sentinel-no-op/src/main/res/values/public.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /sentinel/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /sentinel/proguard-rules.txt: -------------------------------------------------------------------------------- 1 | -keeppackagenames 2 | -keep public class com.infinum.sentinel.* { 3 | public protected *; 4 | } 5 | -keep public class com.infinum.sentinel.Sentinel { 6 | public protected *; 7 | } 8 | -keep public class com.infinum.sentinel.ui.tools.CertificateTool { 9 | public protected *; 10 | } 11 | -keep public class com.infinum.sentinel.databinding.** 12 | 13 | # KotlinX Serialization 14 | -if @kotlinx.serialization.Serializable class ** 15 | -keepclassmembers class <1> { 16 | static <1>$Companion Companion; 17 | } 18 | -if @kotlinx.serialization.Serializable class ** { 19 | static **$* *; 20 | } 21 | -keepclassmembers class <1>$<3> { 22 | kotlinx.serialization.KSerializer serializer(...); 23 | } 24 | -if @kotlinx.serialization.Serializable class ** { 25 | public static ** INSTANCE; 26 | } 27 | -keepclassmembers class <1> { 28 | public static <1> INSTANCE; 29 | kotlinx.serialization.KSerializer serializer(...); 30 | } 31 | -keepattributes RuntimeVisibleAnnotations,AnnotationDefault 32 | -keepattributes InnerClasses # Needed for `getDeclaredClasses`. 33 | -keepnames class <1>$$serializer { # -keepnames suffices; class is kept when serializer() is kept. 34 | static <1>$$serializer INSTANCE; 35 | } 36 | 37 | -keep class * extends androidx.room.RoomDatabase 38 | -keep @androidx.room.Entity class * 39 | -keep @androidx.room.TypeConverters class * -------------------------------------------------------------------------------- /sentinel/src/androidTest/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 11 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /sentinel/src/androidTest/kotlin/com/infinum/sentinel/data/sources/raw/collectors/CollectorsTestSuite.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.sources.raw.collectors 2 | 3 | import org.junit.runner.RunWith 4 | import org.junit.runners.Suite 5 | import org.junit.runners.Suite.SuiteClasses 6 | 7 | @RunWith(Suite::class) 8 | @SuiteClasses( 9 | ToolsCollectorTests::class, 10 | DeviceCollectorTestSuite::class, 11 | ApplicationCollectorTests::class, 12 | PermissionsCollectorTests::class, 13 | PreferencesCollectorTests::class 14 | ) 15 | public class CollectorsTestSuite 16 | -------------------------------------------------------------------------------- /sentinel/src/androidTest/kotlin/com/infinum/sentinel/data/sources/raw/collectors/DeviceCollectorTestSuite.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.sources.raw.collectors 2 | 3 | import org.junit.runner.RunWith 4 | import org.junit.runners.Suite 5 | import org.junit.runners.Suite.SuiteClasses 6 | 7 | @RunWith(Suite::class) 8 | @SuiteClasses(DeviceCollectorDeviceTests::class, DeviceCollectorEmulatorTests::class) 9 | public class DeviceCollectorTestSuite 10 | -------------------------------------------------------------------------------- /sentinel/src/androidTest/kotlin/com/infinum/sentinel/data/sources/raw/formatters/FormattersTestSuite.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.sources.raw.formatters 2 | 3 | import org.junit.runner.RunWith 4 | import org.junit.runners.Suite 5 | import org.junit.runners.Suite.SuiteClasses 6 | 7 | @RunWith(Suite::class) 8 | @SuiteClasses( 9 | PlainStringBuilderTests::class, 10 | MarkdownStringBuilderTests::class, 11 | JsonStringBuilderTests::class, 12 | XmlStringBuilderTests::class, 13 | HtmlStringBuilderTests::class 14 | ) 15 | public class FormattersTestSuite 16 | -------------------------------------------------------------------------------- /sentinel/src/androidTest/kotlin/com/infinum/sentinel/ui/SentinelTestApplication.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui 2 | 3 | import android.app.Application 4 | 5 | public class SentinelTestApplication : Application() 6 | -------------------------------------------------------------------------------- /sentinel/src/androidTest/kotlin/com/infinum/sentinel/ui/tools/DummyTool.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.tools 2 | 3 | import android.view.View 4 | import androidx.annotation.StringRes 5 | import com.infinum.sentinel.Sentinel 6 | 7 | internal data class DummyTool( 8 | @StringRes private val name: Int = 1 9 | ) : Sentinel.Tool { 10 | 11 | override fun name() = name 12 | 13 | override fun listener(): View.OnClickListener = View.OnClickListener { 14 | // no - op 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /sentinel/src/androidTest/kotlin/com/infinum/sentinel/ui/tools/NoNameTool.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.tools 2 | 3 | import android.view.View 4 | import androidx.annotation.StringRes 5 | import com.infinum.sentinel.Sentinel 6 | 7 | internal data class NoNameTool( 8 | @StringRes private val name: Int = 0 9 | ) : Sentinel.Tool { 10 | 11 | override fun name() = name 12 | 13 | override fun listener(): View.OnClickListener = View.OnClickListener { 14 | // no - op 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /sentinel/src/androidTest/resources/expected_markdown_no_preferences.md: -------------------------------------------------------------------------------- 1 | # application 2 | _version_code_: 100000 3 | _version_name_: 1.0.0 4 | _first_install_: yyyy-MM-dd HH:mm:ss 5 | _last_update_: yyyy-MM-dd HH:mm:ss 6 | _min_sdk_: 21 7 | _target_sdk_: 34 8 | _package_name_: com.infinum.sentinel.test 9 | _process_name_: com.infinum.sentinel.test 10 | _task_affinity_: com.infinum.sentinel.test 11 | _locale_language_: en 12 | _locale_country_: US 13 | _installer_: 14 | 15 | # permissions 16 | - _android.permission.POST_NOTIFICATIONS_: false 17 | - _android.permission.WAKE_LOCK_: true 18 | - _android.permission.ACCESS_NETWORK_STATE_: true 19 | - _android.permission.RECEIVE_BOOT_COMPLETED_: true 20 | - _android.permission.FOREGROUND_SERVICE_: true 21 | - _android.permission.REORDER_TASKS_: true 22 | - _com.infinum.sentinel.test.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION_: true 23 | 24 | # device 25 | _manufacturer_: Google 26 | _model_: Android SDK built for x86 27 | _id_: QSR1.190920.001 28 | _bootloader_: unknown 29 | _device_: generic_x86 30 | _board_: goldfish_x86 31 | _architectures_: x86 32 | _codename_: REL 33 | _release_: 10 34 | _sdk_: 29 35 | _security_patch_: 2019-09-05 36 | _emulator_: true 37 | _automatic_time_: true 38 | _automatic_timezone_: true 39 | _rooted_: false 40 | _screen_width_: 41 | _screen_height_: 42 | _screen_size_: 43 | _screen_density_: 44 | _font_scale_: 45 | 46 | # preferences 47 | 48 | -------------------------------------------------------------------------------- /sentinel/src/androidTest/resources/expected_plain_no_preferences.txt: -------------------------------------------------------------------------------- 1 | APPLICATION 2 | ----------- 3 | version_code: 100000 4 | version_name: 1.0.0 5 | first_install: yyyy-MM-dd HH:mm:ss 6 | last_update: yyyy-MM-dd HH:mm:ss 7 | min_sdk: 21 8 | target_sdk: 34 9 | package_name: com.infinum.sentinel.test 10 | process_name: com.infinum.sentinel.test 11 | task_affinity: com.infinum.sentinel.test 12 | locale_language: en 13 | locale_country: US 14 | installer: 15 | 16 | 17 | PERMISSIONS 18 | ----------- 19 | android.permission.POST_NOTIFICATIONS: false 20 | android.permission.WAKE_LOCK: true 21 | android.permission.ACCESS_NETWORK_STATE: true 22 | android.permission.RECEIVE_BOOT_COMPLETED: true 23 | android.permission.FOREGROUND_SERVICE: true 24 | android.permission.REORDER_TASKS: true 25 | com.infinum.sentinel.test.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION: true 26 | 27 | 28 | DEVICE 29 | ------ 30 | manufacturer: Google 31 | model: Android SDK built for x86 32 | id: QSR1.190920.001 33 | bootloader: unknown 34 | device: generic_x86 35 | board: goldfish_x86 36 | architectures: x86 37 | codename: REL 38 | release: 10 39 | sdk: 29 40 | security_patch: 2019-09-05 41 | emulator: true 42 | automatic_time: true 43 | automatic_timezone: true 44 | rooted: false 45 | screen_width: 46 | screen_height: 47 | screen_size: 48 | screen_density: 49 | font_scale: 50 | 51 | 52 | PREFERENCES 53 | ----------- 54 | 55 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/SentinelInitializer.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel 2 | 3 | import android.content.Context 4 | import androidx.startup.Initializer 5 | import com.infinum.sentinel.di.LibraryComponents 6 | 7 | public class SentinelInitializer : Initializer> { 8 | 9 | override fun create(context: Context): Class { 10 | 11 | LibraryComponents.initialize(context) 12 | 13 | return SentinelInitializer::class.java 14 | } 15 | 16 | override fun dependencies(): List>> = 17 | listOf() 18 | } 19 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/data/models/local/BundleMonitorEntity.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.models.local 2 | 3 | import androidx.room.ColumnInfo 4 | import androidx.room.Entity 5 | import androidx.room.PrimaryKey 6 | 7 | @Entity(tableName = "bundle_monitor") 8 | internal data class BundleMonitorEntity( 9 | 10 | @PrimaryKey(autoGenerate = false) 11 | @ColumnInfo(name = "id") 12 | var id: Long?, 13 | 14 | @ColumnInfo(name = "limit", defaultValue = "500") 15 | var limit: Int = 500, 16 | 17 | @ColumnInfo(name = "notify", defaultValue = "true") 18 | var notify: Boolean = true, 19 | 20 | @ColumnInfo(name = "activity_intent_extras", defaultValue = "true") 21 | var activityIntentExtras: Boolean = true, 22 | 23 | @ColumnInfo(name = "activity_saved_state", defaultValue = "true") 24 | var activitySavedState: Boolean = true, 25 | 26 | @ColumnInfo(name = "fragment_arguments", defaultValue = "true") 27 | var fragmentArguments: Boolean = true, 28 | 29 | @ColumnInfo(name = "fragment_saved_state", defaultValue = "true") 30 | var fragmentSavedState: Boolean = true 31 | ) 32 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/data/models/local/CertificateMonitorEntity.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.models.local 2 | 3 | import androidx.room.ColumnInfo 4 | import androidx.room.Entity 5 | import androidx.room.PrimaryKey 6 | import com.infinum.sentinel.utils.ChronoUnit 7 | 8 | @Entity(tableName = "certificate_monitor") 9 | internal data class CertificateMonitorEntity( 10 | 11 | @PrimaryKey(autoGenerate = false) 12 | @ColumnInfo(name = "id") 13 | var id: Long?, 14 | 15 | @ColumnInfo(name = "run_on_start", defaultValue = "false") 16 | var runOnStart: Boolean = false, 17 | 18 | @ColumnInfo(name = "run_in_background", defaultValue = "false") 19 | var runInBackground: Boolean = false, 20 | 21 | @ColumnInfo(name = "notify_invalid_now", defaultValue = "false") 22 | var notifyInvalidNow: Boolean = false, 23 | 24 | @ColumnInfo(name = "notify_to_expire", defaultValue = "false") 25 | var notifyToExpire: Boolean = false, 26 | 27 | @ColumnInfo(name = "expire_in_amount", defaultValue = "0") 28 | var expireInAmount: Int = 0, 29 | 30 | @ColumnInfo(name = "expire_in_unit", defaultValue = "DAYS") 31 | var expireInUnit: ChronoUnit = ChronoUnit.DAYS 32 | ) 33 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/data/models/local/CrashEntity.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.models.local 2 | 3 | import androidx.room.ColumnInfo 4 | import androidx.room.Entity 5 | import androidx.room.PrimaryKey 6 | import com.infinum.sentinel.data.models.local.crash.CrashData 7 | 8 | @Entity(tableName = "crashes") 9 | internal data class CrashEntity( 10 | 11 | @PrimaryKey(autoGenerate = true) 12 | @ColumnInfo(name = "id") 13 | var id: Long? = null, 14 | 15 | @ColumnInfo(name = "application_name") 16 | var applicationName: String, 17 | 18 | @ColumnInfo(name = "timestamp") 19 | var timestamp: Long, 20 | 21 | @ColumnInfo(name = "data") 22 | var data: CrashData 23 | ) 24 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/data/models/local/CrashMonitorEntity.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.models.local 2 | 3 | import androidx.room.ColumnInfo 4 | import androidx.room.Entity 5 | import androidx.room.PrimaryKey 6 | 7 | @Entity(tableName = "crash_monitor") 8 | internal data class CrashMonitorEntity( 9 | 10 | @PrimaryKey(autoGenerate = false) 11 | @ColumnInfo(name = "id") 12 | var id: Long?, 13 | 14 | @ColumnInfo(name = "notify_exceptions", defaultValue = "false") 15 | var notifyExceptions: Boolean = false, 16 | 17 | @ColumnInfo(name = "notify_anrs", defaultValue = "false") 18 | var notifyAnrs: Boolean = false, 19 | 20 | @ColumnInfo(name = "include_all_data", defaultValue = "false") 21 | var includeAllData: Boolean = false 22 | ) 23 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/data/models/local/FormatEntity.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.models.local 2 | 3 | import androidx.room.ColumnInfo 4 | import androidx.room.Entity 5 | import androidx.room.PrimaryKey 6 | import com.infinum.sentinel.data.models.memory.formats.FormatType 7 | 8 | @Entity(tableName = "formats") 9 | internal data class FormatEntity( 10 | 11 | @PrimaryKey(autoGenerate = false) 12 | @ColumnInfo(name = "id") 13 | var id: Long?, 14 | 15 | @ColumnInfo(name = "type") 16 | var type: FormatType?, 17 | 18 | @ColumnInfo(name = "selected", defaultValue = "false") 19 | var selected: Boolean = false 20 | ) 21 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/data/models/local/TriggerEntity.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.models.local 2 | 3 | import androidx.room.ColumnInfo 4 | import androidx.room.Entity 5 | import androidx.room.PrimaryKey 6 | import com.infinum.sentinel.data.models.memory.triggers.TriggerType 7 | 8 | @Entity(tableName = "triggers") 9 | internal data class TriggerEntity( 10 | 11 | @PrimaryKey(autoGenerate = false) 12 | @ColumnInfo(name = "id") 13 | var id: Long?, 14 | 15 | @ColumnInfo(name = "type") 16 | var type: TriggerType?, 17 | 18 | @ColumnInfo(name = "enabled", defaultValue = "true") 19 | var enabled: Boolean = true, 20 | 21 | @ColumnInfo(name = "editable", defaultValue = "true") 22 | var editable: Boolean = true 23 | ) 24 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/data/models/local/crash/CrashData.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.models.local.crash 2 | 3 | import kotlinx.serialization.SerialName 4 | import kotlinx.serialization.Serializable 5 | 6 | @Serializable 7 | internal data class CrashData( 8 | 9 | @SerialName("thread") 10 | val thread: ThreadData? = null, 11 | 12 | @SerialName("exception") 13 | val exception: ExceptionData? = null, 14 | 15 | @SerialName("thread_state") 16 | val threadState: List? = null 17 | ) 18 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/data/models/local/crash/ExceptionData.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.models.local.crash 2 | 3 | import kotlinx.serialization.SerialName 4 | import kotlinx.serialization.Serializable 5 | 6 | @Serializable 7 | internal data class ExceptionData( 8 | 9 | @SerialName("message") 10 | val message: String?, 11 | 12 | @SerialName("name") 13 | val name: String?, 14 | 15 | @SerialName("file") 16 | val file: String?, 17 | 18 | @SerialName("lineNumber") 19 | val lineNumber: Int, 20 | 21 | @SerialName("stack_trace") 22 | val stackTrace: List, 23 | 24 | @SerialName("is_ANR_exception") 25 | val isANRException: Boolean = false 26 | ) { 27 | 28 | fun asPrint(spacer: String = "\n\t\t\t"): String = 29 | "$name: $message ${stackTrace.joinToString { "$spacer at $it" }}" 30 | } 31 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/data/models/local/crash/ProcessThread.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.models.local.crash 2 | 3 | import kotlinx.serialization.SerialName 4 | import kotlinx.serialization.Serializable 5 | 6 | @Serializable 7 | internal data class ProcessThread( 8 | 9 | @SerialName("name") 10 | val name: String, 11 | 12 | @SerialName("state") 13 | val state: String, 14 | 15 | @SerialName("stack_trace") 16 | val stackTrace: List 17 | ) 18 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/data/models/local/crash/ThreadData.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.models.local.crash 2 | 3 | import kotlinx.serialization.SerialName 4 | import kotlinx.serialization.Serializable 5 | 6 | @Serializable 7 | internal data class ThreadData( 8 | 9 | @SerialName("id") 10 | val id: Long, 11 | 12 | @SerialName("name") 13 | val name: String, 14 | 15 | @SerialName("priority") 16 | val priority: Int, 17 | 18 | @SerialName("is_daemon") 19 | val isDaemon: Boolean, 20 | 21 | @SerialName("state") 22 | val state: String, 23 | 24 | @SerialName("group") 25 | val group: ThreadGroupData? 26 | ) 27 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/data/models/local/crash/ThreadGroupData.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.models.local.crash 2 | 3 | import kotlinx.serialization.SerialName 4 | import kotlinx.serialization.Serializable 5 | 6 | @Serializable 7 | internal data class ThreadGroupData( 8 | 9 | @SerialName("name") 10 | val name: String, 11 | 12 | @SerialName("parent") 13 | val parent: String, 14 | 15 | @SerialName("active_count") 16 | val activeCount: Int 17 | ) 18 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/data/models/memory/bundles/BundleTree.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.models.memory.bundles 2 | 3 | internal data class BundleTree( 4 | val id: String, 5 | val size: Int, 6 | val subTrees: List = emptyList() 7 | ) 8 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/data/models/memory/formats/FormatType.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.models.memory.formats 2 | 3 | internal enum class FormatType { 4 | PLAIN, 5 | MARKDOWN, 6 | JSON, 7 | XML, 8 | HTML 9 | } 10 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/data/models/memory/triggers/Trigger.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.models.memory.triggers 2 | 3 | internal interface Trigger { 4 | 5 | fun start() 6 | 7 | fun stop() 8 | } 9 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/data/models/memory/triggers/TriggerType.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.models.memory.triggers 2 | 3 | internal enum class TriggerType { 4 | MANUAL, 5 | SHAKE, 6 | PROXIMITY, 7 | FOREGROUND, 8 | USB_CONNECTED, 9 | AIRPLANE_MODE_ON 10 | } 11 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/data/models/memory/triggers/airplanemode/AirplaneModeOnTrigger.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.models.memory.triggers.airplanemode 2 | 3 | import android.content.Context 4 | import android.content.Intent 5 | import com.infinum.sentinel.data.models.memory.triggers.shared.BroadcastReceiverTrigger 6 | import com.infinum.sentinel.data.models.memory.triggers.shared.receiver.BroadcastReceiver 7 | import me.tatarka.inject.annotations.Inject 8 | 9 | @Inject 10 | internal class AirplaneModeOnTrigger( 11 | context: Context, 12 | private val trigger: () -> Unit 13 | ) : BroadcastReceiverTrigger(context) { 14 | 15 | companion object { 16 | private const val STATE = "state" 17 | } 18 | 19 | override val broadcastReceiver: BroadcastReceiver = BroadcastReceiver { 20 | onAction(Intent.ACTION_AIRPLANE_MODE_CHANGED) { 21 | if (active) { 22 | if (it.hasExtra(STATE)) { 23 | val isEnabled = it.getBooleanExtra(STATE, false) 24 | if (isEnabled) { 25 | trigger() 26 | } 27 | } 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/data/models/memory/triggers/foreground/ForegroundTrigger.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.models.memory.triggers.foreground 2 | 3 | import com.infinum.sentinel.data.models.memory.triggers.shared.AbstractTrigger 4 | import me.tatarka.inject.annotations.Inject 5 | 6 | @Inject 7 | internal class ForegroundTrigger( 8 | private val trigger: () -> Unit 9 | ) : AbstractTrigger() { 10 | 11 | override fun start() { 12 | if (active) { 13 | trigger() 14 | } 15 | } 16 | 17 | override fun stop() = Unit 18 | } 19 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/data/models/memory/triggers/manual/ManualTrigger.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.models.memory.triggers.manual 2 | 3 | import com.infinum.sentinel.data.models.memory.triggers.shared.AbstractTrigger 4 | import me.tatarka.inject.annotations.Inject 5 | 6 | @Inject 7 | internal class ManualTrigger : AbstractTrigger() { 8 | 9 | init { 10 | active = true 11 | } 12 | 13 | override fun start() = Unit 14 | 15 | override fun stop() = Unit 16 | } 17 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/data/models/memory/triggers/proximity/ProximityTrigger.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.models.memory.triggers.proximity 2 | 3 | import android.content.Context 4 | import android.hardware.Sensor 5 | import android.hardware.SensorEvent 6 | import com.infinum.sentinel.data.models.memory.triggers.shared.SensorTrigger 7 | import me.tatarka.inject.annotations.Inject 8 | 9 | @Inject 10 | internal class ProximityTrigger( 11 | context: Context, 12 | trigger: () -> Unit 13 | ) : SensorTrigger(context, trigger, Sensor.TYPE_PROXIMITY, null) { 14 | 15 | companion object { 16 | private const val SENSITIVITY_THRESHOLD = 4 17 | } 18 | 19 | override fun threshold(): Int = SENSITIVITY_THRESHOLD 20 | 21 | override fun processEvent(event: SensorEvent): Boolean { 22 | val value = event.values[0] 23 | return value >= -threshold() && value <= threshold() 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/data/models/memory/triggers/shake/ShakeTrigger.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.models.memory.triggers.shake 2 | 3 | import android.content.Context 4 | import android.hardware.Sensor 5 | import android.hardware.SensorEvent 6 | import com.infinum.sentinel.data.models.memory.triggers.shared.SensorTrigger 7 | import com.infinum.sentinel.data.models.memory.triggers.shared.samples.SampleQueue 8 | import me.tatarka.inject.annotations.Inject 9 | 10 | @Inject 11 | internal class ShakeTrigger( 12 | context: Context, 13 | trigger: () -> Unit 14 | ) : SensorTrigger(context, trigger, Sensor.TYPE_ACCELEROMETER, SampleQueue()) { 15 | 16 | companion object { 17 | private const val MAGNITUDE_THRESHOLD = 169 18 | } 19 | 20 | override fun threshold(): Int = MAGNITUDE_THRESHOLD 21 | 22 | override fun processEvent(event: SensorEvent): Boolean { 23 | val ax = event.values[0] 24 | val ay = event.values[1] 25 | val az = event.values[2] 26 | val magnitudeSquared = ax * ax + ay * ay + (az * az).toDouble() 27 | return magnitudeSquared > threshold() 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/data/models/memory/triggers/shared/AbstractTrigger.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.models.memory.triggers.shared 2 | 3 | import androidx.lifecycle.Lifecycle 4 | import androidx.lifecycle.LifecycleEventObserver 5 | import androidx.lifecycle.LifecycleOwner 6 | import androidx.lifecycle.ProcessLifecycleOwner 7 | import com.infinum.sentinel.data.models.memory.triggers.Trigger 8 | import kotlinx.coroutines.Dispatchers 9 | import kotlinx.coroutines.MainScope 10 | import kotlinx.coroutines.launch 11 | 12 | internal abstract class AbstractTrigger : LifecycleEventObserver, Trigger { 13 | 14 | internal var active: Boolean = false 15 | 16 | init { 17 | MainScope().launch(Dispatchers.Main) { 18 | ProcessLifecycleOwner.get().lifecycle.addObserver(this@AbstractTrigger) 19 | } 20 | } 21 | 22 | override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) { 23 | when (event) { 24 | Lifecycle.Event.ON_START -> start() 25 | Lifecycle.Event.ON_STOP -> stop() 26 | else -> Unit 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/data/models/memory/triggers/shared/BroadcastReceiverTrigger.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.models.memory.triggers.shared 2 | 3 | import android.content.Context 4 | import com.infinum.sentinel.data.models.memory.triggers.shared.receiver.BroadcastReceiver 5 | 6 | internal abstract class BroadcastReceiverTrigger( 7 | private val context: Context 8 | ) : AbstractTrigger() { 9 | 10 | abstract val broadcastReceiver: BroadcastReceiver 11 | 12 | override fun start() { 13 | context.registerReceiver( 14 | broadcastReceiver.receiver, 15 | broadcastReceiver.filter 16 | ) 17 | this.active = false 18 | } 19 | 20 | override fun stop() { 21 | context.unregisterReceiver(broadcastReceiver.receiver) 22 | this.active = false 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/data/models/memory/triggers/shared/receiver/BroadcastReceiver.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.models.memory.triggers.shared.receiver 2 | 3 | import android.content.BroadcastReceiver as AndroidBroadcastReceiver 4 | import android.content.Context 5 | import android.content.Intent 6 | import android.content.IntentFilter 7 | 8 | internal class BroadcastReceiver( 9 | builder: Builder.() -> Unit 10 | ) { 11 | private val instructions: List 12 | 13 | val filter: IntentFilter 14 | 15 | init { 16 | with(Builder()) { 17 | builder(this) 18 | filter = filter() 19 | instructions = instructions() 20 | } 21 | } 22 | 23 | val receiver = object : AndroidBroadcastReceiver() { 24 | override fun onReceive( 25 | context: Context, 26 | intent: Intent 27 | ) { 28 | for (instruction in instructions) { 29 | if (instruction.matches(intent)) { 30 | instruction.execution().invoke(intent) 31 | break 32 | } 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/data/models/memory/triggers/shared/samples/Sample.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.models.memory.triggers.shared.samples 2 | 3 | /** An accelerometer sample. */ 4 | internal data class Sample( 5 | /** Time sample was taken. */ 6 | var timestamp: Long = 0, 7 | 8 | /** If event values > threshold(). */ 9 | var triggered: Boolean = false, 10 | 11 | /** Next sample in the queue or pool. */ 12 | var next: Sample? = null 13 | ) 14 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/data/models/memory/triggers/shared/samples/SamplePool.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.models.memory.triggers.shared.samples 2 | 3 | /** Pools samples. Avoids garbage collection. */ 4 | internal class SamplePool { 5 | 6 | private var head: Sample? = null 7 | 8 | /** Acquires a sample from the pool. */ 9 | fun acquire(): Sample { 10 | var acquired = head 11 | if (acquired == null) { 12 | acquired = Sample() 13 | } else { // Remove instance from pool. 14 | head = acquired.next 15 | } 16 | return acquired 17 | } 18 | 19 | /** Returns a sample to the pool. */ 20 | fun release(sample: Sample) { 21 | sample.next = head 22 | head = sample 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/data/models/memory/triggers/usb/UsbConnectedTrigger.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.models.memory.triggers.usb 2 | 3 | import android.content.Context 4 | import com.infinum.sentinel.data.models.memory.triggers.shared.BroadcastReceiverTrigger 5 | import com.infinum.sentinel.data.models.memory.triggers.shared.receiver.BroadcastReceiver 6 | import me.tatarka.inject.annotations.Inject 7 | 8 | @Inject 9 | internal class UsbConnectedTrigger( 10 | private val context: Context, 11 | private val trigger: () -> Unit 12 | ) : BroadcastReceiverTrigger(context) { 13 | 14 | companion object { 15 | private const val USB_STATE = "android.hardware.usb.action.USB_STATE" 16 | private const val USB_CONNECTED = "connected" 17 | } 18 | 19 | override val broadcastReceiver = BroadcastReceiver { 20 | onAction(USB_STATE) { 21 | if (active) { 22 | if (it.hasExtra(USB_CONNECTED)) { 23 | val isConnected = it.getBooleanExtra(USB_CONNECTED, false) 24 | if (isConnected) { 25 | trigger() 26 | } 27 | } 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/data/models/raw/ApplicationData.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.models.raw 2 | 3 | import android.graphics.drawable.Drawable 4 | 5 | internal data class ApplicationData( 6 | val applicationIcon: Drawable, 7 | val applicationName: String, 8 | val versionCode: String, 9 | val versionName: String?, 10 | val firstInstall: String, 11 | val lastUpdate: String, 12 | val minSdk: String, 13 | val targetSdk: String, 14 | val packageName: String, 15 | val processName: String, 16 | val taskAffinity: String, 17 | val localeCountry: String, 18 | val localeLanguage: String, 19 | val installerPackageId: String 20 | ) 21 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/data/models/raw/PreferenceType.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.models.raw 2 | 3 | internal enum class PreferenceType { 4 | UNKNOWN, 5 | BOOLEAN, 6 | FLOAT, 7 | INT, 8 | LONG, 9 | STRING, 10 | SET 11 | } 12 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/data/models/raw/PreferencesData.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.models.raw 2 | 3 | internal data class PreferencesData( 4 | val name: String, 5 | val values: List>, 6 | val isSortedAscending: Boolean = false, 7 | val isExpanded: Boolean = true, 8 | ) 9 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/data/models/raw/certificates/CertificateType.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.models.raw.certificates 2 | 3 | internal enum class CertificateType { 4 | USER, 5 | SYSTEM 6 | } 7 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/data/models/raw/certificates/FingerprintData.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.models.raw.certificates 2 | 3 | internal data class FingerprintData( 4 | val md5: String?, 5 | val sha1: String?, 6 | val sha256: String? 7 | ) 8 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/data/models/raw/certificates/PublicKeyData.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.models.raw.certificates 2 | 3 | internal data class PublicKeyData( 4 | val algorithm: String, 5 | val size: Int 6 | ) 7 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/data/models/raw/certificates/SignatureData.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.models.raw.certificates 2 | 3 | internal data class SignatureData( 4 | val algorithmName: String, 5 | val algorithmOID: String 6 | ) 7 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/data/sources/local/room/dao/BundleMonitorDao.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.sources.local.room.dao 2 | 3 | import androidx.room.Dao 4 | import androidx.room.Insert 5 | import androidx.room.OnConflictStrategy 6 | import androidx.room.Query 7 | import com.infinum.sentinel.data.models.local.BundleMonitorEntity 8 | import kotlinx.coroutines.flow.Flow 9 | 10 | @Dao 11 | internal interface BundleMonitorDao { 12 | 13 | @Insert(onConflict = OnConflictStrategy.REPLACE) 14 | suspend fun save(entity: BundleMonitorEntity) 15 | 16 | @Query("SELECT * FROM bundle_monitor LIMIT 1") 17 | fun load(): Flow 18 | } 19 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/data/sources/local/room/dao/CertificateMonitorDao.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.sources.local.room.dao 2 | 3 | import androidx.room.Dao 4 | import androidx.room.Insert 5 | import androidx.room.OnConflictStrategy 6 | import androidx.room.Query 7 | import com.infinum.sentinel.data.models.local.CertificateMonitorEntity 8 | import kotlinx.coroutines.flow.Flow 9 | 10 | @Dao 11 | internal interface CertificateMonitorDao { 12 | 13 | @Insert(onConflict = OnConflictStrategy.REPLACE) 14 | suspend fun save(entity: CertificateMonitorEntity) 15 | 16 | @Query("SELECT * FROM certificate_monitor LIMIT 1") 17 | fun load(): Flow 18 | } 19 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/data/sources/local/room/dao/CrashMonitorDao.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.sources.local.room.dao 2 | 3 | import androidx.room.Dao 4 | import androidx.room.Insert 5 | import androidx.room.OnConflictStrategy 6 | import androidx.room.Query 7 | import com.infinum.sentinel.data.models.local.CrashMonitorEntity 8 | import kotlinx.coroutines.flow.Flow 9 | 10 | @Dao 11 | internal interface CrashMonitorDao { 12 | 13 | @Insert(onConflict = OnConflictStrategy.REPLACE) 14 | suspend fun save(entity: CrashMonitorEntity) 15 | 16 | @Query("SELECT * FROM crash_monitor LIMIT 1") 17 | fun load(): Flow 18 | } 19 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/data/sources/local/room/dao/CrashesDao.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.sources.local.room.dao 2 | 3 | import androidx.room.Dao 4 | import androidx.room.Insert 5 | import androidx.room.OnConflictStrategy 6 | import androidx.room.Query 7 | import com.infinum.sentinel.data.models.local.CrashEntity 8 | import kotlinx.coroutines.flow.Flow 9 | 10 | @Dao 11 | internal interface CrashesDao { 12 | 13 | @Insert(onConflict = OnConflictStrategy.REPLACE) 14 | suspend fun save(entity: CrashEntity): Long 15 | 16 | @Query("SELECT * FROM crashes where id is :id") 17 | suspend fun loadById(id: Long): CrashEntity 18 | 19 | @Query("SELECT * FROM crashes order by timestamp DESC") 20 | fun loadAll(): Flow> 21 | 22 | @Query("DELETE FROM crashes where id is :id") 23 | suspend fun deleteById(id: Long) 24 | 25 | @Query("DELETE FROM crashes") 26 | suspend fun deleteAll() 27 | } 28 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/data/sources/local/room/dao/FormatsDao.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.sources.local.room.dao 2 | 3 | import androidx.room.Dao 4 | import androidx.room.Insert 5 | import androidx.room.OnConflictStrategy 6 | import androidx.room.Query 7 | import com.infinum.sentinel.data.models.local.FormatEntity 8 | import kotlinx.coroutines.flow.Flow 9 | 10 | @Dao 11 | internal interface FormatsDao { 12 | 13 | @Insert(onConflict = OnConflictStrategy.REPLACE) 14 | suspend fun save(entities: List) 15 | 16 | @Query("SELECT * FROM formats WHERE selected = 1") 17 | fun load(): Flow 18 | } 19 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/data/sources/local/room/dao/TriggersDao.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.sources.local.room.dao 2 | 3 | import androidx.room.Dao 4 | import androidx.room.Insert 5 | import androidx.room.OnConflictStrategy 6 | import androidx.room.Query 7 | import com.infinum.sentinel.data.models.local.TriggerEntity 8 | import kotlinx.coroutines.flow.Flow 9 | 10 | @Dao 11 | internal interface TriggersDao { 12 | 13 | @Insert(onConflict = OnConflictStrategy.REPLACE) 14 | suspend fun save(entity: TriggerEntity) 15 | 16 | @Query("SELECT * FROM triggers") 17 | fun load(): Flow> 18 | } 19 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/data/sources/local/room/typeconverters/ChronoUnitConverter.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.sources.local.room.typeconverters 2 | 3 | import androidx.room.TypeConverter 4 | import java.time.temporal.ChronoUnit 5 | 6 | internal class ChronoUnitConverter { 7 | 8 | @TypeConverter 9 | fun toEnum(name: String): ChronoUnit = ChronoUnit.values().single { it.name == name } 10 | 11 | @TypeConverter 12 | fun fromEnum(unit: ChronoUnit): String = unit.name 13 | } 14 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/data/sources/local/room/typeconverters/CrashDataConverter.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.sources.local.room.typeconverters 2 | 3 | import androidx.room.TypeConverter 4 | import com.infinum.sentinel.data.models.local.crash.CrashData 5 | import kotlinx.serialization.decodeFromString 6 | import kotlinx.serialization.encodeToString 7 | import kotlinx.serialization.json.Json 8 | 9 | internal class CrashDataConverter { 10 | 11 | @TypeConverter 12 | fun toEntity(data: String): CrashData = 13 | Json.decodeFromString(data) 14 | 15 | @TypeConverter 16 | fun fromEntity(data: CrashData): String = 17 | Json.encodeToString(data) 18 | } 19 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/data/sources/local/room/typeconverters/FormatTypeConverter.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.sources.local.room.typeconverters 2 | 3 | import androidx.room.TypeConverter 4 | import com.infinum.sentinel.data.models.memory.formats.FormatType 5 | 6 | internal class FormatTypeConverter { 7 | 8 | @TypeConverter 9 | fun toEnum(name: String): FormatType = FormatType.valueOf(name) 10 | 11 | @TypeConverter 12 | fun fromEnum(type: FormatType): String = type.name 13 | } 14 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/data/sources/local/room/typeconverters/TriggerTypeConverter.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.sources.local.room.typeconverters 2 | 3 | import androidx.room.TypeConverter 4 | import com.infinum.sentinel.data.models.memory.triggers.TriggerType 5 | 6 | internal class TriggerTypeConverter { 7 | 8 | @TypeConverter 9 | fun toEnum(name: String): TriggerType = TriggerType.valueOf(name) 10 | 11 | @TypeConverter 12 | fun fromEnum(type: TriggerType): String = type.name 13 | } 14 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/data/sources/memory/bundles/BundlesCache.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.sources.memory.bundles 2 | 3 | import com.infinum.sentinel.domain.bundle.descriptor.models.BundleDescriptor 4 | import kotlinx.coroutines.flow.SharedFlow 5 | 6 | internal interface BundlesCache { 7 | 8 | suspend fun clear() 9 | 10 | suspend fun save(descriptor: BundleDescriptor) 11 | 12 | fun load(): SharedFlow> 13 | } 14 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/data/sources/memory/bundles/InMemoryBundlesCache.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.sources.memory.bundles 2 | 3 | import com.infinum.sentinel.domain.bundle.descriptor.models.BundleDescriptor 4 | import kotlinx.coroutines.flow.MutableSharedFlow 5 | import kotlinx.coroutines.flow.SharedFlow 6 | import kotlinx.coroutines.flow.asSharedFlow 7 | import me.tatarka.inject.annotations.Inject 8 | 9 | @Inject 10 | internal class InMemoryBundlesCache : BundlesCache { 11 | 12 | private val descriptors = mutableListOf() 13 | private val descriptorsFlow = MutableSharedFlow>(replay = 1) 14 | 15 | override suspend fun clear() { 16 | descriptors.clear() 17 | descriptorsFlow.emit(descriptors.toList()) 18 | } 19 | 20 | override suspend fun save(descriptor: BundleDescriptor) { 21 | descriptors.add(0, descriptor) 22 | descriptorsFlow.emit(descriptors.toList()) 23 | } 24 | 25 | override fun load(): SharedFlow> = 26 | descriptorsFlow.asSharedFlow() 27 | } 28 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/data/sources/memory/certificate/CertificateCache.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.sources.memory.certificate 2 | 3 | import com.infinum.sentinel.data.models.raw.CertificateData 4 | 5 | internal interface CertificateCache { 6 | 7 | fun save(value: CertificateData) 8 | 9 | fun load(): CertificateData 10 | 11 | fun clear() 12 | } 13 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/data/sources/memory/certificate/InMemoryCertificateCache.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.sources.memory.certificate 2 | 3 | import com.infinum.sentinel.data.models.raw.CertificateData 4 | import me.tatarka.inject.annotations.Inject 5 | 6 | @Inject 7 | internal class InMemoryCertificateCache : CertificateCache { 8 | 9 | private var value: CertificateData? = null 10 | 11 | override fun save(value: CertificateData) { 12 | this.value = value 13 | } 14 | 15 | override fun load(): CertificateData { 16 | return this.value!! 17 | } 18 | 19 | override fun clear() { 20 | this.value = null 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/data/sources/memory/preference/InMemoryPreferenceCache.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.sources.memory.preference 2 | 3 | import com.infinum.sentinel.data.models.raw.PreferenceType 4 | import me.tatarka.inject.annotations.Inject 5 | 6 | @Inject 7 | internal class InMemoryPreferenceCache : PreferenceCache { 8 | 9 | private var name: String? = null 10 | private var tuple: Triple? = null 11 | 12 | override fun save(name: String, tuple: Triple) { 13 | this.name = name 14 | this.tuple = tuple 15 | } 16 | 17 | override fun load(): Pair> { 18 | return this.name!! to this.tuple!! 19 | } 20 | 21 | override fun clear() { 22 | this.name = null 23 | this.tuple = null 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/data/sources/memory/preference/PreferenceCache.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.sources.memory.preference 2 | 3 | import com.infinum.sentinel.data.models.raw.PreferenceType 4 | 5 | internal interface PreferenceCache { 6 | 7 | fun save(name: String, tuple: Triple) 8 | 9 | fun load(): Pair> 10 | 11 | fun clear() 12 | } 13 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/data/sources/memory/triggers/TriggersCache.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.sources.memory.triggers 2 | 3 | import com.infinum.sentinel.data.models.memory.triggers.airplanemode.AirplaneModeOnTrigger 4 | import com.infinum.sentinel.data.models.memory.triggers.foreground.ForegroundTrigger 5 | import com.infinum.sentinel.data.models.memory.triggers.manual.ManualTrigger 6 | import com.infinum.sentinel.data.models.memory.triggers.proximity.ProximityTrigger 7 | import com.infinum.sentinel.data.models.memory.triggers.shake.ShakeTrigger 8 | import com.infinum.sentinel.data.models.memory.triggers.usb.UsbConnectedTrigger 9 | 10 | internal interface TriggersCache { 11 | 12 | fun manual(): ManualTrigger 13 | 14 | fun foreground(): ForegroundTrigger 15 | 16 | fun shake(): ShakeTrigger 17 | 18 | fun proximity(): ProximityTrigger 19 | 20 | fun usbConnected(): UsbConnectedTrigger 21 | 22 | fun airplaneModeOn(): AirplaneModeOnTrigger 23 | } 24 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/data/sources/raw/collectors/Collector.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.sources.raw.collectors 2 | 3 | internal interface Collector { 4 | 5 | operator fun invoke(): T 6 | 7 | operator fun invoke(filter: Boolean): T { 8 | return invoke() 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/data/sources/raw/collectors/ToolsCollector.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.sources.raw.collectors 2 | 3 | import com.infinum.sentinel.Sentinel 4 | import com.infinum.sentinel.domain.collectors.Collectors 5 | import me.tatarka.inject.annotations.Assisted 6 | import me.tatarka.inject.annotations.Inject 7 | 8 | @Inject 9 | internal class ToolsCollector( 10 | @Assisted private val tools: Set 11 | ) : Collectors.Tools { 12 | 13 | override fun invoke() = 14 | tools.filterNot { it.name() == 0 }.toSet() 15 | } 16 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/data/sources/raw/formatters/Formatter.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.data.sources.raw.formatters 2 | 3 | import com.infinum.sentinel.data.models.local.CrashEntity 4 | 5 | internal interface Formatter { 6 | 7 | companion object { 8 | internal const val APPLICATION = "application" 9 | internal const val DEVICE = "device" 10 | internal const val PERMISSIONS = "permissions" 11 | internal const val NAME = "name" 12 | internal const val STATUS = "status" 13 | internal const val PREFERENCES = "preferences" 14 | internal const val CRASH = "crash" 15 | internal const val VALUES = "values" 16 | } 17 | 18 | operator fun invoke(): String 19 | 20 | fun formatCrash(includeAllData: Boolean, entity: CrashEntity): String 21 | 22 | fun application(): Model 23 | 24 | fun permissions(): Collection 25 | 26 | fun device(): Model 27 | 28 | fun preferences(): Collection 29 | 30 | fun crash(entity: CrashEntity): Model 31 | } 32 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/di/WorkManagerInitializer.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.di 2 | 3 | import com.infinum.sentinel.di.component.DomainComponent 4 | import com.infinum.sentinel.ui.certificates.observer.CertificateCheckWorker 5 | import com.infinum.sentinel.ui.certificates.observer.DelegateWorker 6 | import com.infinum.sentinel.ui.certificates.observer.SentinelWorkerFactory 7 | 8 | internal object WorkManagerInitializer { 9 | 10 | fun init(domainComponent: DomainComponent) { 11 | DelegateWorker.workerFactories[CertificateCheckWorker.NAME] = 12 | SentinelWorkerFactory(domainComponent.collectors, domainComponent.notificationFactory) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/di/scope/DataScope.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.di.scope 2 | 3 | import me.tatarka.inject.annotations.Scope 4 | 5 | @Scope 6 | internal annotation class DataScope 7 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/di/scope/DomainScope.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.di.scope 2 | 3 | import me.tatarka.inject.annotations.Scope 4 | 5 | @Scope 6 | internal annotation class DomainScope 7 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/di/scope/PresentationScope.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.di.scope 2 | 3 | import me.tatarka.inject.annotations.Scope 4 | 5 | @Scope 6 | internal annotation class PresentationScope 7 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/domain/Factories.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.domain 2 | 3 | import com.infinum.sentinel.domain.collectors.Collectors 4 | import com.infinum.sentinel.domain.formatters.Formatters 5 | 6 | internal interface Factories { 7 | 8 | interface Collector { 9 | 10 | fun device(): Collectors.Device 11 | 12 | fun application(): Collectors.Application 13 | 14 | fun permissions(): Collectors.Permissions 15 | 16 | fun preferences(): Collectors.Preferences 17 | 18 | fun certificates(): Collectors.Certificates 19 | 20 | fun tools(): Collectors.Tools 21 | } 22 | 23 | interface Formatter { 24 | 25 | fun plain(): Formatters.Plain 26 | 27 | fun markdown(): Formatters.Markdown 28 | 29 | fun json(): Formatters.Json 30 | 31 | fun xml(): Formatters.Xml 32 | 33 | fun html(): Formatters.Html 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/domain/bundle/descriptor/BundlesRepository.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.domain.bundle.descriptor 2 | 3 | import com.infinum.sentinel.data.sources.memory.bundles.BundlesCache 4 | import com.infinum.sentinel.domain.Repositories 5 | import com.infinum.sentinel.domain.bundle.descriptor.models.BundleDescriptor 6 | import com.infinum.sentinel.domain.bundle.descriptor.models.BundleParameters 7 | import kotlinx.coroutines.flow.Flow 8 | import me.tatarka.inject.annotations.Inject 9 | 10 | @Inject 11 | internal class BundlesRepository( 12 | private val cache: BundlesCache 13 | ) : Repositories.Bundles { 14 | 15 | override suspend fun clear() = 16 | cache.clear() 17 | 18 | override suspend fun save(input: BundleParameters) { 19 | input.descriptor?.let { cache.save(it) } 20 | ?: error("Cannot save null descriptors") 21 | } 22 | 23 | override fun load(input: BundleParameters): Flow> = 24 | cache.load() 25 | } 26 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/domain/bundle/descriptor/models/BundleDescriptor.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.domain.bundle.descriptor.models 2 | 3 | import com.infinum.sentinel.data.models.memory.bundles.BundleTree 4 | import com.infinum.sentinel.ui.bundles.callbacks.BundleCallSite 5 | 6 | internal data class BundleDescriptor( 7 | val timestamp: Long, 8 | val className: String?, 9 | val callSite: BundleCallSite, 10 | val bundleTree: BundleTree, 11 | val limit: Int = 0 12 | ) 13 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/domain/bundle/descriptor/models/BundleParameters.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.domain.bundle.descriptor.models 2 | 3 | import com.infinum.sentinel.domain.shared.base.BaseParameters 4 | 5 | internal data class BundleParameters( 6 | val bundleId: String? = null, 7 | val descriptor: BundleDescriptor? = null 8 | ) : BaseParameters 9 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/domain/bundle/monitor/BundleMonitorRepository.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.domain.bundle.monitor 2 | 3 | import com.infinum.sentinel.data.models.local.BundleMonitorEntity 4 | import com.infinum.sentinel.data.sources.local.room.dao.BundleMonitorDao 5 | import com.infinum.sentinel.domain.Repositories 6 | import com.infinum.sentinel.domain.bundle.monitor.models.BundleMonitorParameters 7 | import kotlinx.coroutines.flow.Flow 8 | import me.tatarka.inject.annotations.Inject 9 | 10 | @Inject 11 | internal class BundleMonitorRepository( 12 | private val dao: BundleMonitorDao 13 | ) : Repositories.BundleMonitor { 14 | 15 | override suspend fun save(input: BundleMonitorParameters) { 16 | input.entity?.let { dao.save(it) } 17 | ?: error("Cannot save null entities") 18 | } 19 | 20 | override fun load(input: BundleMonitorParameters): Flow = 21 | dao.load() 22 | } 23 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/domain/bundle/monitor/models/BundleMonitorParameters.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.domain.bundle.monitor.models 2 | 3 | import com.infinum.sentinel.data.models.local.BundleMonitorEntity 4 | import com.infinum.sentinel.domain.shared.base.BaseParameters 5 | 6 | internal data class BundleMonitorParameters( 7 | val query: String? = null, 8 | val entity: BundleMonitorEntity? = null 9 | ) : BaseParameters 10 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/domain/bundle/shared/BundlesParameters.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.domain.bundle.shared 2 | 3 | import com.infinum.sentinel.domain.bundle.descriptor.models.BundleParameters 4 | import com.infinum.sentinel.domain.bundle.monitor.models.BundleMonitorParameters 5 | import com.infinum.sentinel.domain.shared.base.BaseParameters 6 | 7 | internal data class BundlesParameters( 8 | val monitor: BundleMonitorParameters, 9 | val details: BundleParameters 10 | ) : BaseParameters 11 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/domain/certificate/CertificateRepository.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.domain.certificate 2 | 3 | import com.infinum.sentinel.data.models.raw.CertificateData 4 | import com.infinum.sentinel.data.sources.memory.certificate.CertificateCache 5 | import com.infinum.sentinel.domain.Repositories 6 | import com.infinum.sentinel.domain.certificate.models.CertificateParameters 7 | import me.tatarka.inject.annotations.Inject 8 | 9 | @Inject 10 | internal class CertificateRepository( 11 | private val memoryCache: CertificateCache 12 | ) : Repositories.Certificate { 13 | 14 | override fun cache(cache: CertificateParameters.Cache) { 15 | memoryCache.save(cache.value) 16 | } 17 | 18 | override fun consume(): CertificateData { 19 | val item = memoryCache.load() 20 | memoryCache.clear() 21 | return item 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/domain/certificate/models/CertificateParameters.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.domain.certificate.models 2 | 3 | import com.infinum.sentinel.data.models.raw.CertificateData 4 | import com.infinum.sentinel.domain.shared.base.BaseParameters 5 | 6 | internal sealed class CertificateParameters : BaseParameters { 7 | 8 | data class Cache( 9 | val value: CertificateData 10 | ) : CertificateParameters() 11 | } 12 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/domain/certificate/monitor/CertificateMonitorRepository.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.domain.certificate.monitor 2 | 3 | import com.infinum.sentinel.data.models.local.CertificateMonitorEntity 4 | import com.infinum.sentinel.data.sources.local.room.dao.CertificateMonitorDao 5 | import com.infinum.sentinel.domain.Repositories 6 | import com.infinum.sentinel.domain.certificate.monitor.models.CertificateMonitorParameters 7 | import kotlinx.coroutines.flow.Flow 8 | import me.tatarka.inject.annotations.Inject 9 | 10 | @Inject 11 | internal class CertificateMonitorRepository( 12 | private val dao: CertificateMonitorDao 13 | ) : Repositories.CertificateMonitor { 14 | 15 | override suspend fun save(input: CertificateMonitorParameters) { 16 | input.entity?.let { dao.save(it) } 17 | ?: error("Cannot save null entities") 18 | } 19 | 20 | override fun load(input: CertificateMonitorParameters): Flow = 21 | dao.load() 22 | } 23 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/domain/certificate/monitor/models/CertificateMonitorParameters.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.domain.certificate.monitor.models 2 | 3 | import com.infinum.sentinel.data.models.local.CertificateMonitorEntity 4 | import com.infinum.sentinel.domain.shared.base.BaseParameters 5 | 6 | internal data class CertificateMonitorParameters( 7 | val entity: CertificateMonitorEntity? = null 8 | ) : BaseParameters 9 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/domain/collectors/CollectorFactory.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.domain.collectors 2 | 3 | import com.infinum.sentinel.domain.Factories 4 | 5 | @Suppress("LongParameterList") 6 | internal class CollectorFactory( 7 | private val device: Collectors.Device, 8 | private val application: Collectors.Application, 9 | private val permissions: Collectors.Permissions, 10 | private val preferences: Collectors.Preferences, 11 | private val certificates: Collectors.Certificates, 12 | private val tools: Collectors.Tools 13 | ) : Factories.Collector { 14 | 15 | override fun device(): Collectors.Device = device 16 | 17 | override fun application(): Collectors.Application = application 18 | 19 | override fun permissions(): Collectors.Permissions = permissions 20 | 21 | override fun preferences(): Collectors.Preferences = preferences 22 | 23 | override fun certificates(): Collectors.Certificates = certificates 24 | 25 | override fun tools(): Collectors.Tools = tools 26 | } 27 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/domain/collectors/Collectors.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.domain.collectors 2 | 3 | import com.infinum.sentinel.Sentinel 4 | import com.infinum.sentinel.data.models.raw.ApplicationData 5 | import com.infinum.sentinel.data.models.raw.CertificateData 6 | import com.infinum.sentinel.data.models.raw.DeviceData 7 | import com.infinum.sentinel.data.models.raw.PreferencesData 8 | import com.infinum.sentinel.data.models.raw.certificates.CertificateType 9 | import com.infinum.sentinel.data.sources.raw.collectors.Collector 10 | 11 | internal interface Collectors { 12 | 13 | interface Device : Collector 14 | 15 | interface Application : Collector 16 | 17 | interface Permissions : Collector> 18 | 19 | interface Preferences : Collector> 20 | 21 | interface Certificates : Collector>> 22 | 23 | interface Tools : Collector> 24 | } 25 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/domain/crash/models/CrashParameters.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.domain.crash.models 2 | 3 | import com.infinum.sentinel.domain.shared.base.BaseParameters 4 | 5 | internal data class CrashParameters( 6 | val query: String? = null, 7 | val crashId: Long? = null, 8 | ) : BaseParameters 9 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/domain/crash/monitor/CrashMonitorRepository.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.domain.crash.monitor 2 | 3 | import com.infinum.sentinel.data.models.local.CrashMonitorEntity 4 | import com.infinum.sentinel.data.sources.local.room.dao.CrashMonitorDao 5 | import com.infinum.sentinel.domain.Repositories 6 | import com.infinum.sentinel.domain.crash.monitor.models.CrashMonitorParameters 7 | import kotlinx.coroutines.flow.Flow 8 | import me.tatarka.inject.annotations.Inject 9 | 10 | @Inject 11 | internal class CrashMonitorRepository( 12 | private val dao: CrashMonitorDao 13 | ) : Repositories.CrashMonitor { 14 | 15 | override suspend fun save(input: CrashMonitorParameters) { 16 | input.entity?.let { dao.save(it) } 17 | ?: error("Cannot save null entities") 18 | } 19 | 20 | override fun load(input: CrashMonitorParameters): Flow = 21 | dao.load() 22 | } 23 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/domain/crash/monitor/models/CrashMonitorParameters.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.domain.crash.monitor.models 2 | 3 | import com.infinum.sentinel.data.models.local.CrashMonitorEntity 4 | import com.infinum.sentinel.domain.shared.base.BaseParameters 5 | 6 | internal data class CrashMonitorParameters( 7 | val entity: CrashMonitorEntity? = null 8 | ) : BaseParameters 9 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/domain/formats/FormatsRepository.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.domain.formats 2 | 3 | import com.infinum.sentinel.data.models.local.FormatEntity 4 | import com.infinum.sentinel.data.sources.local.room.dao.FormatsDao 5 | import com.infinum.sentinel.domain.Repositories 6 | import com.infinum.sentinel.domain.formats.models.FormatsParameters 7 | import kotlinx.coroutines.flow.Flow 8 | import me.tatarka.inject.annotations.Inject 9 | 10 | @Inject 11 | internal class FormatsRepository( 12 | private val dao: FormatsDao 13 | ) : Repositories.Formats { 14 | 15 | override suspend fun save(input: FormatsParameters) { 16 | input.entities?.let { dao.save(it) } 17 | ?: error("Cannot save null entities") 18 | } 19 | 20 | override fun load(input: FormatsParameters): Flow = 21 | dao.load() 22 | } 23 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/domain/formats/models/FormatsParameters.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.domain.formats.models 2 | 3 | import com.infinum.sentinel.data.models.local.FormatEntity 4 | import com.infinum.sentinel.domain.shared.base.BaseParameters 5 | 6 | internal data class FormatsParameters( 7 | val entities: List? = null 8 | ) : BaseParameters 9 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/domain/formatters/FormatterFactory.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.domain.formatters 2 | 3 | import com.infinum.sentinel.domain.Factories 4 | 5 | internal class FormatterFactory( 6 | private val plain: Formatters.Plain, 7 | private val markdown: Formatters.Markdown, 8 | private val json: Formatters.Json, 9 | private val xml: Formatters.Xml, 10 | private val html: Formatters.Html 11 | ) : Factories.Formatter { 12 | 13 | override fun plain(): Formatters.Plain = plain 14 | 15 | override fun markdown(): Formatters.Markdown = markdown 16 | 17 | override fun json(): Formatters.Json = json 18 | 19 | override fun xml(): Formatters.Xml = xml 20 | 21 | override fun html(): Formatters.Html = html 22 | } 23 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/domain/formatters/Formatters.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.domain.formatters 2 | 3 | import com.infinum.sentinel.data.sources.raw.formatters.Formatter 4 | import org.json.JSONArray 5 | import org.json.JSONObject 6 | 7 | internal interface Formatters { 8 | 9 | interface Plain : Formatter 10 | 11 | interface Markdown : Formatter 12 | 13 | interface Json : Formatter 14 | 15 | interface Xml : Formatter 16 | 17 | interface Html : Formatter 18 | } 19 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/domain/shared/base/BaseParameters.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.domain.shared.base 2 | 3 | internal interface BaseParameters 4 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/domain/shared/base/BaseRepository.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.domain.shared.base 2 | 3 | import kotlinx.coroutines.flow.Flow 4 | 5 | internal interface BaseRepository { 6 | 7 | suspend fun save(input: InputModel): Unit = throw NotImplementedError() 8 | 9 | fun load(input: InputModel): Flow = throw NotImplementedError() 10 | } 11 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/domain/triggers/models/TriggerParameters.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.domain.triggers.models 2 | 3 | import com.infinum.sentinel.data.models.local.TriggerEntity 4 | import com.infinum.sentinel.domain.shared.base.BaseParameters 5 | 6 | internal data class TriggerParameters( 7 | val entity: TriggerEntity? = null 8 | ) : BaseParameters 9 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/extensions/Activity.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.extensions 2 | 3 | import android.app.Activity 4 | import android.content.pm.PackageManager 5 | import android.os.Build 6 | import android.os.Bundle 7 | import com.infinum.sentinel.BuildConfig 8 | import com.infinum.sentinel.R 9 | 10 | internal val Activity.isMonitoredScreen: Boolean 11 | get() = if (BuildConfig.DEBUG) { 12 | true 13 | } else { 14 | val metaKey = getString(R.string.sentinel_infinum_monitored) 15 | 16 | val metadata: Bundle? = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { 17 | packageManager 18 | .getActivityInfo( 19 | this.componentName, 20 | PackageManager.ComponentInfoFlags.of(PackageManager.GET_META_DATA.toLong()) 21 | ) 22 | .metaData 23 | } else { 24 | @Suppress("DEPRECATION") 25 | packageManager 26 | .getActivityInfo(this.componentName, PackageManager.GET_META_DATA) 27 | .metaData 28 | } 29 | 30 | metadata?.let { 31 | if (it.containsKey(metaKey)) { 32 | it.getBoolean(metaKey) 33 | } else { 34 | true 35 | } 36 | } ?: true 37 | } 38 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/extensions/Array.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.extensions 2 | 3 | internal fun Array.asStringArray(): List = 4 | map { 5 | if (it.isNativeMethod) { 6 | "${it.className}.${it.methodName}[Native Method]" 7 | } else { 8 | "${it.className}.${it.methodName}(${it.fileName}:${it.lineNumber})" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/extensions/ByteArray.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.extensions 2 | 3 | import java.math.BigInteger 4 | import java.util.Locale 5 | 6 | internal fun ByteArray.asHexString(): String = 7 | String.format( 8 | Locale.ENGLISH, 9 | "%0${this.size shl 1}X", 10 | BigInteger(1, this) 11 | ) 12 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/extensions/FormatType.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.extensions 2 | 3 | import com.infinum.sentinel.data.models.memory.formats.FormatType 4 | import com.infinum.sentinel.domain.Factories 5 | 6 | internal fun FormatType.formatter(formatters: Factories.Formatter) = 7 | when (this) { 8 | FormatType.PLAIN -> formatters.plain() 9 | FormatType.MARKDOWN -> formatters.markdown() 10 | FormatType.JSON -> formatters.json() 11 | FormatType.XML -> formatters.xml() 12 | FormatType.HTML -> formatters.html() 13 | } 14 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/extensions/IntentBuilder.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.extensions 2 | 3 | import androidx.core.app.ShareCompat 4 | import com.infinum.sentinel.R 5 | import com.infinum.sentinel.ui.shared.Constants 6 | 7 | internal fun ShareCompat.IntentBuilder.shareText(text: String) = 8 | this.setChooserTitle(R.string.sentinel_name) 9 | .setType(Constants.SHARE_MIME_TYPE) 10 | .setText(text) 11 | .startChooser() 12 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/extensions/Menu.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.extensions 2 | 3 | import android.view.Menu 4 | import androidx.appcompat.widget.SearchView 5 | import com.infinum.sentinel.R 6 | 7 | internal val Menu.searchView 8 | get() = findItem(R.id.search)?.actionView as? SearchView 9 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/extensions/SearchView.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.extensions 2 | 3 | import android.widget.ImageView 4 | import androidx.appcompat.widget.SearchView 5 | import com.infinum.sentinel.R 6 | import com.infinum.sentinel.ui.shared.search.SimpleQueryTextListener 7 | 8 | internal fun SearchView.setup( 9 | hint: String?, 10 | onSearchClosed: () -> Unit, 11 | onQueryTextChanged: (String?) -> Unit 12 | ) { 13 | setIconifiedByDefault(true) 14 | isSubmitButtonEnabled = false 15 | isQueryRefinementEnabled = true 16 | maxWidth = Integer.MAX_VALUE 17 | queryHint = hint 18 | setOnCloseListener { 19 | onSearchClosed() 20 | false 21 | } 22 | setOnQueryTextListener( 23 | SimpleQueryTextListener(onQueryTextChanged) 24 | ) 25 | findViewById(androidx.appcompat.R.id.search_close_btn) 26 | .setImageResource(R.drawable.sentinel_ic_clear_search) 27 | } 28 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/extensions/String.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.extensions 2 | 3 | import android.annotation.SuppressLint 4 | import java.util.Locale 5 | import java.util.regex.Matcher 6 | import java.util.regex.Pattern 7 | 8 | @SuppressLint("DefaultLocale") 9 | internal fun String.sanitize() = this.lowercase(Locale.getDefault()).replace(" ", "_") 10 | 11 | internal fun String.allOccurrenceIndexes(term: String): ArrayList { 12 | val indexes = arrayListOf() 13 | var idx = 0 14 | while (indexOf(term, idx, true).also { idx = it } >= 0) { 15 | indexes.add(idx) 16 | idx++ 17 | } 18 | return indexes 19 | } 20 | 21 | internal fun String.asASN(): List { 22 | val result = mutableListOf() 23 | val matcher: Matcher = Pattern.compile("(\\w+)=([^,\\n\\r]+)").matcher(this) 24 | while (matcher.find()) { 25 | val key = matcher.group(1).orEmpty().trim() 26 | val value = matcher.group(2).orEmpty().trim() 27 | result.add("$key = $value") 28 | } 29 | return result.toList() 30 | } 31 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/extensions/Thread.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.extensions 2 | 3 | import com.infinum.sentinel.data.models.local.crash.ThreadData 4 | 5 | internal fun Thread.asThreadData(): ThreadData = 6 | ThreadData( 7 | id = id, 8 | name = name, 9 | isDaemon = isDaemon, 10 | state = state.name, 11 | group = threadGroup.asThreadGroupData(), 12 | priority = priority 13 | ) 14 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/extensions/ThreadGroup.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.extensions 2 | 3 | import com.infinum.sentinel.data.models.local.crash.ThreadGroupData 4 | 5 | internal fun ThreadGroup?.asThreadGroupData(): ThreadGroupData? = 6 | this?.let { 7 | ThreadGroupData( 8 | name = it.name, 9 | parent = it.parent.name, 10 | activeCount = it.activeCount() 11 | ) 12 | } 13 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/extensions/Throwable.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.extensions 2 | 3 | import com.infinum.sentinel.data.models.local.crash.ExceptionData 4 | 5 | internal fun Throwable.asExceptionData(isANR: Boolean = false): ExceptionData = 6 | ExceptionData( 7 | name = this.toString().replace(": $message", "", true), 8 | message = message, 9 | stackTrace = stackTrace.asStringArray(), 10 | file = stackTrace.getOrNull(0)?.fileName, 11 | lineNumber = stackTrace.getOrNull(0)?.lineNumber ?: Int.MIN_VALUE, 12 | isANRException = isANR 13 | ) 14 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/bundles/BundlesActivity.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.bundles 2 | 3 | import android.os.Bundle 4 | import androidx.annotation.RestrictTo 5 | import com.infinum.sentinel.ui.shared.base.BaseChildActivity 6 | 7 | @RestrictTo(RestrictTo.Scope.LIBRARY) 8 | internal class BundlesActivity : BaseChildActivity() { 9 | 10 | override fun onCreate(savedInstanceState: Bundle?) { 11 | super.onCreate(savedInstanceState) 12 | 13 | supportFragmentManager.beginTransaction() 14 | .replace(android.R.id.content, BundlesFragment.newInstance(), BundlesFragment.TAG) 15 | .commit() 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/bundles/BundlesDiffUtil.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.bundles 2 | 3 | import androidx.recyclerview.widget.DiffUtil 4 | import com.infinum.sentinel.domain.bundle.descriptor.models.BundleDescriptor 5 | 6 | internal class BundlesDiffUtil : DiffUtil.ItemCallback() { 7 | 8 | override fun areItemsTheSame(oldItem: BundleDescriptor, newItem: BundleDescriptor): Boolean { 9 | return oldItem.bundleTree.id == newItem.bundleTree.id 10 | } 11 | 12 | override fun areContentsTheSame(oldItem: BundleDescriptor, newItem: BundleDescriptor): Boolean { 13 | return oldItem == newItem 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/bundles/BundlesEvent.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.bundles 2 | 3 | import com.infinum.sentinel.domain.bundle.descriptor.models.BundleDescriptor 4 | 5 | internal sealed class BundlesEvent { 6 | 7 | data class BundlesIntercepted( 8 | val value: List 9 | ) : BundlesEvent() 10 | } 11 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/bundles/callbacks/BundleCallSite.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.bundles.callbacks 2 | 3 | import androidx.annotation.DrawableRes 4 | import androidx.annotation.StringRes 5 | import com.infinum.sentinel.R 6 | 7 | internal enum class BundleCallSite(@DrawableRes val icon: Int, @StringRes val text: Int) { 8 | ACTIVITY_INTENT_EXTRAS( 9 | R.drawable.sentinel_ic_activity_intent_extras, 10 | R.string.sentinel_bundle_activity_intent_extras 11 | ), 12 | ACTIVITY_SAVED_STATE( 13 | R.drawable.sentinel_ic_activity_saved_state, 14 | R.string.sentinel_bundle_activity_saved_state 15 | ), 16 | FRAGMENT_ARGUMENTS( 17 | R.drawable.sentinel_ic_fragment_arguments, 18 | R.string.sentinel_fragment_arguments 19 | ), 20 | FRAGMENT_SAVED_STATE( 21 | R.drawable.sentinel_ic_fragment_saved_state, 22 | R.string.sentinel_fragment_saved_state 23 | ) 24 | } 25 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/bundles/callbacks/BundleMonitorNotificationCallbacks.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.bundles.callbacks 2 | 3 | import android.app.Activity 4 | import android.app.Application 5 | import android.os.Bundle 6 | 7 | internal class BundleMonitorNotificationCallbacks : Application.ActivityLifecycleCallbacks { 8 | 9 | var currentActivity: Activity? = null 10 | 11 | override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) { 12 | currentActivity = activity 13 | } 14 | 15 | override fun onActivityStarted(activity: Activity) { 16 | currentActivity = activity 17 | } 18 | 19 | override fun onActivityResumed(activity: Activity) { 20 | currentActivity = activity 21 | } 22 | 23 | override fun onActivityPaused(activity: Activity) = Unit 24 | 25 | override fun onActivityDestroyed(activity: Activity) = Unit 26 | 27 | override fun onActivityStopped(activity: Activity) = Unit 28 | 29 | override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) = Unit 30 | } 31 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/bundles/details/BundleDetailsActivity.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.bundles.details 2 | 3 | import android.os.Bundle 4 | import androidx.annotation.RestrictTo 5 | import com.infinum.sentinel.ui.shared.Constants 6 | import com.infinum.sentinel.ui.shared.base.BaseChildActivity 7 | 8 | @RestrictTo(RestrictTo.Scope.LIBRARY) 9 | internal class BundleDetailsActivity : BaseChildActivity() { 10 | 11 | override fun onCreate(savedInstanceState: Bundle?) { 12 | super.onCreate(savedInstanceState) 13 | 14 | supportFragmentManager.beginTransaction() 15 | .replace( 16 | android.R.id.content, 17 | BundleDetailsFragment.newInstance(intent.extras?.getString(Constants.Keys.BUNDLE_ID)), 18 | BundleDetailsFragment.TAG 19 | ) 20 | .commit() 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/bundles/details/BundleDetailsAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.bundles.details 2 | 3 | import android.view.LayoutInflater 4 | import android.view.ViewGroup 5 | import androidx.recyclerview.widget.ListAdapter 6 | import com.infinum.sentinel.data.models.memory.bundles.BundleTree 7 | import com.infinum.sentinel.databinding.SentinelItemBundleKeyBinding 8 | 9 | internal class BundleDetailsAdapter( 10 | private val parentSize: Int 11 | ) : ListAdapter(BundleDetailsDiffUtil()) { 12 | 13 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BundleDetailsViewHolder = 14 | BundleDetailsViewHolder( 15 | SentinelItemBundleKeyBinding.inflate( 16 | LayoutInflater.from(parent.context), 17 | parent, 18 | false 19 | ) 20 | ) 21 | 22 | override fun onBindViewHolder(holder: BundleDetailsViewHolder, position: Int) = 23 | holder.bind(getItem(position), parentSize) 24 | 25 | override fun onViewRecycled(holder: BundleDetailsViewHolder) { 26 | holder.unbind() 27 | super.onViewRecycled(holder) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/bundles/details/BundleDetailsDiffUtil.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.bundles.details 2 | 3 | import androidx.recyclerview.widget.DiffUtil 4 | import com.infinum.sentinel.data.models.memory.bundles.BundleTree 5 | 6 | internal class BundleDetailsDiffUtil : DiffUtil.ItemCallback() { 7 | 8 | override fun areItemsTheSame(oldItem: BundleTree, newItem: BundleTree): Boolean { 9 | return oldItem.id == newItem.id 10 | } 11 | 12 | override fun areContentsTheSame(oldItem: BundleTree, newItem: BundleTree): Boolean { 13 | return oldItem == newItem 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/bundles/details/BundleDetailsState.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.bundles.details 2 | 3 | import com.infinum.sentinel.domain.bundle.descriptor.models.BundleDescriptor 4 | 5 | internal sealed class BundleDetailsState { 6 | 7 | data class Data( 8 | val value: BundleDescriptor 9 | ) : BundleDetailsState() 10 | } 11 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/bundles/details/BundleDetailsViewHolder.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.bundles.details 2 | 3 | import android.text.format.Formatter 4 | import androidx.recyclerview.widget.RecyclerView 5 | import com.infinum.sentinel.data.models.memory.bundles.BundleTree 6 | import com.infinum.sentinel.databinding.SentinelItemBundleKeyBinding 7 | 8 | internal class BundleDetailsViewHolder( 9 | private val binding: SentinelItemBundleKeyBinding 10 | ) : RecyclerView.ViewHolder(binding.root) { 11 | 12 | fun bind(item: BundleTree?, parentSize: Int) = 13 | item?.let { tree -> 14 | with(binding) { 15 | nameView.text = tree.id 16 | sizeView.text = Formatter.formatFileSize(sizeView.context, tree.size.toLong()) 17 | magnitudeView.progress = tree.size 18 | magnitudeView.max = parentSize 19 | } 20 | } ?: unbind() 21 | 22 | fun unbind() = 23 | with(binding) { 24 | nameView.text = null 25 | sizeView.text = null 26 | magnitudeView.progress = 0 27 | magnitudeView.max = 0 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/bundles/details/BundleDetailsViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.bundles.details 2 | 3 | import androidx.lifecycle.viewModelScope 4 | import com.infinum.sentinel.domain.Repositories 5 | import com.infinum.sentinel.domain.bundle.descriptor.models.BundleParameters 6 | import com.infinum.sentinel.ui.shared.base.BaseChildViewModel 7 | import kotlinx.coroutines.flow.flowOn 8 | import kotlinx.coroutines.flow.launchIn 9 | import kotlinx.coroutines.flow.map 10 | import kotlinx.coroutines.flow.onEach 11 | import me.tatarka.inject.annotations.Inject 12 | 13 | @Inject 14 | internal class BundleDetailsViewModel( 15 | private val bundles: Repositories.Bundles 16 | ) : BaseChildViewModel() { 17 | 18 | private var parameters: BundleParameters = BundleParameters() 19 | 20 | override fun data() = 21 | launch { 22 | bundles.load(parameters) 23 | .flowOn(runningDispatchers) 24 | .map { it.single { descriptor -> descriptor.bundleTree.id == parameters.bundleId } } 25 | .onEach { setState(BundleDetailsState.Data(value = it)) } 26 | .launchIn(viewModelScope) 27 | } 28 | 29 | fun setBundleId(value: String?) { 30 | parameters = parameters.copy(bundleId = value) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/certificates/CertificatesActivity.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.certificates 2 | 3 | import android.os.Build 4 | import android.os.Bundle 5 | import androidx.annotation.RequiresApi 6 | import androidx.annotation.RestrictTo 7 | import com.infinum.sentinel.ui.shared.base.BaseChildActivity 8 | 9 | @RestrictTo(RestrictTo.Scope.LIBRARY) 10 | @RequiresApi(Build.VERSION_CODES.O) 11 | internal class CertificatesActivity : BaseChildActivity() { 12 | 13 | override fun onCreate(savedInstanceState: Bundle?) { 14 | super.onCreate(savedInstanceState) 15 | 16 | supportFragmentManager.beginTransaction() 17 | .replace(android.R.id.content, CertificatesFragment.newInstance(), CertificatesFragment.TAG) 18 | .commit() 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/certificates/CertificatesDiffUtil.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.certificates 2 | 3 | import androidx.recyclerview.widget.DiffUtil 4 | import com.infinum.sentinel.data.models.raw.CertificateData 5 | 6 | internal class CertificatesDiffUtil : DiffUtil.ItemCallback() { 7 | 8 | override fun areItemsTheSame(oldItem: CertificateData, newItem: CertificateData): Boolean { 9 | return oldItem.fingerprint.sha256 == newItem.fingerprint.sha256 10 | } 11 | 12 | override fun areContentsTheSame(oldItem: CertificateData, newItem: CertificateData): Boolean { 13 | return oldItem == newItem 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/certificates/CertificatesEvent.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.certificates 2 | 3 | internal sealed class CertificatesEvent { 4 | 5 | class Cached : CertificatesEvent() 6 | } 7 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/certificates/CertificatesState.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.certificates 2 | 3 | import com.infinum.sentinel.data.models.local.CertificateMonitorEntity 4 | import com.infinum.sentinel.data.models.raw.CertificateData 5 | 6 | internal sealed class CertificatesState { 7 | 8 | data class Data( 9 | val userCertificates: List, 10 | val systemCertificates: List, 11 | val settings: CertificateMonitorEntity 12 | ) : CertificatesState() 13 | } 14 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/certificates/HeaderAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.certificates 2 | 3 | import android.view.LayoutInflater 4 | import android.view.ViewGroup 5 | import androidx.annotation.StringRes 6 | import androidx.recyclerview.widget.RecyclerView 7 | import com.infinum.sentinel.databinding.SentinelItemHeaderBinding 8 | 9 | internal class HeaderAdapter( 10 | @StringRes private val title: Int, 11 | private var count: Int 12 | ) : RecyclerView.Adapter() { 13 | 14 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HeaderViewHolder = 15 | HeaderViewHolder( 16 | SentinelItemHeaderBinding.inflate( 17 | LayoutInflater.from(parent.context), 18 | parent, 19 | false 20 | ) 21 | ) 22 | 23 | override fun onBindViewHolder(holder: HeaderViewHolder, position: Int) = 24 | holder.bind(title, count) 25 | 26 | override fun onViewRecycled(holder: HeaderViewHolder) { 27 | holder.unbind() 28 | } 29 | 30 | override fun getItemCount(): Int = 1 31 | 32 | fun updateCount(count: Int) { 33 | this.count = count 34 | if (itemCount > 0) { 35 | notifyItemChanged(0) 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/certificates/HeaderViewHolder.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.certificates 2 | 3 | import androidx.annotation.StringRes 4 | import androidx.core.view.isVisible 5 | import androidx.recyclerview.widget.RecyclerView 6 | import com.infinum.sentinel.databinding.SentinelItemHeaderBinding 7 | 8 | internal class HeaderViewHolder( 9 | private val binding: SentinelItemHeaderBinding 10 | ) : RecyclerView.ViewHolder(binding.root) { 11 | 12 | fun bind(@StringRes title: Int, count: Int) { 13 | with(binding) { 14 | titleView.text = titleView.context.getString(title) 15 | countView.text = count.toString() 16 | countView.isVisible = count > 0 17 | } 18 | } 19 | 20 | fun unbind() { 21 | with(binding) { 22 | titleView.text = null 23 | countView.text = null 24 | countView.isVisible = false 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/certificates/details/CertificateDetailsActivity.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.certificates.details 2 | 3 | import android.os.Build 4 | import android.os.Bundle 5 | import androidx.annotation.RequiresApi 6 | import androidx.annotation.RestrictTo 7 | import com.infinum.sentinel.ui.shared.base.BaseChildActivity 8 | 9 | @RestrictTo(RestrictTo.Scope.LIBRARY) 10 | @RequiresApi(Build.VERSION_CODES.O) 11 | internal class CertificateDetailsActivity : BaseChildActivity() { 12 | 13 | override fun onCreate(savedInstanceState: Bundle?) { 14 | super.onCreate(savedInstanceState) 15 | 16 | supportFragmentManager.beginTransaction() 17 | .replace( 18 | android.R.id.content, 19 | CertificateDetailsFragment.newInstance(), 20 | CertificateDetailsFragment.TAG 21 | ) 22 | .commit() 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/certificates/details/CertificateDetailsState.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.certificates.details 2 | 3 | import com.infinum.sentinel.data.models.local.CertificateMonitorEntity 4 | import com.infinum.sentinel.data.models.raw.CertificateData 5 | 6 | internal sealed class CertificateDetailsState { 7 | 8 | data class Cache( 9 | val value: CertificateData, 10 | val settings: CertificateMonitorEntity 11 | ) : CertificateDetailsState() 12 | } 13 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/certificates/details/CertificateDetailsViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.certificates.details 2 | 3 | import com.infinum.sentinel.domain.Repositories 4 | import com.infinum.sentinel.domain.certificate.monitor.models.CertificateMonitorParameters 5 | import com.infinum.sentinel.ui.shared.base.BaseChildViewModel 6 | import kotlinx.coroutines.flow.first 7 | import me.tatarka.inject.annotations.Inject 8 | 9 | @Inject 10 | internal class CertificateDetailsViewModel( 11 | private val repository: Repositories.Certificate, 12 | private val monitor: Repositories.CertificateMonitor 13 | ) : BaseChildViewModel() { 14 | 15 | override fun data() = 16 | launch { 17 | val result = io { 18 | val item = repository.consume() 19 | val settings = monitor.load(CertificateMonitorParameters()).first() 20 | item to settings 21 | } 22 | setState( 23 | CertificateDetailsState.Cache( 24 | value = result.first, 25 | settings = result.second 26 | ) 27 | ) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/certificates/observer/CertificateCount.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.certificates.observer 2 | 3 | internal data class CertificateCount( 4 | val invalid: Int, 5 | val toExpire: Int 6 | ) 7 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/certificates/observer/SentinelWorkerFactory.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.certificates.observer 2 | 3 | import android.content.Context 4 | import androidx.work.ListenableWorker 5 | import androidx.work.WorkerFactory 6 | import androidx.work.WorkerParameters 7 | import com.infinum.sentinel.domain.Factories 8 | import com.infinum.sentinel.ui.shared.notification.NotificationFactory 9 | import me.tatarka.inject.annotations.Inject 10 | 11 | @Inject 12 | internal class SentinelWorkerFactory( 13 | private val collectors: Factories.Collector, 14 | private val notificationFactory: NotificationFactory 15 | ) : WorkerFactory() { 16 | 17 | override fun createWorker( 18 | appContext: Context, 19 | workerClassName: String, 20 | workerParameters: WorkerParameters 21 | ): ListenableWorker? = 22 | when (workerClassName) { 23 | CertificateCheckWorker.NAME -> 24 | CertificateCheckWorker( 25 | appContext, 26 | workerParameters, 27 | collectors, 28 | notificationFactory 29 | ) 30 | else -> null 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/crash/CrashesActivity.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.crash 2 | 3 | import android.os.Bundle 4 | import androidx.annotation.RestrictTo 5 | import com.infinum.sentinel.ui.shared.Constants 6 | import com.infinum.sentinel.ui.shared.base.BaseChildActivity 7 | 8 | @RestrictTo(RestrictTo.Scope.LIBRARY) 9 | internal class CrashesActivity : BaseChildActivity() { 10 | 11 | override fun onCreate(savedInstanceState: Bundle?) { 12 | super.onCreate(savedInstanceState) 13 | 14 | supportFragmentManager.beginTransaction() 15 | .replace( 16 | android.R.id.content, 17 | CrashesFragment.newInstance(intent.getStringExtra(Constants.Keys.APPLICATION_NAME)), 18 | CrashesFragment.TAG 19 | ) 20 | .commit() 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/crash/CrashesAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.crash 2 | 3 | import android.view.LayoutInflater 4 | import android.view.ViewGroup 5 | import androidx.recyclerview.widget.ListAdapter 6 | import com.infinum.sentinel.data.models.local.CrashEntity 7 | import com.infinum.sentinel.databinding.SentinelItemCrashBinding 8 | 9 | internal class CrashesAdapter( 10 | private val onListChanged: (Boolean) -> Unit, 11 | private val onClick: (CrashEntity) -> Unit 12 | ) : ListAdapter(CrashesDiffUtil()) { 13 | 14 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CrashViewHolder = 15 | CrashViewHolder( 16 | SentinelItemCrashBinding.inflate( 17 | LayoutInflater.from(parent.context), 18 | parent, 19 | false 20 | ) 21 | ) 22 | 23 | override fun onBindViewHolder(holder: CrashViewHolder, position: Int) = 24 | holder.bind(getItem(position), onClick) 25 | 26 | override fun onViewRecycled(holder: CrashViewHolder) { 27 | holder.unbind() 28 | } 29 | 30 | override fun onCurrentListChanged( 31 | previousList: MutableList, 32 | currentList: MutableList 33 | ) = 34 | onListChanged(currentList.isEmpty()) 35 | } 36 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/crash/CrashesDiffUtil.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.crash 2 | 3 | import androidx.recyclerview.widget.DiffUtil 4 | import com.infinum.sentinel.data.models.local.CrashEntity 5 | 6 | internal class CrashesDiffUtil : DiffUtil.ItemCallback() { 7 | 8 | override fun areItemsTheSame(oldItem: CrashEntity, newItem: CrashEntity): Boolean { 9 | return oldItem.id == newItem.id 10 | } 11 | 12 | override fun areContentsTheSame(oldItem: CrashEntity, newItem: CrashEntity): Boolean { 13 | return oldItem == newItem 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/crash/CrashesEvent.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.crash 2 | 3 | import com.infinum.sentinel.data.models.local.CrashEntity 4 | 5 | internal sealed class CrashesEvent { 6 | 7 | data class CrashesIntercepted( 8 | val value: List 9 | ) : CrashesEvent() 10 | } 11 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/crash/anr/SentinelAnrObserver.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.crash.anr 2 | 3 | import com.infinum.sentinel.Sentinel 4 | 5 | internal interface SentinelAnrObserver { 6 | 7 | fun start() 8 | 9 | fun stop() 10 | 11 | fun setListener(listener: Sentinel.ApplicationNotRespondingListener?) 12 | } 13 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/crash/details/CrashDetailsActivity.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.crash.details 2 | 3 | import android.os.Bundle 4 | import androidx.annotation.RestrictTo 5 | import com.infinum.sentinel.ui.shared.Constants 6 | import com.infinum.sentinel.ui.shared.base.BaseChildActivity 7 | 8 | @RestrictTo(RestrictTo.Scope.LIBRARY) 9 | internal class CrashDetailsActivity : BaseChildActivity() { 10 | 11 | override fun onCreate(savedInstanceState: Bundle?) { 12 | super.onCreate(savedInstanceState) 13 | 14 | intent?.getLongExtra(Constants.Keys.CRASH_ID, -1L) 15 | ?.takeUnless { it == -1L } 16 | ?.let { 17 | supportFragmentManager.beginTransaction() 18 | .replace( 19 | android.R.id.content, 20 | CrashDetailsFragment.newInstance(it), 21 | CrashDetailsFragment.TAG 22 | ) 23 | .commit() 24 | } ?: finish() 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/crash/details/CrashDetailsEvent.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.crash.details 2 | 3 | internal sealed class CrashDetailsEvent { 4 | 5 | class Removed : CrashDetailsEvent() 6 | 7 | data class Formatted( 8 | val value: String 9 | ) : CrashDetailsEvent() 10 | } 11 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/crash/details/CrashDetailsState.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.crash.details 2 | 3 | import com.infinum.sentinel.data.models.local.CrashEntity 4 | 5 | internal sealed class CrashDetailsState { 6 | 7 | data class Data( 8 | val value: CrashEntity 9 | ) : CrashDetailsState() 10 | } 11 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/crash/handler/SentinelExceptionHandler.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.crash.handler 2 | 3 | internal interface SentinelExceptionHandler : Thread.UncaughtExceptionHandler { 4 | 5 | fun start() 6 | 7 | fun stop() 8 | 9 | fun setExceptionHandler(handler: Thread.UncaughtExceptionHandler?) 10 | } 11 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/main/SentinelActivity.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.main 2 | 3 | import android.os.Bundle 4 | import androidx.annotation.RestrictTo 5 | import com.infinum.sentinel.ui.shared.base.BaseActivity 6 | import com.infinum.sentinel.ui.shared.base.BaseViewModel 7 | 8 | @RestrictTo(RestrictTo.Scope.LIBRARY) 9 | internal class SentinelActivity : BaseActivity() { 10 | 11 | override fun onCreate(savedInstanceState: Bundle?) { 12 | super.onCreate(savedInstanceState) 13 | 14 | SentinelFragment().show(supportFragmentManager, SentinelFragment.TAG) 15 | } 16 | 17 | override val viewModel: BaseViewModel? = null 18 | 19 | override fun onState(state: Nothing) = Unit 20 | 21 | override fun onEvent(event: Nothing) = Unit 22 | } 23 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/main/SentinelEvent.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.main 2 | 3 | internal sealed class SentinelEvent { 4 | 5 | data class Formatted( 6 | val value: String 7 | ) : SentinelEvent() 8 | } 9 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/main/SentinelState.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.main 2 | 3 | import android.graphics.drawable.Drawable 4 | 5 | internal sealed class SentinelState { 6 | 7 | data class ApplicationIconAndName( 8 | val icon: Drawable, 9 | val name: String 10 | ) : SentinelState() 11 | } 12 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/main/application/ApplicationState.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.main.application 2 | 3 | import com.infinum.sentinel.data.models.raw.ApplicationData 4 | 5 | internal sealed class ApplicationState { 6 | 7 | data class Data( 8 | val value: ApplicationData 9 | ) : ApplicationState() 10 | } 11 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/main/application/ApplicationViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.main.application 2 | 3 | import com.infinum.sentinel.domain.Factories 4 | import com.infinum.sentinel.ui.shared.base.BaseChildViewModel 5 | import me.tatarka.inject.annotations.Inject 6 | 7 | @Inject 8 | internal class ApplicationViewModel( 9 | private val collectors: Factories.Collector 10 | ) : BaseChildViewModel() { 11 | 12 | override fun data() = 13 | launch { 14 | val result = io { 15 | collectors.application()() 16 | } 17 | setState( 18 | ApplicationState.Data( 19 | value = result 20 | ) 21 | ) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/main/device/DeviceState.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.main.device 2 | 3 | import com.infinum.sentinel.data.models.raw.DeviceData 4 | 5 | internal sealed class DeviceState { 6 | 7 | data class Data( 8 | val value: DeviceData 9 | ) : DeviceState() 10 | } 11 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/main/device/DeviceViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.main.device 2 | 3 | import com.infinum.sentinel.domain.Factories 4 | import com.infinum.sentinel.ui.shared.base.BaseChildViewModel 5 | import me.tatarka.inject.annotations.Inject 6 | 7 | @Inject 8 | internal class DeviceViewModel( 9 | private val collectors: Factories.Collector 10 | ) : BaseChildViewModel() { 11 | 12 | override fun data() = 13 | launch { 14 | val result = io { 15 | collectors.device()() 16 | } 17 | setState( 18 | DeviceState.Data( 19 | value = result 20 | ) 21 | ) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/main/permissions/PermissionsState.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.main.permissions 2 | 3 | internal sealed class PermissionsState { 4 | 5 | data class Data( 6 | val value: Map 7 | ) : PermissionsState() 8 | } 9 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/main/permissions/PermissionsViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.main.permissions 2 | 3 | import com.infinum.sentinel.domain.Factories 4 | import com.infinum.sentinel.ui.shared.base.BaseChildViewModel 5 | import me.tatarka.inject.annotations.Inject 6 | 7 | @Inject 8 | internal class PermissionsViewModel( 9 | private val collectors: Factories.Collector 10 | ) : BaseChildViewModel() { 11 | 12 | override fun data() = 13 | launch { 14 | val result = io { 15 | collectors.permissions()() 16 | } 17 | setState( 18 | PermissionsState.Data( 19 | value = result 20 | ) 21 | ) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/main/preferences/PreferencesEvent.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.main.preferences 2 | 3 | internal sealed class PreferencesEvent { 4 | 5 | class Cached : PreferencesEvent() 6 | } 7 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/main/preferences/PreferencesState.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.main.preferences 2 | 3 | import com.infinum.sentinel.data.models.raw.PreferencesData 4 | 5 | internal sealed class PreferencesState { 6 | 7 | data class Data( 8 | val value: List 9 | ) : PreferencesState() 10 | } 11 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/main/preferences/all/AllPreferencesActivity.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.main.preferences.all 2 | 3 | import android.os.Bundle 4 | import androidx.annotation.RestrictTo 5 | import androidx.core.view.WindowCompat 6 | import com.infinum.sentinel.ui.main.preferences.PreferencesFragment 7 | import com.infinum.sentinel.ui.shared.base.BaseChildActivity 8 | 9 | @RestrictTo(RestrictTo.Scope.LIBRARY) 10 | internal class AllPreferencesActivity : BaseChildActivity() { 11 | 12 | override fun onCreate(savedInstanceState: Bundle?) { 13 | super.onCreate(savedInstanceState) 14 | 15 | WindowCompat.setDecorFitsSystemWindows(window, false) 16 | 17 | supportFragmentManager.beginTransaction() 18 | .replace( 19 | android.R.id.content, 20 | PreferencesFragment.newInstance(PreferencesFragment.ALL_PREFERENCES), 21 | PreferencesFragment.TAG 22 | ) 23 | .commit() 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/main/preferences/editor/PreferenceEditorActivity.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.main.preferences.editor 2 | 3 | import android.os.Bundle 4 | import androidx.annotation.RestrictTo 5 | import com.infinum.sentinel.ui.shared.base.BaseChildActivity 6 | 7 | @RestrictTo(RestrictTo.Scope.LIBRARY) 8 | internal class PreferenceEditorActivity : BaseChildActivity() { 9 | 10 | override fun onCreate(savedInstanceState: Bundle?) { 11 | super.onCreate(savedInstanceState) 12 | 13 | supportFragmentManager.beginTransaction() 14 | .replace( 15 | android.R.id.content, 16 | PreferenceEditorFragment.newInstance(), 17 | PreferenceEditorFragment.TAG 18 | ) 19 | .commit() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/main/preferences/editor/PreferenceEditorContract.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.main.preferences.editor 2 | 3 | import android.app.Activity 4 | import android.content.Context 5 | import android.content.Intent 6 | import androidx.activity.result.contract.ActivityResultContract 7 | import com.infinum.sentinel.ui.shared.Constants 8 | 9 | internal class PreferenceEditorContract : ActivityResultContract() { 10 | 11 | override fun createIntent(context: Context, input: Unit): Intent = 12 | Intent(context, PreferenceEditorActivity::class.java) 13 | 14 | override fun parseResult(resultCode: Int, intent: Intent?): Boolean = 15 | if (resultCode == Activity.RESULT_OK) { 16 | intent?.getBooleanExtra( 17 | Constants.Keys.SHOULD_REFRESH, 18 | false 19 | ) ?: false 20 | } else { 21 | false 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/main/preferences/editor/PreferenceEditorEvent.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.main.preferences.editor 2 | 3 | internal sealed class PreferenceEditorEvent { 4 | 5 | class Saved : PreferenceEditorEvent() 6 | } 7 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/main/preferences/editor/PreferenceEditorState.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.main.preferences.editor 2 | 3 | import com.infinum.sentinel.data.models.raw.PreferenceType 4 | 5 | internal sealed class PreferenceEditorState { 6 | 7 | data class Cache( 8 | val name: String, 9 | val type: PreferenceType, 10 | val key: String, 11 | val value: Any 12 | ) : PreferenceEditorState() 13 | } 14 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/main/preferences/shared/adapter/PreferenceChildViewHolder.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.main.preferences.shared.adapter 2 | 3 | import androidx.recyclerview.widget.RecyclerView 4 | import com.infinum.sentinel.data.models.raw.PreferenceType 5 | import com.infinum.sentinel.databinding.SentinelViewItemTextBinding 6 | import com.infinum.sentinel.extensions.copyToClipboard 7 | import com.infinum.sentinel.ui.main.preferences.shared.model.PreferencesItem 8 | 9 | internal class PreferenceChildViewHolder(private val binding: SentinelViewItemTextBinding) : 10 | RecyclerView.ViewHolder(binding.root) { 11 | 12 | fun bind( 13 | data: PreferencesItem.Child, 14 | onPreferenceClicked: ((String, Triple) -> Unit)?, 15 | ) { 16 | binding.labelView.text = data.label 17 | binding.valueView.text = data.value.toString() 18 | 19 | binding.root.setOnClickListener { 20 | onPreferenceClicked?.invoke(data.parentName, Triple(data.preferenceType, data.label, data.value)) 21 | } 22 | 23 | binding.root.setOnLongClickListener { 24 | it.context.copyToClipboard(key = data.label, value = data.value.toString()) 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/main/preferences/shared/adapter/PreferenceParentViewHolder.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.main.preferences.shared.adapter 2 | 3 | import androidx.recyclerview.widget.RecyclerView 4 | import com.infinum.sentinel.R 5 | import com.infinum.sentinel.databinding.SentinelViewItemPreferenceBinding 6 | import com.infinum.sentinel.ui.main.preferences.shared.model.PreferencesItem 7 | 8 | internal class PreferenceParentViewHolder(private val binding: SentinelViewItemPreferenceBinding) : 9 | RecyclerView.ViewHolder(binding.root) { 10 | 11 | fun bind( 12 | data: PreferencesItem.Parent, 13 | onSortClicked: (String) -> Unit, 14 | onHideExpandClicked: (String) -> Unit, 15 | ) { 16 | binding.nameView.text = data.name 17 | binding.sortImageView.setOnClickListener { onSortClicked(data.name) } 18 | binding.hideExpandImageView.setOnClickListener { onHideExpandClicked(data.name) } 19 | 20 | binding.hideExpandImageView.setImageResource( 21 | if (data.isExpanded) R.drawable.sentinel_ic_minus else R.drawable.sentinel_ic_plus 22 | ) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/main/preferences/shared/model/PreferencesItem.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.main.preferences.shared.model 2 | 3 | import com.infinum.sentinel.data.models.raw.PreferenceType 4 | import com.infinum.sentinel.data.models.raw.PreferencesData 5 | 6 | internal sealed class PreferencesItem { 7 | 8 | data class Parent( 9 | val name: String, 10 | var isExpanded: Boolean 11 | ) : PreferencesItem() 12 | 13 | data class Child( 14 | val preferenceType: PreferenceType, 15 | val label: String, 16 | val value: Any, 17 | val parentName: String 18 | ) : PreferencesItem() 19 | } 20 | 21 | internal fun List.flatten(): List = flatMap { data -> 22 | listOf(PreferencesItem.Parent(name = data.name, isExpanded = data.isExpanded)) + 23 | if (data.isExpanded) { 24 | data.values.map { (preferenceType, label, value) -> 25 | PreferencesItem.Child(preferenceType, label, value, parentName = data.name) 26 | } 27 | } else { 28 | emptyList() 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/main/tools/ToolsState.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.main.tools 2 | 3 | import com.infinum.sentinel.Sentinel 4 | 5 | internal sealed class ToolsState { 6 | 7 | data class Data( 8 | val value: Set 9 | ) : ToolsState() 10 | } 11 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/main/tools/ToolsViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.main.tools 2 | 3 | import com.infinum.sentinel.domain.Factories 4 | import com.infinum.sentinel.ui.shared.base.BaseChildViewModel 5 | import me.tatarka.inject.annotations.Inject 6 | 7 | @Inject 8 | internal class ToolsViewModel( 9 | private val collectors: Factories.Collector 10 | ) : BaseChildViewModel() { 11 | 12 | override fun data() = 13 | launch { 14 | val result = io { 15 | collectors.tools()() 16 | } 17 | setState( 18 | ToolsState.Data( 19 | value = result 20 | ) 21 | ) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/settings/SettingsActivity.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.settings 2 | 3 | import android.os.Bundle 4 | import androidx.annotation.RestrictTo 5 | import com.infinum.sentinel.ui.shared.base.BaseChildActivity 6 | 7 | @RestrictTo(RestrictTo.Scope.LIBRARY) 8 | internal class SettingsActivity : BaseChildActivity() { 9 | 10 | override fun onCreate(savedInstanceState: Bundle?) { 11 | super.onCreate(savedInstanceState) 12 | 13 | supportFragmentManager.beginTransaction() 14 | .replace(android.R.id.content, SettingsFragment.newInstance(), SettingsFragment.TAG) 15 | .commit() 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/settings/SettingsEvent.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.settings 2 | 3 | import com.infinum.sentinel.data.models.local.BundleMonitorEntity 4 | import com.infinum.sentinel.data.models.local.CertificateMonitorEntity 5 | import com.infinum.sentinel.data.models.local.CrashMonitorEntity 6 | import com.infinum.sentinel.data.models.local.FormatEntity 7 | import com.infinum.sentinel.data.models.local.TriggerEntity 8 | 9 | internal sealed class SettingsEvent { 10 | 11 | data class TriggersChanged( 12 | val value: List 13 | ) : SettingsEvent() 14 | 15 | data class FormatChanged( 16 | val value: FormatEntity 17 | ) : SettingsEvent() 18 | 19 | data class BundleMonitorChanged( 20 | val value: BundleMonitorEntity 21 | ) : SettingsEvent() 22 | 23 | data class CrashMonitorChanged( 24 | val value: CrashMonitorEntity 25 | ) : SettingsEvent() 26 | 27 | data class CertificateMonitorChanged( 28 | val value: CertificateMonitorEntity 29 | ) : SettingsEvent() 30 | 31 | object PermissionsCheck : SettingsEvent() 32 | } 33 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/shared/Constants.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.shared 2 | 3 | internal object Constants { 4 | const val BYTE_MULTIPLIER = 1000 5 | const val SHARE_MIME_TYPE = "text/plain" 6 | const val NOTIFICATIONS_CHANNEL_ID = "sentinel" 7 | 8 | object Keys { 9 | const val BUNDLE_ID = "KEY_BUNDLE_ID" 10 | const val SHOULD_REFRESH: String = "KEY_SHOULD_REFRESH" 11 | const val APPLICATION_NAME: String = "KEY_APPLICATION_NAME" 12 | const val CRASH_ID: String = "KEY_CRASH_ID" 13 | const val NOTIFY_INVALID_NOW: String = "KEY_NOTIFY_INVALID_NOW" 14 | const val NOTIFY_TO_EXPIRE: String = "KEY_NOTIFY_TO_EXPIRE" 15 | const val EXPIRE_IN_AMOUNT: String = "KEY_EXPIRE_IN_AMOUNT" 16 | const val EXPIRE_IN_UNIT: String = "KEY_EXPIRE_IN_UNIT" 17 | const val WORKER_CLASS_NAME = "WORKER_CLASS_NAME" 18 | const val WORKER_ID = "WORKER_ID" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/shared/base/BaseChildActivity.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.shared.base 2 | 3 | import androidx.annotation.RestrictTo 4 | 5 | @RestrictTo(RestrictTo.Scope.LIBRARY) 6 | internal abstract class BaseChildActivity : BaseActivity() { 7 | 8 | override val viewModel: BaseViewModel? = null 9 | 10 | override fun onState(state: Nothing) = Unit 11 | 12 | override fun onEvent(event: Nothing) = Unit 13 | } 14 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/shared/base/BaseChildFragment.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.shared.base 2 | 3 | import android.os.Bundle 4 | import android.view.View 5 | import androidx.annotation.CallSuper 6 | import androidx.annotation.LayoutRes 7 | import androidx.annotation.RestrictTo 8 | import androidx.fragment.app.Fragment 9 | import androidx.viewbinding.ViewBinding 10 | 11 | @RestrictTo(RestrictTo.Scope.LIBRARY) 12 | internal abstract class BaseChildFragment( 13 | @LayoutRes contentLayoutId: Int 14 | ) : Fragment(contentLayoutId), BaseView { 15 | 16 | abstract val binding: ViewBinding 17 | 18 | abstract override val viewModel: BaseChildViewModel 19 | 20 | @CallSuper 21 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 22 | collectFlows(viewLifecycleOwner) 23 | 24 | viewModel.data() 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/shared/base/BaseChildViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.shared.base 2 | 3 | internal abstract class BaseChildViewModel : BaseViewModel() { 4 | 5 | abstract fun data() 6 | } 7 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/shared/edgefactories/bounce/BounceEdgeEffectFactory.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.shared.edgefactories.bounce 2 | 3 | import android.widget.EdgeEffect 4 | import androidx.recyclerview.widget.RecyclerView 5 | 6 | internal class BounceEdgeEffectFactory : RecyclerView.EdgeEffectFactory() { 7 | 8 | override fun createEdgeEffect(recyclerView: RecyclerView, direction: Int): EdgeEffect = 9 | BounceEdgeEffect(recyclerView, direction) 10 | } 11 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/shared/notification/IntentFactory.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.shared.notification 2 | 3 | import android.content.Intent 4 | 5 | internal interface IntentFactory { 6 | 7 | fun crash(applicationName: String, id: Long): Array 8 | 9 | fun certificate(applicationName: String): Array 10 | } 11 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/shared/notification/NotificationFactory.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.shared.notification 2 | 3 | import com.infinum.sentinel.data.models.local.CrashEntity 4 | 5 | internal interface NotificationFactory { 6 | 7 | fun showCrash(applicationName: String, id: Long, entity: CrashEntity) 8 | 9 | fun showAnr(applicationName: String, id: Long, entity: CrashEntity) 10 | 11 | fun showExpiredCertificate(applicationName: String, count: Int) 12 | 13 | fun showToExpireCertificate(applicationName: String, count: Int) 14 | } 15 | -------------------------------------------------------------------------------- /sentinel/src/main/kotlin/com/infinum/sentinel/ui/shared/search/SimpleQueryTextListener.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.shared.search 2 | 3 | import androidx.appcompat.widget.SearchView 4 | 5 | internal class SimpleQueryTextListener( 6 | private val onQueryTextChanged: (String?) -> Unit 7 | ) : SearchView.OnQueryTextListener { 8 | 9 | override fun onQueryTextSubmit(query: String?): Boolean { 10 | onQueryTextChanged(query) 11 | return true 12 | } 13 | 14 | override fun onQueryTextChange(newText: String?): Boolean { 15 | onQueryTextChanged(newText) 16 | return true 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /sentinel/src/main/res/color/sentinel_selector_bottomnavigation_icon_tint.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /sentinel/src/main/res/color/sentinel_selector_bottomnavigation_text_color.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /sentinel/src/main/res/drawable/sentinel_ic_activity_intent_extras.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /sentinel/src/main/res/drawable/sentinel_ic_activity_saved_state.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /sentinel/src/main/res/drawable/sentinel_ic_anr.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /sentinel/src/main/res/drawable/sentinel_ic_application.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /sentinel/src/main/res/drawable/sentinel_ic_back.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | -------------------------------------------------------------------------------- /sentinel/src/main/res/drawable/sentinel_ic_bundle_monitor_empty.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /sentinel/src/main/res/drawable/sentinel_ic_certificates_empty.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /sentinel/src/main/res/drawable/sentinel_ic_checked_filter.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | -------------------------------------------------------------------------------- /sentinel/src/main/res/drawable/sentinel_ic_clear.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | -------------------------------------------------------------------------------- /sentinel/src/main/res/drawable/sentinel_ic_clear_search.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | -------------------------------------------------------------------------------- /sentinel/src/main/res/drawable/sentinel_ic_close.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | -------------------------------------------------------------------------------- /sentinel/src/main/res/drawable/sentinel_ic_crashes_empty.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /sentinel/src/main/res/drawable/sentinel_ic_device.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /sentinel/src/main/res/drawable/sentinel_ic_logger_empty.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /sentinel/src/main/res/drawable/sentinel_ic_minus.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /sentinel/src/main/res/drawable/sentinel_ic_notification_anr.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /sentinel/src/main/res/drawable/sentinel_ic_notification_certificate_invalid.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /sentinel/src/main/res/drawable/sentinel_ic_permissions.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /sentinel/src/main/res/drawable/sentinel_ic_plus.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /sentinel/src/main/res/drawable/sentinel_ic_preferences.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | -------------------------------------------------------------------------------- /sentinel/src/main/res/drawable/sentinel_ic_save.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | -------------------------------------------------------------------------------- /sentinel/src/main/res/drawable/sentinel_ic_share.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /sentinel/src/main/res/drawable/sentinel_ic_sort.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /sentinel/src/main/res/drawable/sentinel_ic_tools.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /sentinel/src/main/res/layout/sentinel_fragment_permissions.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 18 | 19 | -------------------------------------------------------------------------------- /sentinel/src/main/res/layout/sentinel_fragment_tools.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 18 | 19 | -------------------------------------------------------------------------------- /sentinel/src/main/res/layout/sentinel_view_item_button.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 13 | 14 | -------------------------------------------------------------------------------- /sentinel/src/main/res/layout/sentinel_view_item_thread_state.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 15 | 16 | 19 | 20 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /sentinel/src/main/res/menu/sentinel_bottom_navigation.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 13 | 18 | 19 | 24 | 25 | 30 | 31 | 36 | 37 | -------------------------------------------------------------------------------- /sentinel/src/main/res/menu/sentinel_bundle_monitor_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 13 | 18 | 19 | -------------------------------------------------------------------------------- /sentinel/src/main/res/menu/sentinel_crash_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 13 | 18 | 19 | -------------------------------------------------------------------------------- /sentinel/src/main/res/menu/sentinel_crash_monitor_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 13 | 18 | 19 | -------------------------------------------------------------------------------- /sentinel/src/main/res/menu/sentinel_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /sentinel/src/main/res/menu/sentinel_preference_editor.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /sentinel/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /sentinel/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12dp 4 | 64dp 5 | 32dp 6 | 40dp 7 | 56dp 8 | 4dp 9 | 8dp 10 | 16dp 11 | 0dp 12 | 20dp 13 | 14 | 40dp 15 | 20dp 16 | 6dp 17 | 20dp 18 | 10dp 19 | 20 | 104dp 21 | 4dp 22 | 23 | 13dp 24 | -------------------------------------------------------------------------------- /sentinel/src/main/res/values/integers.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 4 | -------------------------------------------------------------------------------- /sentinel/src/main/res/values/metadata.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | infinum.sentinel.monitored 4 | -------------------------------------------------------------------------------- /sentinel/src/main/res/values/public.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'org.gradle.toolchains.foojay-resolver-convention' version '0.4.0' 3 | } 4 | 5 | rootProject.name="Sentinel" 6 | 7 | include ":sample" 8 | include ":sentinel" 9 | include ":sentinel-no-op" 10 | include ":tool-chucker" 11 | include ":tool-collar" 12 | include ":tool-dbinspector" 13 | include ":tool-leakcanary" 14 | include ":tool-googleplay" 15 | include ":tool-appgallery" 16 | include ":tool-thimble" 17 | include ":tool-timber" 18 | 19 | gradle.startParameter.excludedTaskNames.add(":buildSrc:testClasses") 20 | -------------------------------------------------------------------------------- /tasks.gradle: -------------------------------------------------------------------------------- 1 | apply from: '../config.gradle' 2 | 3 | private void replaceVersionsInFile(File file) { 4 | def content = file.text 5 | content = content.replaceAll(~/sentinelVersion\s*=\s*".*"/, "sentinelVersion = \"${releaseConfig.version}\"") 6 | file.setText(content) 7 | } 8 | 9 | tasks.register('generateReadme') { 10 | doFirst { 11 | replaceVersionsInFile(new File("${rootDir}/README.md")) 12 | } 13 | } -------------------------------------------------------------------------------- /tool-appgallery/api/tool-appgallery.api: -------------------------------------------------------------------------------- 1 | public final class com/infinum/sentinel/ui/tools/AppGalleryTool : com/infinum/sentinel/Sentinel$Tool { 2 | public fun (Ljava/lang/String;)V 3 | public fun (Ljava/lang/String;Landroid/view/View$OnClickListener;)V 4 | public synthetic fun (Ljava/lang/String;Landroid/view/View$OnClickListener;ILkotlin/jvm/internal/DefaultConstructorMarker;)V 5 | public final fun component1 ()Ljava/lang/String; 6 | public final fun copy (Ljava/lang/String;Landroid/view/View$OnClickListener;)Lcom/infinum/sentinel/ui/tools/AppGalleryTool; 7 | public static synthetic fun copy$default (Lcom/infinum/sentinel/ui/tools/AppGalleryTool;Ljava/lang/String;Landroid/view/View$OnClickListener;ILjava/lang/Object;)Lcom/infinum/sentinel/ui/tools/AppGalleryTool; 8 | public fun equals (Ljava/lang/Object;)Z 9 | public final fun getAppId ()Ljava/lang/String; 10 | public fun hashCode ()I 11 | public fun icon ()Ljava/lang/Integer; 12 | public fun listener ()Landroid/view/View$OnClickListener; 13 | public fun name ()I 14 | public fun toString ()Ljava/lang/String; 15 | } 16 | 17 | -------------------------------------------------------------------------------- /tool-appgallery/proguard-rules.txt: -------------------------------------------------------------------------------- 1 | -keeppackagenames 2 | -keep public class com.infinum.sentinel.ui.tools.AppGalleryTool { 3 | public protected *; 4 | } 5 | -keep public class com.infinum.sentinel.databinding.** 6 | -------------------------------------------------------------------------------- /tool-appgallery/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tool-appgallery/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Huawei AppGallery 4 | -------------------------------------------------------------------------------- /tool-chucker/api/tool-chucker.api: -------------------------------------------------------------------------------- 1 | public final class com/infinum/sentinel/ui/tools/ChuckerTool : com/infinum/sentinel/Sentinel$Tool { 2 | public fun ()V 3 | public fun (Landroid/view/View$OnClickListener;)V 4 | public synthetic fun (Landroid/view/View$OnClickListener;ILkotlin/jvm/internal/DefaultConstructorMarker;)V 5 | public final fun copy (Landroid/view/View$OnClickListener;)Lcom/infinum/sentinel/ui/tools/ChuckerTool; 6 | public static synthetic fun copy$default (Lcom/infinum/sentinel/ui/tools/ChuckerTool;Landroid/view/View$OnClickListener;ILjava/lang/Object;)Lcom/infinum/sentinel/ui/tools/ChuckerTool; 7 | public fun equals (Ljava/lang/Object;)Z 8 | public fun hashCode ()I 9 | public fun icon ()Ljava/lang/Integer; 10 | public fun listener ()Landroid/view/View$OnClickListener; 11 | public fun name ()I 12 | public fun toString ()Ljava/lang/String; 13 | } 14 | 15 | -------------------------------------------------------------------------------- /tool-chucker/proguard-rules.txt: -------------------------------------------------------------------------------- 1 | -keeppackagenames 2 | -keep public class com.infinum.sentinel.ui.tools.ChuckerTool { 3 | public protected *; 4 | } 5 | -keep public class com.infinum.sentinel.databinding.** 6 | -------------------------------------------------------------------------------- /tool-chucker/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tool-chucker/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Network 4 | -------------------------------------------------------------------------------- /tool-collar/api/tool-collar.api: -------------------------------------------------------------------------------- 1 | public final class com/infinum/sentinel/ui/tools/CollarTool : com/infinum/sentinel/Sentinel$Tool { 2 | public fun ()V 3 | public fun (Landroid/view/View$OnClickListener;)V 4 | public synthetic fun (Landroid/view/View$OnClickListener;ILkotlin/jvm/internal/DefaultConstructorMarker;)V 5 | public final fun copy (Landroid/view/View$OnClickListener;)Lcom/infinum/sentinel/ui/tools/CollarTool; 6 | public static synthetic fun copy$default (Lcom/infinum/sentinel/ui/tools/CollarTool;Landroid/view/View$OnClickListener;ILjava/lang/Object;)Lcom/infinum/sentinel/ui/tools/CollarTool; 7 | public fun equals (Ljava/lang/Object;)Z 8 | public fun hashCode ()I 9 | public fun icon ()Ljava/lang/Integer; 10 | public fun listener ()Landroid/view/View$OnClickListener; 11 | public fun name ()I 12 | public fun toString ()Ljava/lang/String; 13 | } 14 | 15 | -------------------------------------------------------------------------------- /tool-collar/proguard-rules.txt: -------------------------------------------------------------------------------- 1 | -keeppackagenames 2 | -keep public class com.infinum.sentinel.ui.tools.CollarTool { 3 | public protected *; 4 | } 5 | -keep public class com.infinum.sentinel.databinding.** 6 | -------------------------------------------------------------------------------- /tool-collar/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tool-collar/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Analytics 4 | -------------------------------------------------------------------------------- /tool-dbinspector/api/tool-dbinspector.api: -------------------------------------------------------------------------------- 1 | public final class com/infinum/sentinel/ui/tools/DbInspectorTool : com/infinum/sentinel/Sentinel$Tool { 2 | public fun ()V 3 | public fun (Landroid/view/View$OnClickListener;)V 4 | public synthetic fun (Landroid/view/View$OnClickListener;ILkotlin/jvm/internal/DefaultConstructorMarker;)V 5 | public final fun copy (Landroid/view/View$OnClickListener;)Lcom/infinum/sentinel/ui/tools/DbInspectorTool; 6 | public static synthetic fun copy$default (Lcom/infinum/sentinel/ui/tools/DbInspectorTool;Landroid/view/View$OnClickListener;ILjava/lang/Object;)Lcom/infinum/sentinel/ui/tools/DbInspectorTool; 7 | public fun equals (Ljava/lang/Object;)Z 8 | public fun hashCode ()I 9 | public fun icon ()Ljava/lang/Integer; 10 | public fun listener ()Landroid/view/View$OnClickListener; 11 | public fun name ()I 12 | public fun toString ()Ljava/lang/String; 13 | } 14 | 15 | -------------------------------------------------------------------------------- /tool-dbinspector/proguard-rules.txt: -------------------------------------------------------------------------------- 1 | -keeppackagenames 2 | -keep public class com.infinum.sentinel.ui.tools.DbInspectorTool { 3 | public protected *; 4 | } 5 | -keep public class com.infinum.sentinel.databinding.** -------------------------------------------------------------------------------- /tool-dbinspector/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tool-dbinspector/src/main/kotlin/com/infinum/sentinel/ui/tools/DbInspectorTool.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.tools 2 | 3 | import android.view.View 4 | import com.infinum.dbinspector.DbInspector 5 | import com.infinum.sentinel.R 6 | import com.infinum.sentinel.Sentinel 7 | 8 | /** 9 | * Specific wrapper tool around DbInspector. 10 | * 11 | * Tool Activity will launch with no additional flags. 12 | */ 13 | public data class DbInspectorTool( 14 | private val listener: View.OnClickListener = View.OnClickListener { 15 | DbInspector.show() 16 | } 17 | ) : Sentinel.Tool { 18 | 19 | /** 20 | * An optional icon for this tool 21 | * 22 | * @return a Drawable resource that will be used to generate an icon in a Button in Tools UI 23 | */ 24 | override fun icon(): Int? = null 25 | 26 | /** 27 | * A dedicated name for this tool 28 | * 29 | * @return a String resource that will be used to generate a name for a Button in Tools UI 30 | */ 31 | override fun name(): Int = R.string.sentinel_database 32 | 33 | /** 34 | * A callback to be invoked when this view is clicked. 35 | * 36 | * @return an assigned OnClickListener that will be used to generate a Button in Tools UI 37 | */ 38 | override fun listener(): View.OnClickListener = listener 39 | } 40 | -------------------------------------------------------------------------------- /tool-dbinspector/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Database 4 | -------------------------------------------------------------------------------- /tool-googleplay/api/tool-googleplay.api: -------------------------------------------------------------------------------- 1 | public final class com/infinum/sentinel/ui/tools/GooglePlayTool : com/infinum/sentinel/Sentinel$Tool { 2 | public fun ()V 3 | public fun (Landroid/view/View$OnClickListener;)V 4 | public synthetic fun (Landroid/view/View$OnClickListener;ILkotlin/jvm/internal/DefaultConstructorMarker;)V 5 | public final fun copy (Landroid/view/View$OnClickListener;)Lcom/infinum/sentinel/ui/tools/GooglePlayTool; 6 | public static synthetic fun copy$default (Lcom/infinum/sentinel/ui/tools/GooglePlayTool;Landroid/view/View$OnClickListener;ILjava/lang/Object;)Lcom/infinum/sentinel/ui/tools/GooglePlayTool; 7 | public fun equals (Ljava/lang/Object;)Z 8 | public fun hashCode ()I 9 | public fun icon ()Ljava/lang/Integer; 10 | public fun listener ()Landroid/view/View$OnClickListener; 11 | public fun name ()I 12 | public fun toString ()Ljava/lang/String; 13 | } 14 | 15 | -------------------------------------------------------------------------------- /tool-googleplay/proguard-rules.txt: -------------------------------------------------------------------------------- 1 | -keeppackagenames 2 | -keep public class com.infinum.sentinel.ui.tools.GooglePlayTool { 3 | public protected *; 4 | } 5 | -keep public class com.infinum.sentinel.databinding.** 6 | -------------------------------------------------------------------------------- /tool-googleplay/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tool-googleplay/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Google Play 4 | -------------------------------------------------------------------------------- /tool-leakcanary/api/tool-leakcanary.api: -------------------------------------------------------------------------------- 1 | public final class com/infinum/sentinel/ui/tools/LeakCanaryTool : com/infinum/sentinel/Sentinel$Tool { 2 | public fun ()V 3 | public fun (Landroid/view/View$OnClickListener;)V 4 | public synthetic fun (Landroid/view/View$OnClickListener;ILkotlin/jvm/internal/DefaultConstructorMarker;)V 5 | public final fun copy (Landroid/view/View$OnClickListener;)Lcom/infinum/sentinel/ui/tools/LeakCanaryTool; 6 | public static synthetic fun copy$default (Lcom/infinum/sentinel/ui/tools/LeakCanaryTool;Landroid/view/View$OnClickListener;ILjava/lang/Object;)Lcom/infinum/sentinel/ui/tools/LeakCanaryTool; 7 | public fun equals (Ljava/lang/Object;)Z 8 | public fun hashCode ()I 9 | public fun icon ()Ljava/lang/Integer; 10 | public fun listener ()Landroid/view/View$OnClickListener; 11 | public fun name ()I 12 | public fun toString ()Ljava/lang/String; 13 | } 14 | 15 | -------------------------------------------------------------------------------- /tool-leakcanary/proguard-rules.txt: -------------------------------------------------------------------------------- 1 | -keeppackagenames 2 | -keep public class com.infinum.sentinel.ui.tools.LeakCanaryTool { 3 | public protected *; 4 | } 5 | -keep public class com.infinum.sentinel.databinding.** 6 | -------------------------------------------------------------------------------- /tool-leakcanary/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tool-leakcanary/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Memory 4 | -------------------------------------------------------------------------------- /tool-thimble/api/tool-thimble.api: -------------------------------------------------------------------------------- 1 | public final class com/infinum/sentinel/ui/tools/ThimbleTool : com/infinum/sentinel/Sentinel$Tool { 2 | public fun ()V 3 | public fun (Landroid/view/View$OnClickListener;)V 4 | public synthetic fun (Landroid/view/View$OnClickListener;ILkotlin/jvm/internal/DefaultConstructorMarker;)V 5 | public final fun copy (Landroid/view/View$OnClickListener;)Lcom/infinum/sentinel/ui/tools/ThimbleTool; 6 | public static synthetic fun copy$default (Lcom/infinum/sentinel/ui/tools/ThimbleTool;Landroid/view/View$OnClickListener;ILjava/lang/Object;)Lcom/infinum/sentinel/ui/tools/ThimbleTool; 7 | public fun equals (Ljava/lang/Object;)Z 8 | public fun hashCode ()I 9 | public fun icon ()Ljava/lang/Integer; 10 | public fun listener ()Landroid/view/View$OnClickListener; 11 | public fun name ()I 12 | public fun toString ()Ljava/lang/String; 13 | } 14 | 15 | -------------------------------------------------------------------------------- /tool-thimble/proguard-rules.txt: -------------------------------------------------------------------------------- 1 | -keeppackagenames 2 | -keep public class com.infinum.sentinel.ui.tools.ThimbleTool { 3 | public protected *; 4 | } 5 | -keep public class com.infinum.sentinel.databinding.** 6 | -------------------------------------------------------------------------------- /tool-thimble/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tool-thimble/src/main/kotlin/com/infinum/sentinel/ui/tools/ThimbleTool.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.tools 2 | 3 | import android.view.View 4 | import com.infinum.sentinel.R 5 | import com.infinum.sentinel.Sentinel 6 | import com.infinum.thimble.Thimble 7 | 8 | /** 9 | * Specific wrapper tool around Thimble. 10 | * 11 | * Tool Activity will launch with no additional flags. 12 | */ 13 | public data class ThimbleTool( 14 | private val listener: View.OnClickListener = View.OnClickListener { 15 | Thimble.show() 16 | } 17 | ) : Sentinel.Tool { 18 | 19 | /** 20 | * An optional icon for this tool 21 | * 22 | * @return a Drawable resource that will be used to generate an icon in a Button in Tools UI 23 | */ 24 | override fun icon(): Int? = null 25 | 26 | /** 27 | * A dedicated name for this tool 28 | * 29 | * @return a String resource that will be used to generate a name for a Button in Tools UI 30 | */ 31 | override fun name(): Int = R.string.sentinel_design 32 | 33 | /** 34 | * A callback to be invoked when this view is clicked. 35 | * 36 | * @return an assigned OnClickListener that will be used to generate a Button in Tools UI 37 | */ 38 | override fun listener(): View.OnClickListener = listener 39 | } 40 | -------------------------------------------------------------------------------- /tool-thimble/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Design 4 | -------------------------------------------------------------------------------- /tool-timber/proguard-rules.txt: -------------------------------------------------------------------------------- 1 | -keeppackagenames 2 | -keep public class com.infinum.sentinel.ui.tools.TimberTool { 3 | public protected *; 4 | } 5 | -keep public class com.infinum.sentinel.databinding.** 6 | -------------------------------------------------------------------------------- /tool-timber/src/main/kotlin/com/infinum/sentinel/SentinelLogsProvider.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel 2 | 3 | import androidx.core.content.FileProvider 4 | 5 | internal class SentinelLogsProvider : FileProvider(R.xml.sentinel_file_paths) 6 | -------------------------------------------------------------------------------- /tool-timber/src/main/kotlin/com/infinum/sentinel/TimberInitializer.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel 2 | 3 | import android.content.Context 4 | import androidx.startup.Initializer 5 | import com.infinum.sentinel.ui.logger.models.FlowBuffer 6 | import timber.log.Timber 7 | 8 | public class TimberInitializer : Initializer> { 9 | 10 | override fun create(context: Context): Class { 11 | 12 | Timber.plant( 13 | SentinelFileTree( 14 | context, 15 | FlowBuffer() 16 | ) 17 | ) 18 | 19 | return TimberInitializer::class.java 20 | } 21 | 22 | override fun dependencies(): List>> = 23 | listOf() 24 | } 25 | -------------------------------------------------------------------------------- /tool-timber/src/main/kotlin/com/infinum/sentinel/ui/logger/LoggerDiffUtil.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.logger 2 | 3 | import androidx.recyclerview.widget.DiffUtil 4 | import com.infinum.sentinel.SentinelFileTree 5 | 6 | internal class LoggerDiffUtil : DiffUtil.ItemCallback() { 7 | 8 | override fun areItemsTheSame( 9 | oldItem: SentinelFileTree.Entry, 10 | newItem: SentinelFileTree.Entry 11 | ): Boolean { 12 | return oldItem.timestamp == newItem.timestamp 13 | } 14 | 15 | override fun areContentsTheSame( 16 | oldItem: SentinelFileTree.Entry, 17 | newItem: SentinelFileTree.Entry 18 | ): Boolean { 19 | return oldItem == newItem 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tool-timber/src/main/kotlin/com/infinum/sentinel/ui/logger/models/BaseEntry.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.logger.models 2 | 3 | import java.text.SimpleDateFormat 4 | import java.util.Date 5 | import org.json.JSONObject 6 | 7 | internal open class BaseEntry( 8 | open val level: Level, 9 | open val timestamp: Long, 10 | open val tag: String? = null, 11 | open val message: String? = null, 12 | open val stackTrace: String? = null 13 | ) { 14 | fun asJSONString(): String = 15 | JSONObject() 16 | .put("level", level) 17 | .put("timestamp", timestamp) 18 | .put("tag", tag) 19 | .put("message", message) 20 | .put("stackTrace", stackTrace) 21 | .toString() 22 | 23 | fun asLineString(dateTimeFormat: SimpleDateFormat): String = 24 | buildString { 25 | append(dateTimeFormat.format(Date(timestamp))) 26 | append(" LEVEL: ") 27 | append(level) 28 | append(" TAG: ") 29 | append(tag.orEmpty()) 30 | append(" MESSAGE: ") 31 | append(message.orEmpty()) 32 | append(" STACKTRACE: ") 33 | append(stackTrace.orEmpty()) 34 | append(System.lineSeparator()) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tool-timber/src/main/kotlin/com/infinum/sentinel/ui/logger/models/FlowBuffer.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.logger.models 2 | 3 | import kotlinx.coroutines.flow.Flow 4 | import kotlinx.coroutines.flow.MutableStateFlow 5 | import kotlinx.coroutines.flow.asStateFlow 6 | 7 | internal class FlowBuffer { 8 | 9 | private var queue: List = listOf() 10 | private val flow: MutableStateFlow> = MutableStateFlow(queue.reversed()) 11 | 12 | suspend fun enqueue(item: T) { 13 | queue = queue.plus(item) 14 | flow.emit(queue.reversed()) 15 | } 16 | 17 | fun asFlow(): Flow> = flow.asStateFlow() 18 | 19 | suspend fun clear() { 20 | queue = emptyList() 21 | flow.emit(queue) 22 | } 23 | 24 | suspend fun filter(query: String?) { 25 | flow.emit( 26 | if (query.isNullOrBlank()) { 27 | queue.reversed() 28 | } else { 29 | queue.reversed().filter { 30 | it.tag?.lowercase()?.contains(query.lowercase()) == true || 31 | it.message?.lowercase()?.contains(query.lowercase()) == true || 32 | it.stackTrace?.lowercase()?.contains(query.lowercase()) == true 33 | } 34 | } 35 | ) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tool-timber/src/main/kotlin/com/infinum/sentinel/ui/logger/models/Level.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.logger.models 2 | 3 | import android.util.Log 4 | 5 | internal enum class Level { 6 | UNKNOWN, 7 | ASSERT, 8 | DEBUG, 9 | ERROR, 10 | INFO, 11 | VERBOSE, 12 | WARN; 13 | 14 | companion object { 15 | fun forLogLevel(logLevel: Int): Level = 16 | when (logLevel) { 17 | Log.ASSERT -> ASSERT 18 | Log.DEBUG -> DEBUG 19 | Log.ERROR -> ERROR 20 | Log.INFO -> INFO 21 | Log.VERBOSE -> VERBOSE 22 | Log.WARN -> WARN 23 | else -> UNKNOWN 24 | } 25 | 26 | fun byLevelName(levelName: String): Level = 27 | when (levelName) { 28 | "ASSERT" -> ASSERT 29 | "DEBUG" -> DEBUG 30 | "ERROR" -> ERROR 31 | "INFO" -> INFO 32 | "VERBOSE" -> VERBOSE 33 | "WARN" -> WARN 34 | else -> UNKNOWN 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tool-timber/src/main/kotlin/com/infinum/sentinel/ui/logger/storage/AllowedTags.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.logger.storage 2 | 3 | internal object AllowedTags { 4 | 5 | private var tags: List? = null 6 | 7 | val value: List 8 | get() = tags ?: emptyList() 9 | 10 | fun set(allowedTags: List) { 11 | tags = allowedTags 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /tool-timber/src/main/kotlin/com/infinum/sentinel/ui/logs/LogsDiffUtil.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.logs 2 | 3 | import androidx.recyclerview.widget.DiffUtil 4 | import java.io.File 5 | 6 | internal class LogsDiffUtil : DiffUtil.ItemCallback() { 7 | 8 | override fun areItemsTheSame( 9 | oldItem: File, 10 | newItem: File 11 | ): Boolean { 12 | return oldItem.name == newItem.name 13 | } 14 | 15 | override fun areContentsTheSame( 16 | oldItem: File, 17 | newItem: File 18 | ): Boolean { 19 | return oldItem == newItem 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tool-timber/src/main/kotlin/com/infinum/sentinel/ui/logs/LogsViewHolder.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.logs 2 | 3 | import androidx.recyclerview.widget.RecyclerView 4 | import com.infinum.sentinel.databinding.SentinelItemLogFileBinding 5 | import java.io.File 6 | import java.text.SimpleDateFormat 7 | import java.util.Date 8 | 9 | internal class LogsViewHolder( 10 | private val binding: SentinelItemLogFileBinding 11 | ) : RecyclerView.ViewHolder(binding.root) { 12 | 13 | fun bind(item: File?, dateTimeFormat: SimpleDateFormat, onDelete: (File) -> Unit, onShare: (File) -> Unit) { 14 | item?.let { entry -> 15 | with(binding) { 16 | messageView.text = entry.name 17 | timestampView.text = dateTimeFormat.format(Date(entry.lastModified())) 18 | deleteButton.setOnClickListener { onDelete(entry) } 19 | shareButton.setOnClickListener { onShare(entry) } 20 | } 21 | } ?: unbind() 22 | } 23 | 24 | fun unbind() = 25 | with(binding) { 26 | timestampView.text = null 27 | messageView.text = null 28 | deleteButton.setOnClickListener(null) 29 | shareButton.setOnClickListener(null) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tool-timber/src/main/kotlin/com/infinum/sentinel/ui/shared/BounceEdgeEffectFactory.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.shared 2 | 3 | import android.widget.EdgeEffect 4 | import androidx.recyclerview.widget.RecyclerView 5 | 6 | internal class BounceEdgeEffectFactory : RecyclerView.EdgeEffectFactory() { 7 | 8 | override fun createEdgeEffect(recyclerView: RecyclerView, direction: Int): EdgeEffect = 9 | BounceEdgeEffect(recyclerView, direction) 10 | } 11 | -------------------------------------------------------------------------------- /tool-timber/src/main/kotlin/com/infinum/sentinel/ui/shared/SearchViewKtx.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.shared 2 | 3 | import android.widget.ImageView 4 | import androidx.appcompat.widget.SearchView 5 | import com.infinum.sentinel.R 6 | 7 | internal fun SearchView.setup( 8 | hint: String?, 9 | onSearchClosed: () -> Unit, 10 | onQueryTextChanged: (String?) -> Unit 11 | ) { 12 | setIconifiedByDefault(true) 13 | isSubmitButtonEnabled = false 14 | isQueryRefinementEnabled = true 15 | maxWidth = Integer.MAX_VALUE 16 | queryHint = hint 17 | setOnCloseListener { 18 | onSearchClosed() 19 | false 20 | } 21 | setOnQueryTextListener( 22 | SimpleQueryTextListener(onQueryTextChanged) 23 | ) 24 | findViewById(androidx.appcompat.R.id.search_close_btn) 25 | .setImageResource(R.drawable.sentinel_ic_clear_search) 26 | } 27 | -------------------------------------------------------------------------------- /tool-timber/src/main/kotlin/com/infinum/sentinel/ui/shared/SimpleQueryTextListener.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.shared 2 | 3 | import androidx.appcompat.widget.SearchView 4 | 5 | internal class SimpleQueryTextListener( 6 | private val onQueryTextChanged: (String?) -> Unit 7 | ) : SearchView.OnQueryTextListener { 8 | 9 | override fun onQueryTextSubmit(query: String?): Boolean { 10 | onQueryTextChanged(query) 11 | return true 12 | } 13 | 14 | override fun onQueryTextChange(newText: String?): Boolean { 15 | onQueryTextChanged(newText) 16 | return true 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tool-timber/src/main/kotlin/com/infinum/sentinel/ui/shared/TimberToolConstants.kt: -------------------------------------------------------------------------------- 1 | package com.infinum.sentinel.ui.shared 2 | 3 | internal object TimberToolConstants { 4 | const val LOG_DATE_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZ" 5 | } 6 | -------------------------------------------------------------------------------- /tool-timber/src/main/res/drawable/sentinel_ic_clear_search.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | -------------------------------------------------------------------------------- /tool-timber/src/main/res/drawable/sentinel_ic_logs.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | -------------------------------------------------------------------------------- /tool-timber/src/main/res/menu/sentinel_logger_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 13 | 18 | 19 | 24 | 25 | -------------------------------------------------------------------------------- /tool-timber/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #9c27b0 4 | #2196f3 5 | #f44336 6 | #4caf50 7 | #9e9e9e 8 | #ffeb3b 9 | #00FFFFFF 10 | -------------------------------------------------------------------------------- /tool-timber/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Sentinel 4 | Search 5 | Logger 6 | Logs 7 | %1$s: %2$s 8 | -------------------------------------------------------------------------------- /tool-timber/src/main/res/xml/sentinel_file_paths.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /ui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infinum/android-sentinel/ed10229ac3bcf4fedc4117d3469861e687626628/ui.png --------------------------------------------------------------------------------