├── .github
└── PULL_REQUEST_TEMPLATE.md
├── .gitignore
├── .idea
├── codeStyles
│ ├── Project.xml
│ └── codeStyleConfig.xml
└── vcs.xml
├── Dangerfile
├── Gemfile
├── Gemfile.lock
├── LICENSE
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-instant-run.pro
├── proguard-rules-release-preprod.pro
├── proguard-rules-release.pro
├── proguard-rules-test.pro
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── kotlin
│ │ └── co
│ │ └── netguru
│ │ └── baby
│ │ └── monitor
│ │ └── client
│ │ └── database
│ │ └── DatabaseTests.kt
│ ├── debug
│ └── kotlin
│ │ └── co
│ │ └── netguru
│ │ └── baby
│ │ └── monitor
│ │ └── client
│ │ └── application
│ │ ├── DebugMetricsHelper.kt
│ │ └── RxJavaErrorHandlerImpl.kt
│ ├── debugTools
│ └── kotlin
│ │ └── co
│ │ └── netguru
│ │ └── baby
│ │ └── monitor
│ │ └── client
│ │ └── feature
│ │ └── debug
│ │ ├── DebugModule.kt
│ │ └── DebugNotificationManager.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── assets
│ │ ├── model_exp76.pb
│ │ └── tiny_conv_dataset.pb
│ ├── ic_launcher-web.png
│ ├── kotlin
│ │ └── co
│ │ │ └── netguru
│ │ │ └── baby
│ │ │ └── monitor
│ │ │ └── client
│ │ │ ├── application
│ │ │ ├── App.kt
│ │ │ ├── RxJavaErrorHandler.kt
│ │ │ ├── di
│ │ │ │ ├── ActivityBindingsModule.kt
│ │ │ │ ├── ApplicationComponent.kt
│ │ │ │ ├── ApplicationModule.kt
│ │ │ │ ├── BabyMonitorGlideModule.kt
│ │ │ │ ├── FirebaseModule.kt
│ │ │ │ ├── FragmentBindingsModule.kt
│ │ │ │ ├── NetworkModule.kt
│ │ │ │ ├── NotificationsModule.kt
│ │ │ │ ├── ServiceBindingsModule.kt
│ │ │ │ └── SharedPreferencesModule.kt
│ │ │ ├── firebase
│ │ │ │ ├── FirebaseRepository.kt
│ │ │ │ └── FirebaseSharedPreferencesWrapper.kt
│ │ │ └── scope
│ │ │ │ ├── ActivityScope.kt
│ │ │ │ └── FragmentScope.kt
│ │ │ ├── common
│ │ │ ├── DateProvider.kt
│ │ │ ├── LocalDateTimeProvider.kt
│ │ │ ├── NotificationHandler.kt
│ │ │ ├── Optional.kt
│ │ │ ├── PermissionUtils.kt
│ │ │ ├── Randomiser.kt
│ │ │ ├── RunsInBackground.kt
│ │ │ ├── SchedulersProvider.kt
│ │ │ ├── SingleLiveEvent.kt
│ │ │ ├── YesNoDialog.kt
│ │ │ ├── base
│ │ │ │ ├── AnalyticScreen.kt
│ │ │ │ ├── BaseFragment.kt
│ │ │ │ ├── BaseServiceWithFacade.kt
│ │ │ │ └── ViewModelFactory.kt
│ │ │ ├── extensions
│ │ │ │ ├── ActivityExtensions.kt
│ │ │ │ ├── ContextExtensions.kt
│ │ │ │ ├── DataExtensions.kt
│ │ │ │ ├── FragmentExtensions.kt
│ │ │ │ ├── LiveDataExtensions.kt
│ │ │ │ ├── NotificationExtensions.kt
│ │ │ │ ├── Optional.kt
│ │ │ │ ├── RxExtensions.kt
│ │ │ │ ├── SharedPreferencesExtensions.kt
│ │ │ │ └── ViewExtensions.kt
│ │ │ └── view
│ │ │ │ ├── BaseViewHolder.kt
│ │ │ │ ├── CustomSurfaceViewRenderer.kt
│ │ │ │ ├── PulsatingView.kt
│ │ │ │ ├── SeekBarProgress.kt
│ │ │ │ ├── StickyHeaderDecorator.kt
│ │ │ │ └── StickyHeaderInterface.kt
│ │ │ ├── data
│ │ │ ├── AppDatabase.kt
│ │ │ ├── Converters.kt
│ │ │ ├── DataRepository.kt
│ │ │ ├── client
│ │ │ │ ├── ChildDataDao.kt
│ │ │ │ ├── ChildDataEntity.kt
│ │ │ │ └── home
│ │ │ │ │ ├── ToolbarState.kt
│ │ │ │ │ └── log
│ │ │ │ │ ├── LogData.kt
│ │ │ │ │ ├── LogDataDao.kt
│ │ │ │ │ └── LogDataEntity.kt
│ │ │ ├── communication
│ │ │ │ ├── ClientDataDao.kt
│ │ │ │ ├── ClientEntity.kt
│ │ │ │ ├── SingleEvent.kt
│ │ │ │ ├── nsd
│ │ │ │ │ └── DiscoveryStatus.kt
│ │ │ │ └── websocket
│ │ │ │ │ └── ClientConnectionStatus.kt
│ │ │ ├── server
│ │ │ │ └── CameraState.kt
│ │ │ └── splash
│ │ │ │ └── AppStateHandler.kt
│ │ │ └── feature
│ │ │ ├── analytics
│ │ │ ├── AnalyticsManager.kt
│ │ │ ├── Event.kt
│ │ │ ├── Screen.kt
│ │ │ └── UserProperty.kt
│ │ │ ├── babynotification
│ │ │ ├── BabyEventActionIntentService.kt
│ │ │ ├── BabyMonitorMessagingService.kt
│ │ │ ├── NotifyBabyEventUseCase.kt
│ │ │ ├── OpenCameraUseCase.kt
│ │ │ └── SnoozeNotificationUseCase.kt
│ │ │ ├── batterylevel
│ │ │ ├── LowBatteryReceiver.kt
│ │ │ └── NotifyLowBatteryUseCase.kt
│ │ │ ├── client
│ │ │ ├── configuration
│ │ │ │ ├── AllDoneFragment.kt
│ │ │ │ └── ParentDeviceInfoFragment.kt
│ │ │ └── home
│ │ │ │ ├── BackButtonState.kt
│ │ │ │ ├── ClientHomeActivity.kt
│ │ │ │ ├── ClientHomeViewModel.kt
│ │ │ │ ├── RestartAppUseCase.kt
│ │ │ │ ├── SendBabyNameUseCase.kt
│ │ │ │ ├── SendFirebaseTokenUseCase.kt
│ │ │ │ ├── dashboard
│ │ │ │ └── ClientDashboardFragment.kt
│ │ │ │ ├── livecamera
│ │ │ │ ├── ClientLiveCameraFragment.kt
│ │ │ │ └── ClientLiveCameraFragmentViewModel.kt
│ │ │ │ └── log
│ │ │ │ ├── ActivityLogAdapter.kt
│ │ │ │ ├── ClientActivityLogFragment.kt
│ │ │ │ └── LogsViewHolder.kt
│ │ │ ├── communication
│ │ │ ├── ConfirmationUseCase.kt
│ │ │ ├── communication.kt
│ │ │ ├── internet
│ │ │ │ └── CheckInternetConnectionUseCase.kt
│ │ │ ├── nsd
│ │ │ │ ├── DeviceNameProvider.kt
│ │ │ │ ├── IDeviceNameProvider.kt
│ │ │ │ ├── NsdServiceDiffUtil.kt
│ │ │ │ ├── NsdServiceExceptions.kt
│ │ │ │ ├── NsdServiceManager.kt
│ │ │ │ ├── NsdServicesAdapter.kt
│ │ │ │ ├── NsdState.kt
│ │ │ │ └── ServiceViewHolder.kt
│ │ │ ├── pairing
│ │ │ │ ├── ConnectingDevicesFailedFragment.kt
│ │ │ │ ├── PairingFragment.kt
│ │ │ │ ├── PairingUseCase.kt
│ │ │ │ ├── PairingViewModel.kt
│ │ │ │ ├── ServiceDiscoveryFragment.kt
│ │ │ │ └── ServiceDiscoveryViewModel.kt
│ │ │ ├── webrtc
│ │ │ │ ├── PeerConnectionExtensions.kt
│ │ │ │ ├── StreamState.kt
│ │ │ │ ├── base
│ │ │ │ │ └── RtcMessageHandler.kt
│ │ │ │ ├── client
│ │ │ │ │ ├── RtcClient.kt
│ │ │ │ │ ├── RtcClientController.kt
│ │ │ │ │ └── RtcClientMessageController.kt
│ │ │ │ ├── observers
│ │ │ │ │ ├── ConnectionObserver.kt
│ │ │ │ │ ├── DefaultObserver.kt
│ │ │ │ │ └── DefaultSdpObserver.kt
│ │ │ │ └── server
│ │ │ │ │ ├── WebRtcManager.kt
│ │ │ │ │ └── WebRtcService.kt
│ │ │ └── websocket
│ │ │ │ ├── CustomWebSocketServer.kt
│ │ │ │ ├── Message.kt
│ │ │ │ ├── MessageController.kt
│ │ │ │ ├── MessageParser.kt
│ │ │ │ ├── RxWebSocketClient.kt
│ │ │ │ ├── WebSocketServerHandler.kt
│ │ │ │ └── WebSocketServerService.kt
│ │ │ ├── debug
│ │ │ ├── DebugState.kt
│ │ │ └── DebugView.kt
│ │ │ ├── firebasenotification
│ │ │ ├── FirebaseInstanceManager.kt
│ │ │ ├── FirebaseNotificationSender.kt
│ │ │ ├── Message.kt
│ │ │ └── NotificationType.kt
│ │ │ ├── machinelearning
│ │ │ └── MachineLearning.kt
│ │ │ ├── noisedetection
│ │ │ └── NoiseDetector.kt
│ │ │ ├── onboarding
│ │ │ ├── FeaturePresentationFragment.kt
│ │ │ ├── FinishOnboardingUseCase.kt
│ │ │ ├── InfoAboutDevicesFragment.kt
│ │ │ ├── OnboardingActivity.kt
│ │ │ ├── SpecifyDeviceFragment.kt
│ │ │ ├── VoiceRecordingsSettingsFragment.kt
│ │ │ └── baby
│ │ │ │ ├── ConnectWifiFragment.kt
│ │ │ │ ├── PermissionCameraFragment.kt
│ │ │ │ ├── PermissionDenied.kt
│ │ │ │ ├── PermissionMicrophoneFragment.kt
│ │ │ │ ├── SetupInformationFragment.kt
│ │ │ │ └── WifiReceiver.kt
│ │ │ ├── server
│ │ │ ├── ChildMonitorFragment.kt
│ │ │ ├── ChildMonitorFragmentModule.kt
│ │ │ ├── ChildMonitorViewModel.kt
│ │ │ ├── ReceiveFirebaseTokenUseCase.kt
│ │ │ ├── ServerActivity.kt
│ │ │ └── ServerViewModel.kt
│ │ │ ├── settings
│ │ │ ├── ChangeState.kt
│ │ │ ├── ClientSettingsFragment.kt
│ │ │ ├── ConfigurationViewModel.kt
│ │ │ ├── ResetAppUseCase.kt
│ │ │ ├── SeekBarState.kt
│ │ │ ├── ServerSettingsFragment.kt
│ │ │ └── SettingsViewModel.kt
│ │ │ ├── splash
│ │ │ ├── SplashFragment.kt
│ │ │ └── SplashViewModel.kt
│ │ │ └── voiceAnalysis
│ │ │ ├── AacRecorder.kt
│ │ │ ├── RecordingController.kt
│ │ │ ├── RecordingData.kt
│ │ │ ├── VoiceAnalysisController.kt
│ │ │ ├── VoiceAnalysisOption.kt
│ │ │ ├── VoiceAnalysisService.kt
│ │ │ ├── VoiceAnalysisUseCase.kt
│ │ │ └── WavFileGenerator.kt
│ └── res
│ │ ├── drawable-hdpi
│ │ ├── heart.png
│ │ ├── logo.png
│ │ ├── made_with_love.png
│ │ ├── white_logo.png
│ │ └── wifi.png
│ │ ├── drawable-mdpi
│ │ ├── heart.png
│ │ ├── logo.png
│ │ ├── made_with_love.png
│ │ ├── white_logo.png
│ │ └── wifi.png
│ │ ├── drawable-v21
│ │ ├── button_purple_outline_to_purple_rounded.xml
│ │ ├── button_rounded_ripple.xml
│ │ ├── button_white_outline_to_purple_rounded.xml
│ │ └── dark_button_rounded_ripple.xml
│ │ ├── drawable-xhdpi
│ │ ├── heart.png
│ │ ├── logo.png
│ │ ├── made_with_love.png
│ │ ├── white_logo.png
│ │ └── wifi.png
│ │ ├── drawable-xxhdpi
│ │ ├── heart.png
│ │ ├── logo.png
│ │ ├── made_with_love.png
│ │ ├── white_logo.png
│ │ └── wifi.png
│ │ ├── drawable-xxxhdpi
│ │ ├── heart.png
│ │ ├── logo.png
│ │ ├── made_with_love.png
│ │ ├── white_logo.png
│ │ └── wifi.png
│ │ ├── drawable
│ │ ├── all_circle_white_bg.xml
│ │ ├── all_done.xml
│ │ ├── animated_done.xml
│ │ ├── animated_fail.xml
│ │ ├── baby_logo.xml
│ │ ├── button_purple_outline_to_purple_rounded.xml
│ │ ├── button_rounded_ripple.xml
│ │ ├── button_white_outline_to_purple_rounded.xml
│ │ ├── circle_primary_bg.xml
│ │ ├── circle_primary_dark_bg.xml
│ │ ├── dark_button_rounded_ripple.xml
│ │ ├── download.xml
│ │ ├── failed.xml
│ │ ├── feature_a.xml
│ │ ├── feature_a_indicator.xml
│ │ ├── feature_b.xml
│ │ ├── feature_b_indicator.xml
│ │ ├── feature_c.xml
│ │ ├── feature_c_indicator.xml
│ │ ├── feature_d.xml
│ │ ├── ic_activity.xml
│ │ ├── ic_arrow_back.xml
│ │ ├── ic_arrow_down.xml
│ │ ├── ic_baby.xml
│ │ ├── ic_camera.xml
│ │ ├── ic_close.xml
│ │ ├── ic_edit.xml
│ │ ├── ic_gear_wheel.xml
│ │ ├── ic_info.xml
│ │ ├── ic_launcher_foreground.xml
│ │ ├── ic_microphone.xml
│ │ ├── ic_night_mode.xml
│ │ ├── ic_night_mode_active.xml
│ │ ├── ic_select_photo_camera.xml
│ │ ├── ic_select_photo_placeholder.xml
│ │ ├── ic_sound_max.xml
│ │ ├── ic_sound_min.xml
│ │ ├── ic_specify_device_checkmark.xml
│ │ ├── ic_sync.xml
│ │ ├── monitoring.xml
│ │ ├── phone_item_icon.xml
│ │ ├── recycler_divider.xml
│ │ ├── safety.xml
│ │ └── shapes.xml
│ │ ├── font
│ │ ├── rubik.xml
│ │ ├── rubik_bold.otf
│ │ ├── rubik_light.otf
│ │ ├── rubik_medium.otf
│ │ └── rubik_regular.otf
│ │ ├── layout
│ │ ├── activity_client_home.xml
│ │ ├── activity_onboarding.xml
│ │ ├── activity_server.xml
│ │ ├── debug_view.xml
│ │ ├── found_service_item.xml
│ │ ├── fragment_all_done.xml
│ │ ├── fragment_camera_permission.xml
│ │ ├── fragment_child_monitor.xml
│ │ ├── fragment_client_activity_log.xml
│ │ ├── fragment_client_dashboard.xml
│ │ ├── fragment_client_live_camera.xml
│ │ ├── fragment_client_settings.xml
│ │ ├── fragment_connect_wifi.xml
│ │ ├── fragment_connecting_devices.xml
│ │ ├── fragment_connecting_setup_information.xml
│ │ ├── fragment_denied_permission.xml
│ │ ├── fragment_failed_devices_connecting.xml
│ │ ├── fragment_feature_a.xml
│ │ ├── fragment_feature_b.xml
│ │ ├── fragment_feature_c.xml
│ │ ├── fragment_info_about_devices.xml
│ │ ├── fragment_microphone_permission.xml
│ │ ├── fragment_pairing.xml
│ │ ├── fragment_parent_device_info.xml
│ │ ├── fragment_server_settings.xml
│ │ ├── fragment_specify_device.xml
│ │ ├── fragment_splash.xml
│ │ ├── fragment_voice_recordings_setting.xml
│ │ ├── item_log_activity_end_text.xml
│ │ ├── item_log_activity_header.xml
│ │ ├── item_log_activity_record.xml
│ │ ├── onboarding_buttons.xml
│ │ ├── seek_bar_progress.xml
│ │ ├── toolbar_child.xml
│ │ └── toolbar_default.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
│ │ ├── navigation
│ │ ├── client_home_nav_graph.xml
│ │ ├── server_nav_graph.xml
│ │ └── splash_nav_graph.xml
│ │ ├── values-v21
│ │ └── styles.xml
│ │ ├── values
│ │ ├── animation.xml
│ │ ├── attrs.xml
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── ic_launcher_background.xml
│ │ ├── resourfes.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ │ └── xml
│ │ ├── progress_scene.xml
│ │ └── searching_scene.xml
│ ├── release
│ └── kotlin
│ │ └── co
│ │ └── netguru
│ │ └── baby
│ │ └── monitor
│ │ └── client
│ │ ├── application
│ │ ├── DebugMetricsHelper.kt
│ │ └── RxJavaErrorHandlerImpl.kt
│ │ └── feature
│ │ └── debug
│ │ ├── DebugModule.kt
│ │ └── DebugNotificationManager.kt
│ ├── releasePreprod
│ └── kotlin
│ │ └── co
│ │ └── netguru
│ │ └── baby
│ │ └── monitor
│ │ └── client
│ │ └── application
│ │ ├── DebugMetricsHelper.kt
│ │ └── RxJavaErrorHandlerImpl.kt
│ └── test
│ ├── kotlin
│ └── co
│ │ └── netguru
│ │ └── baby
│ │ └── monitor
│ │ ├── AnswerWithPair.kt
│ │ ├── RxSchedulersOverrideRule.kt
│ │ ├── RxTestSchedulerOverrideRule.kt
│ │ ├── TestUtils.kt
│ │ └── client
│ │ ├── common
│ │ └── OptionalTest.kt
│ │ ├── data
│ │ └── DataRepositoryTest.kt
│ │ └── feature
│ │ ├── babycrynotification
│ │ ├── NotifyBabyEventUseCaseTest.kt
│ │ ├── OpenCameraUseCaseTest.kt
│ │ └── SnoozeNotificationUseCaseTest.kt
│ │ ├── batterylevel
│ │ └── NotifyLowBatteryUseCaseTest.kt
│ │ ├── client
│ │ └── home
│ │ │ ├── ClientHomeViewModelTest.kt
│ │ │ ├── RestartAppUseCaseTest.kt
│ │ │ ├── SendBabyNameUseCaseTest.kt
│ │ │ ├── SendFirebaseTokenUseCaseTest.kt
│ │ │ └── livecamera
│ │ │ └── ClientLiveCameraFragmentViewModelTest.kt
│ │ ├── communication
│ │ ├── nsd
│ │ │ └── NsdServiceManagerTest.kt
│ │ └── pairing
│ │ │ ├── PairingUseCaseTest.kt
│ │ │ └── ServiceDiscoveryViewModelTest.kt
│ │ ├── onboarding
│ │ └── FinishOnboardingUseCaseTest.kt
│ │ ├── server
│ │ ├── ChildMonitorViewModelTest.kt
│ │ ├── ReceiveFirebaseTokenUseCaseTest.kt
│ │ └── ServerViewModelTest.kt
│ │ ├── settings
│ │ ├── ConfigurationViewModelTest.kt
│ │ └── ResetAppUseCaseTest.kt
│ │ └── voiceAnalysis
│ │ ├── ConfirmationUseCaseTest.kt
│ │ ├── RecordingControllerTest.kt
│ │ └── VoiceAnalysisControllerTest.kt
│ └── resources
│ └── mockito-extensions
│ └── org.mockito.plugins.MockMaker
├── build.gradle
├── buildsystem
├── bitrise.gradle
├── dependencies.gradle
└── secrets.gradle
├── default-detekt-config.yml
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ### Task
2 |
3 | - [JIRA](https://netguru.atlassian.net/browse/BM-)
4 |
5 | ### Description
6 |
7 |
8 | ### Additional Notes (optional)
9 |
10 |
11 | ### Checklist
12 |
13 | - [ ] I am following the [code style guide](https://netguru.atlassian.net/wiki/display/ANDROID/Android+best+practices)
14 | - [ ] The code includes tests of new features
15 | - [ ] The code passes static analysis
16 | - [ ] README.md is up to date
17 | - [ ] Version number is up to date
18 | - [ ] ProGuard configuration files are up to date
19 | - [ ] All temporary TODOs and FIXMEs are removed
20 | - [ ] I have tested the solution on these devices:
21 | * Device A, Android X.Y.Z
22 | * Device B, Android Z.Y
23 | * Virtual device W, Android Y.Y.Z
24 |
25 | ### Merge
26 |
27 | - [ ] Committer
28 | - [ ] Reviewer
29 | - [ ] Project lead
30 |
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source "https://rubygems.org"
2 |
3 | git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4 |
5 | gem 'danger'
6 | gem 'danger-android_lint'
7 | gem 'danger-kotlin_detekt'
8 | gem 'danger-junit'
9 | gem 'danger-jacoco'
10 | gem 'danger-slack'
11 | gem 'danger-jira'
12 | gem 'danger-apkanalyzer'
13 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/proguard-instant-run.pro:
--------------------------------------------------------------------------------
1 | -keep class android.arch.lifecycle.** {*;}
2 | -keep class co.netguru.baby.monitor.client.feature.onboarding.OnboardingActivity
3 |
4 | #Firebase Database
5 | -keep class co.netguru.baby.monitor.client.data.communication.firebase.** { *; }
6 |
--------------------------------------------------------------------------------
/app/proguard-rules-test.pro:
--------------------------------------------------------------------------------
1 | # Additional proguard rules for instrumentation testing
2 | -ignorewarnings
3 |
4 | -keepattributes *Annotation*
5 |
6 | -dontnote junit.framework.**
7 | -dontnote junit.runner.**
8 |
9 | -dontwarn android.test.**
10 | -dontwarn android.support.test.**
11 | -dontwarn org.junit.**
12 | -dontwarn org.hamcrest.**
13 | -dontwarn com.squareup.javawriter.JavaWriter
14 |
15 | -keep class rx.plugins.** { *; }
16 | -keep class org.junit.** { *; }
17 | -keep class co.netguru.android.testcommons.** { *; }
18 | -keep class android.support.test.espresso.** { *; }
19 | -dontwarn org.hamcrest.**
20 |
21 | -keep class android.arch.** { *; }
22 | -keep interface android.arch.** { *; }
23 |
24 | -keep class kotlin.** { *; }
25 | -keep class kotlin.Metadata { *; }
26 | -dontwarn kotlin.**
27 | -keepclassmembers class **$WhenMappings {
28 | ;
29 | }
30 | -keepclassmembers class kotlin.Metadata {
31 | public ;
32 | }
33 | -assumenosideeffects class kotlin.jvm.internal.Intrinsics {
34 | static void checkParameterIsNotNull(java.lang.Object, java.lang.String);
35 | }
36 |
37 | #Firebase Database
38 | -keep class co.netguru.baby.monitor.client.data.communication.firebase.** { *; }
39 |
--------------------------------------------------------------------------------
/app/src/debug/kotlin/co/netguru/baby/monitor/client/application/DebugMetricsHelper.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.application
2 |
3 | import timber.log.Timber
4 | import javax.inject.Inject
5 | import javax.inject.Singleton
6 |
7 | /**
8 | * Helper class that initializes a set of debugging tools
9 | * for the debug build type and register crash manager for release type.
10 | * ## Debug type tools:
11 | * - AndroidDevMetrics
12 | * - Stetho
13 | * - StrictMode
14 | * - LeakCanary
15 | * - Timber
16 | *
17 | * ## Release type tools:
18 | * - CrashManager
19 | */
20 | @Singleton
21 | class DebugMetricsHelper @Inject constructor() {
22 |
23 | internal fun init() {
24 | Timber.plant(Timber.DebugTree())
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/debug/kotlin/co/netguru/baby/monitor/client/application/RxJavaErrorHandlerImpl.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.application
2 |
3 | import io.reactivex.exceptions.UndeliverableException
4 |
5 | class RxJavaErrorHandlerImpl : RxJavaErrorHandler() {
6 |
7 | override fun handleUndeliverableException(undeliverableException: UndeliverableException) {
8 | /**
9 | * Crash the app while in debug as undeliverable exception can sometimes be indication of
10 | * bug's that could have been prevented. Check if disposables/cancelables are set properly
11 | * on emitter or backup with tryOnError from emitter if that's not possible.
12 | */
13 | undeliverableException.printStackTrace()
14 | uncaught(undeliverableException)
15 | }
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/app/src/main/assets/model_exp76.pb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netguru/baby-monitor-client-android/3c3d8838de7ed8a340ebb79a64f249681669abf2/app/src/main/assets/model_exp76.pb
--------------------------------------------------------------------------------
/app/src/main/assets/tiny_conv_dataset.pb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netguru/baby-monitor-client-android/3c3d8838de7ed8a340ebb79a64f249681669abf2/app/src/main/assets/tiny_conv_dataset.pb
--------------------------------------------------------------------------------
/app/src/main/ic_launcher-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netguru/baby-monitor-client-android/3c3d8838de7ed8a340ebb79a64f249681669abf2/app/src/main/ic_launcher-web.png
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/application/App.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.application
2 |
3 | import co.netguru.baby.monitor.client.application.di.DaggerApplicationComponent
4 | import co.netguru.baby.monitor.client.application.firebase.FirebaseRepository
5 | import com.jakewharton.threetenabp.AndroidThreeTen
6 | import dagger.android.AndroidInjector
7 | import dagger.android.support.DaggerApplication
8 | import io.reactivex.plugins.RxJavaPlugins
9 | import javax.inject.Inject
10 |
11 | class App : DaggerApplication() {
12 |
13 | @Inject
14 | lateinit var debugMetricsHelper: DebugMetricsHelper
15 |
16 | @Inject
17 | lateinit var rxJavaErrorHandler: RxJavaErrorHandler
18 |
19 | @Inject
20 | lateinit var firebaseRepository: FirebaseRepository
21 |
22 | override fun onCreate() {
23 | super.onCreate()
24 | debugMetricsHelper.init()
25 | RxJavaPlugins.setErrorHandler(rxJavaErrorHandler)
26 | AndroidThreeTen.init(this)
27 | firebaseRepository.initializeApp(this)
28 | }
29 |
30 | override fun applicationInjector(): AndroidInjector =
31 | DaggerApplicationComponent.builder().create(this)
32 | }
33 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/application/RxJavaErrorHandler.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.application
2 |
3 | import io.reactivex.exceptions.UndeliverableException
4 | import io.reactivex.functions.Consumer
5 |
6 | /**
7 | * [RxJava2 error handling](https://github.com/ReactiveX/RxJava/wiki/What's-different-in-2.0#error-handling)
8 | */
9 | abstract class RxJavaErrorHandler : Consumer {
10 |
11 | override fun accept(throwable: Throwable) = when (throwable) {
12 | is UndeliverableException -> {
13 | //we log such exceptions but avoid app crash for release as we can't do much in such case.
14 | handleUndeliverableException(throwable)
15 | }
16 | else -> {
17 | //we crash the app else - this is a bug
18 | throwable.printStackTrace()
19 | uncaught(throwable)
20 | }
21 | }
22 |
23 | /**
24 | * Something thrown error after stream finished.
25 | * If this happens often it can be indication of some problem in library or our codebase.
26 | * Make sure that source sets disposable/cancellable while creating stream.
27 | * You can also use tryOnError if the wrapped data source doesn't provide good way to cancel emissions.
28 | */
29 | abstract fun handleUndeliverableException(undeliverableException: UndeliverableException)
30 |
31 | protected fun uncaught(throwable: Throwable) {
32 | val currentThread = Thread.currentThread()
33 | val handler = currentThread.uncaughtExceptionHandler
34 | handler.uncaughtException(currentThread, throwable)
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/application/di/ActivityBindingsModule.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.application.di
2 |
3 | import co.netguru.baby.monitor.client.application.scope.ActivityScope
4 | import co.netguru.baby.monitor.client.feature.client.home.ClientHomeActivity
5 | import co.netguru.baby.monitor.client.feature.server.ServerActivity
6 | import dagger.Module
7 | import dagger.android.ContributesAndroidInjector
8 |
9 | @Module
10 | internal abstract class ActivityBindingsModule {
11 |
12 | @ActivityScope
13 | @ContributesAndroidInjector
14 | internal abstract fun clientHomeActivityInjector(): ClientHomeActivity
15 |
16 | @ActivityScope
17 | @ContributesAndroidInjector
18 | internal abstract fun serverActivityInjector(): ServerActivity
19 | }
20 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/application/di/ApplicationComponent.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.application.di
2 |
3 | import co.netguru.baby.monitor.client.application.App
4 | import co.netguru.baby.monitor.client.common.base.ViewModelModule
5 | import dagger.Component
6 | import dagger.android.AndroidInjectionModule
7 | import dagger.android.AndroidInjector
8 | import dagger.android.support.AndroidSupportInjectionModule
9 | import javax.inject.Singleton
10 |
11 | @Singleton
12 | @Component(
13 | modules = [
14 | AndroidInjectionModule::class,
15 | AndroidSupportInjectionModule::class,
16 | ApplicationModule::class,
17 | ViewModelModule::class,
18 | FragmentBindingsModule::class,
19 | ActivityBindingsModule::class,
20 | ServiceBindingsModule::class,
21 | SharedPreferencesModule::class,
22 | NotificationsModule::class,
23 | FirebaseModule::class,
24 | NetworkModule::class
25 | ]
26 | )
27 | internal interface ApplicationComponent : AndroidInjector {
28 | @Component.Builder
29 | abstract class Builder : AndroidInjector.Builder()
30 | }
31 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/application/di/BabyMonitorGlideModule.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.application.di
2 |
3 | import com.bumptech.glide.annotation.GlideModule
4 | import com.bumptech.glide.module.AppGlideModule
5 |
6 | @GlideModule
7 | class BabyMonitorGlideModule : AppGlideModule()
8 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/application/di/FirebaseModule.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.application.di
2 |
3 | import android.content.Context
4 | import co.netguru.baby.monitor.client.application.firebase.FirebaseRepository
5 | import co.netguru.baby.monitor.client.application.firebase.FirebaseSharedPreferencesWrapper
6 | import co.netguru.baby.monitor.client.feature.analytics.AnalyticsManager
7 | import co.netguru.baby.monitor.client.feature.firebasenotification.FirebaseInstanceManager
8 | import com.google.firebase.analytics.FirebaseAnalytics
9 | import com.google.firebase.iid.FirebaseInstanceId
10 | import dagger.Module
11 | import dagger.Provides
12 | import javax.inject.Singleton
13 |
14 | @Module
15 | object FirebaseModule {
16 | @Singleton
17 | @Provides
18 | fun firebaseRepository(preferencesWrapper: FirebaseSharedPreferencesWrapper, context: Context) =
19 | FirebaseRepository(preferencesWrapper, context)
20 |
21 | @Singleton
22 | @Provides
23 | fun firebaseInstanceManager() = FirebaseInstanceManager(FirebaseInstanceId.getInstance())
24 |
25 | @Provides
26 | @Singleton
27 | fun provideAnalyticsManager(context: Context) =
28 | AnalyticsManager(FirebaseAnalytics.getInstance(context))
29 | }
30 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/application/di/NetworkModule.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.application.di
2 |
3 | import android.content.Context
4 | import android.net.nsd.NsdManager
5 | import co.netguru.baby.monitor.client.application.App
6 | import co.netguru.baby.monitor.client.feature.communication.nsd.DeviceNameProvider
7 | import co.netguru.baby.monitor.client.feature.communication.nsd.NsdServiceManager
8 | import com.google.gson.Gson
9 | import dagger.Module
10 | import dagger.Provides
11 | import dagger.Reusable
12 | import okhttp3.OkHttpClient
13 |
14 | @Module
15 | object NetworkModule {
16 | @Reusable
17 | @Provides
18 | fun nsdManager(app: App): NsdManager = app.getSystemService(Context.NSD_SERVICE) as NsdManager
19 |
20 | @Provides
21 | fun nsdServiceManager(nsdManager: NsdManager, deviceNameProvider: DeviceNameProvider) =
22 | NsdServiceManager(nsdManager, deviceNameProvider)
23 |
24 | @Provides
25 | @Reusable
26 | fun provideGson() =
27 | Gson()
28 |
29 | @Provides
30 | @Reusable
31 | fun provideOkHttp() =
32 | OkHttpClient.Builder()
33 | .build()
34 | }
35 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/application/di/NotificationsModule.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.application.di
2 |
3 | import android.content.Context
4 | import co.netguru.baby.monitor.client.R
5 | import co.netguru.baby.monitor.client.common.NotificationHandler
6 | import co.netguru.baby.monitor.client.feature.babynotification.NotifyBabyEventUseCase
7 | import co.netguru.baby.monitor.client.feature.firebasenotification.FirebaseNotificationSender
8 | import dagger.Module
9 | import dagger.Provides
10 |
11 | @Module
12 | object NotificationsModule {
13 | @Provides
14 | fun notificationHandler(context: Context) = NotificationHandler(context)
15 |
16 | @Provides
17 | fun provideNotifyBabyEventUseCase(
18 | notificationSender: FirebaseNotificationSender,
19 | context: Context
20 | ) =
21 | NotifyBabyEventUseCase(
22 | notificationSender,
23 | mapOf(
24 | NotifyBabyEventUseCase.CRY_TITLE_KEY
25 | to context.resources.getString(R.string.notification_baby_is_crying_title),
26 | NotifyBabyEventUseCase.NOISE_TITLE_KEY
27 | to context.resources.getString(R.string.notification_noise_detected_title),
28 | NotifyBabyEventUseCase.NOTIFICATION_TEXT_KEY
29 | to context.resources.getString(R.string.notification_baby_is_crying_content)
30 | )
31 | )
32 | }
33 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/application/di/ServiceBindingsModule.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.application.di
2 |
3 | import co.netguru.baby.monitor.client.feature.babynotification.BabyMonitorMessagingService
4 | import co.netguru.baby.monitor.client.feature.communication.webrtc.server.WebRtcService
5 | import co.netguru.baby.monitor.client.feature.communication.websocket.WebSocketServerService
6 | import co.netguru.baby.monitor.client.feature.babynotification.BabyEventActionIntentService
7 | import co.netguru.baby.monitor.client.feature.voiceAnalysis.VoiceAnalysisService
8 | import dagger.Module
9 | import dagger.android.ContributesAndroidInjector
10 |
11 | @Module
12 | internal abstract class ServiceBindingsModule {
13 |
14 | @ContributesAndroidInjector
15 | internal abstract fun bindVoiceAnalysisService(): VoiceAnalysisService
16 |
17 | @ContributesAndroidInjector
18 | internal abstract fun bindWebRtcService(): WebRtcService
19 |
20 | @ContributesAndroidInjector
21 | internal abstract fun bindWebSocketServerService(): WebSocketServerService
22 |
23 | @ContributesAndroidInjector
24 | internal abstract fun bindMessagingService(): BabyMonitorMessagingService
25 |
26 | @ContributesAndroidInjector
27 | internal abstract fun bindBabyEventActionIntentService(): BabyEventActionIntentService
28 | }
29 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/application/di/SharedPreferencesModule.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.application.di
2 |
3 | import android.content.Context
4 | import android.content.SharedPreferences
5 | import co.netguru.baby.monitor.client.application.App
6 | import dagger.Module
7 | import dagger.Provides
8 | import javax.inject.Qualifier
9 | import javax.inject.Singleton
10 |
11 | @Module
12 | class SharedPreferencesModule {
13 |
14 | @ConfigurationPreferencesQualifier
15 | @Singleton
16 | @Provides
17 | fun provideConfigurationSharedPreferences(app: App): SharedPreferences =
18 | app.getSharedPreferences(app.packageName + CONFIGURATION_PREFERENCES, Context.MODE_PRIVATE)
19 |
20 | companion object {
21 | private const val CONFIGURATION_PREFERENCES = "configuration"
22 | }
23 | }
24 |
25 | @Qualifier
26 | annotation class ConfigurationPreferencesQualifier
27 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/application/scope/ActivityScope.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.application.scope
2 |
3 | import javax.inject.Scope
4 |
5 | @Scope
6 | annotation class ActivityScope
7 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/application/scope/FragmentScope.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.application.scope
2 |
3 | import javax.inject.Scope
4 |
5 | @Scope
6 | annotation class FragmentScope
7 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/common/DateProvider.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.common
2 |
3 | import org.threeten.bp.LocalDateTime
4 | import org.threeten.bp.format.DateTimeFormatter
5 |
6 | object DateProvider {
7 | val midnight: LocalDateTime
8 | get() = LocalDateTime.now().withHour(0).withMinute(0).withSecond(0)
9 | val yesterdaysMidnight: LocalDateTime
10 | get() = LocalDateTime.now().withHour(0).withMinute(0).withSecond(0).minusDays(1)
11 |
12 | val headerFormatter = DateTimeFormatter.ofPattern("MMM dd, yyyy")
13 | val timeStampFormatter = DateTimeFormatter.ofPattern("hh:mm:ss a")
14 | }
15 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/common/LocalDateTimeProvider.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.common
2 |
3 | import org.threeten.bp.LocalDateTime
4 | import javax.inject.Inject
5 |
6 | class LocalDateTimeProvider @Inject constructor() {
7 | fun now(): LocalDateTime = LocalDateTime.now()
8 | }
9 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/common/PermissionUtils.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.common
2 |
3 | import android.app.Activity
4 | import android.content.Context
5 | import android.content.pm.PackageManager
6 | import androidx.core.app.ActivityCompat
7 | import androidx.core.content.ContextCompat
8 |
9 | object PermissionUtils {
10 |
11 | fun arePermissionsGranted(context: Context, vararg permission: String): Boolean {
12 | return !permission
13 | .any { !hasPermission(context, it) }
14 | }
15 |
16 | private fun hasPermission(context: Context, permission: String): Boolean {
17 | return ContextCompat.checkSelfPermission(
18 | context,
19 | permission
20 | ) == PackageManager.PERMISSION_GRANTED
21 | }
22 |
23 | fun getPermissionsRequestResult(
24 | activity: Activity,
25 | requestResultCode: Int,
26 | resultRequestCode: Int,
27 | grantResults: IntArray,
28 | vararg permission: String
29 | ): PermissionResult {
30 | return when {
31 | resultRequestCode != requestResultCode -> PermissionResult.NOT_GRANTED
32 | grantResults.none { it == PackageManager.PERMISSION_DENIED } -> PermissionResult.GRANTED
33 | permission.any {
34 | ActivityCompat.shouldShowRequestPermissionRationale(
35 | activity,
36 | it
37 | )
38 | } -> PermissionResult.SHOW_RATIONALE
39 | else -> PermissionResult.NOT_GRANTED
40 | }
41 | }
42 | }
43 |
44 | enum class PermissionResult {
45 | GRANTED,
46 | NOT_GRANTED,
47 | SHOW_RATIONALE
48 | }
49 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/common/Randomiser.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.common
2 |
3 | import javax.inject.Inject
4 | import kotlin.random.Random
5 | import kotlin.random.nextInt
6 |
7 | class Randomiser @Inject constructor() {
8 |
9 | fun getRandomDigits(numberOfDigits: Int) =
10 | List(numberOfDigits) { Random.nextInt(RANDOM_DIGIT_RANGE) }
11 |
12 | companion object {
13 | private val RANDOM_DIGIT_RANGE = 0..9
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/common/RunsInBackground.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.common
2 |
3 | annotation class RunsInBackground
4 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/common/SchedulersProvider.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.common
2 |
3 | import io.reactivex.Scheduler
4 | import io.reactivex.android.schedulers.AndroidSchedulers
5 | import io.reactivex.schedulers.Schedulers
6 | import javax.inject.Inject
7 |
8 | interface ISchedulersProvider {
9 | fun io(): Scheduler = Schedulers.io()
10 | fun computation(): Scheduler = Schedulers.computation()
11 | fun mainThread(): Scheduler = AndroidSchedulers.mainThread()
12 | }
13 |
14 | class SchedulersProvider @Inject constructor() : ISchedulersProvider
15 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/common/SingleLiveEvent.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.common
2 |
3 | import androidx.annotation.MainThread
4 | import androidx.annotation.Nullable
5 | import androidx.lifecycle.LifecycleOwner
6 | import androidx.lifecycle.MutableLiveData
7 | import androidx.lifecycle.Observer
8 | import timber.log.Timber
9 | import java.util.concurrent.atomic.AtomicBoolean
10 |
11 | class SingleLiveEvent : MutableLiveData() {
12 |
13 | private val mPending = AtomicBoolean(false)
14 |
15 | @MainThread
16 | override fun observe(owner: LifecycleOwner, observer: Observer) {
17 |
18 | if (hasActiveObservers()) {
19 | Timber.w("Multiple observers registered but only one will be notified of changes.")
20 | }
21 |
22 | // Observe the internal MutableLiveData
23 | super.observe(owner, Observer { t ->
24 | if (mPending.compareAndSet(true, false)) {
25 | observer.onChanged(t)
26 | }
27 | })
28 | }
29 |
30 | @MainThread
31 | override fun setValue(@Nullable t: T?) {
32 | mPending.set(true)
33 | super.setValue(t)
34 | }
35 |
36 | /**
37 | * Used for cases where T is Void, to make calls cleaner.
38 | */
39 | @MainThread
40 | fun call() {
41 | value = null
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/common/base/AnalyticScreen.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.common.base
2 |
3 | import co.netguru.baby.monitor.client.feature.analytics.Screen
4 |
5 | interface AnalyticScreen {
6 | val screen: Screen?
7 | }
8 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/common/base/BaseFragment.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.common.base
2 |
3 | import android.os.Bundle
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import co.netguru.baby.monitor.client.feature.analytics.AnalyticsManager
8 | import co.netguru.baby.monitor.client.feature.analytics.Screen
9 | import dagger.android.support.DaggerFragment
10 | import javax.inject.Inject
11 |
12 | abstract class BaseFragment : DaggerFragment(), AnalyticScreen {
13 | abstract val layoutResource: Int
14 | override val screen: Screen? = null
15 |
16 | @Inject
17 | lateinit var analyticsManager: AnalyticsManager
18 |
19 | override fun onCreateView(
20 | inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
21 | ) = inflater.inflate(layoutResource, container, false)
22 |
23 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
24 | super.onViewCreated(view, savedInstanceState)
25 | screen?.run {
26 | analyticsManager.setCurrentScreen(requireActivity(), this)
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/common/base/BaseServiceWithFacade.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.common.base
2 |
3 | import dagger.android.DaggerService
4 | import javax.inject.Inject
5 |
6 | abstract class BaseServiceWithFacade> :
7 | DaggerService() {
8 |
9 | @Inject
10 | lateinit var serviceController: C
11 |
12 | override fun onCreate() {
13 | super.onCreate()
14 | attachServiceToController()
15 | }
16 |
17 | override fun onDestroy() {
18 | super.onDestroy()
19 | serviceController.detachService()
20 | }
21 |
22 | @Suppress("UNCHECKED_CAST")
23 | private fun attachServiceToController() {
24 | serviceController.attachService(this as T)
25 | }
26 | }
27 |
28 | interface ServiceFacade
29 |
30 | interface ServiceController {
31 | fun attachService(service: T)
32 | fun detachService()
33 | }
34 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/common/extensions/ActivityExtensions.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.common.extensions
2 |
3 | import android.app.Activity
4 | import android.media.AudioManager
5 |
6 | fun Activity.controlVideoStreamVolume() {
7 | volumeControlStream = AudioManager.STREAM_VOICE_CALL
8 | }
9 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/common/extensions/ContextExtensions.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.common.extensions
2 |
3 | import android.content.Context
4 | import android.content.pm.PackageManager
5 | import androidx.annotation.AttrRes
6 | import androidx.annotation.ColorRes
7 | import androidx.annotation.DrawableRes
8 | import androidx.core.content.ContextCompat
9 | import android.util.TypedValue
10 | import io.reactivex.Single
11 | import java.io.File
12 | import java.io.FileOutputStream
13 |
14 | fun Context.getColorCompat(@ColorRes color: Int) = ContextCompat.getColor(this, color)
15 |
16 | fun Context.getDrawableCompat(@DrawableRes drawable: Int) =
17 | ContextCompat.getDrawable(this, drawable)
18 |
19 | fun Context.getAttributeColor(@AttrRes attrColor: Int): Int {
20 | val typedValue = TypedValue()
21 | theme.resolveAttribute(attrColor, typedValue, true)
22 | return typedValue.data
23 | }
24 |
25 | fun Context.getAttributeDrawable(@AttrRes attrDrawableRes: Int): Int {
26 | val typedValue = TypedValue()
27 | theme.resolveAttribute(attrDrawableRes, typedValue, true)
28 | return typedValue.resourceId
29 | }
30 |
31 | fun Context.allPermissionsGranted(permissions: Array): Boolean {
32 | for (permission in permissions) {
33 | if (ContextCompat.checkSelfPermission(this, permission)
34 | != PackageManager.PERMISSION_GRANTED
35 | ) {
36 | return false
37 | }
38 | }
39 |
40 | return true
41 | }
42 |
43 | fun Context.saveAssetToCache(name: String) = Single.just(File(cacheDir.toString() + name))
44 | .map { file ->
45 | assets.open(name).use { inputStream ->
46 | val buffer = ByteArray(inputStream.available())
47 | inputStream.read(buffer)
48 | FileOutputStream(file).use {
49 | it.write(buffer)
50 | }
51 | return@map file
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/common/extensions/DataExtensions.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.common.extensions
2 |
3 | import com.google.gson.Gson
4 |
5 | inline fun String.toData(): T? =
6 | Gson().fromJson(this@toData, T::class.java)
7 |
8 | inline fun T.toJson(): String =
9 | Gson().toJson(this@toJson)
10 |
11 | infix fun A?.and(that: B?): Pair? =
12 | if (this == null || that == null) {
13 | null
14 | } else {
15 | Pair(this, that)
16 | }
17 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/common/extensions/FragmentExtensions.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.common.extensions
2 |
3 | import android.content.Intent
4 | import android.content.ServiceConnection
5 | import android.content.res.Resources
6 | import android.net.Uri
7 | import android.provider.Settings
8 | import androidx.annotation.StringRes
9 | import androidx.core.content.res.ResourcesCompat
10 | import androidx.fragment.app.Fragment
11 | import com.google.android.material.snackbar.Snackbar
12 |
13 | fun Fragment.showSnackbarMessage(
14 | @StringRes resId: Int,
15 | action: (Snackbar.() -> Unit)? = null
16 | ): Snackbar? {
17 | return view?.run {
18 | Snackbar.make(this, resId, Snackbar.LENGTH_LONG).apply {
19 | action?.invoke(this)
20 | show()
21 | }
22 | }
23 | }
24 |
25 | fun Fragment.startAppSettings() {
26 | Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).run {
27 | addCategory(Intent.CATEGORY_DEFAULT)
28 | data = Uri.parse("package:" + requireContext().packageName)
29 | startActivity(this)
30 | }
31 | }
32 |
33 | fun Fragment.bindService(intentClass: Class<*>, conn: ServiceConnection, flags: Int) {
34 | requireContext().bindService(
35 | Intent(requireContext(), intentClass),
36 | conn,
37 | flags
38 | )
39 | }
40 |
41 | fun Fragment.getColor(resource: Int, theme: Resources.Theme? = null) = ResourcesCompat.getColor(
42 | resources,
43 | resource,
44 | theme
45 | )
46 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/common/extensions/LiveDataExtensions.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.common.extensions
2 |
3 | import androidx.lifecycle.LifecycleOwner
4 | import androidx.lifecycle.LiveData
5 | import androidx.lifecycle.Observer
6 |
7 | fun LiveData.observeNonNull(owner: LifecycleOwner, observer: (t: T) -> Unit) {
8 | this.observe(owner, Observer {
9 | it?.let(observer)
10 | })
11 | }
12 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/common/extensions/NotificationExtensions.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.common.extensions
2 |
3 | import androidx.core.app.NotificationCompat
4 |
5 | fun NotificationCompat.Builder.addActions(actions: List?): NotificationCompat.Builder {
6 | actions?.forEach {
7 | addAction(it)
8 | }
9 | return this
10 | }
11 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/common/extensions/RxExtensions.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.common.extensions
2 |
3 | import io.reactivex.*
4 | import io.reactivex.android.schedulers.AndroidSchedulers
5 | import io.reactivex.schedulers.Schedulers
6 |
7 | fun Completable.applyIoSchedulers() = this.subscribeOn(Schedulers.io())
8 | .observeOn(AndroidSchedulers.mainThread())
9 |
10 | fun Completable.applyComputationSchedulers() = this.subscribeOn(Schedulers.computation())
11 | .observeOn(AndroidSchedulers.mainThread())
12 |
13 | fun Maybe.applyIoSchedulers() = this.subscribeOn(Schedulers.io())
14 | .observeOn(AndroidSchedulers.mainThread())
15 |
16 | fun Maybe.applyComputationSchedulers() = this.subscribeOn(Schedulers.computation())
17 | .observeOn(AndroidSchedulers.mainThread())
18 |
19 | fun Single.applyIoSchedulers() = this.subscribeOn(Schedulers.io())
20 | .observeOn(AndroidSchedulers.mainThread())
21 |
22 | fun Single.applyComputationSchedulers() = this.subscribeOn(Schedulers.computation())
23 | .observeOn(AndroidSchedulers.mainThread())
24 |
25 | fun Observable.applyIoSchedulers() = this.subscribeOn(Schedulers.io())
26 | .observeOn(AndroidSchedulers.mainThread())
27 |
28 | fun Observable.applyComputationSchedulers() = this.subscribeOn(Schedulers.computation())
29 | .observeOn(AndroidSchedulers.mainThread())
30 |
31 | fun Flowable.applyIoSchedulers(): Flowable = this.subscribeOn(Schedulers.io())
32 | .observeOn(AndroidSchedulers.mainThread())
33 |
34 | fun Flowable.applyComputationSchedulers(): Flowable =
35 | this.subscribeOn(Schedulers.computation())
36 | .observeOn(AndroidSchedulers.mainThread())
37 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/common/extensions/SharedPreferencesExtensions.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.common.extensions
2 |
3 | import android.content.SharedPreferences
4 |
5 | inline fun SharedPreferences.edit(action: SharedPreferences.Editor.() -> Unit) {
6 | val editor = edit()
7 | editor.action()
8 | editor.apply()
9 | }
10 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/common/view/BaseViewHolder.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.common.view
2 |
3 | import androidx.recyclerview.widget.RecyclerView
4 | import android.view.View
5 | import kotlinx.android.extensions.LayoutContainer
6 |
7 | abstract class BaseViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), LayoutContainer {
8 |
9 | override val containerView: View? = itemView
10 |
11 | abstract fun bindView(item: T)
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/common/view/CustomSurfaceViewRenderer.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.common.view
2 |
3 | import android.content.Context
4 | import android.util.AttributeSet
5 | import org.webrtc.EglBase
6 | import org.webrtc.RendererCommon
7 | import org.webrtc.SurfaceViewRenderer
8 |
9 | class CustomSurfaceViewRenderer(context: Context, attrs: AttributeSet?) : SurfaceViewRenderer(context, attrs) {
10 |
11 | internal var initialized = false
12 |
13 | constructor(context: Context) : this(context, null)
14 |
15 | override fun init(sharedContext: EglBase.Context?, rendererEvents: RendererCommon.RendererEvents?) {
16 | super.init(sharedContext, rendererEvents)
17 | initialized = true
18 | }
19 |
20 | override fun release() {
21 | super.release()
22 | initialized = false
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/common/view/StickyHeaderInterface.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.common.view
2 |
3 | import android.view.View
4 |
5 | interface StickyHeaderInterface {
6 |
7 | /**
8 | * This method gets called by [StickHeaderItemDecoration] to fetch the position of the header item in the adapter
9 | * that is used for (represents) item at specified position.
10 | * @param itemPosition int. Adapter's position of the item for which to do the search of the position of the header item.
11 | * @return int. Position of the header item in the adapter.
12 | */
13 | fun getHeaderPositionForItem(itemPosition: Int): Int
14 |
15 | /**
16 | * This method gets called by [StickHeaderItemDecoration] to get layout resource id for the header item at specified adapter's position.
17 | * @param headerPosition int. Position of the header item in the adapter.
18 | * @return int. Layout resource id.
19 | */
20 | fun getHeaderLayout(headerPosition: Int): Int
21 |
22 | /**
23 | * This method gets called by [StickHeaderItemDecoration] to setup the header View.
24 | * @param header View. Header to set the data on.
25 | * @param headerPosition int. Position of the header item in the adapter.
26 | */
27 | fun bindHeaderData(header: View, headerPosition: Int)
28 |
29 | /**
30 | * This method gets called by [StickHeaderItemDecoration] to verify whether the item represents a header.
31 | * @param itemPosition int.
32 | * @return true, if item at the specified adapter's position represents a header.
33 | */
34 | fun isHeader(itemPosition: Int): Boolean
35 | }
36 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/data/AppDatabase.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.data
2 |
3 | import androidx.room.Database
4 | import androidx.room.RoomDatabase
5 | import androidx.room.TypeConverters
6 | import co.netguru.baby.monitor.client.data.client.ChildDataDao
7 | import co.netguru.baby.monitor.client.data.client.ChildDataEntity
8 | import co.netguru.baby.monitor.client.data.client.home.log.LogDataDao
9 | import co.netguru.baby.monitor.client.data.client.home.log.LogDataEntity
10 | import co.netguru.baby.monitor.client.data.communication.ClientDataDao
11 | import co.netguru.baby.monitor.client.data.communication.ClientEntity
12 |
13 | @Database(
14 | entities = [
15 | LogDataEntity::class,
16 | ClientEntity::class,
17 | ChildDataEntity::class
18 | ],
19 | version = 5,
20 | exportSchema = false
21 | )
22 | @TypeConverters(Converters::class)
23 | abstract class AppDatabase : RoomDatabase() {
24 | abstract fun logDataDao(): LogDataDao
25 | abstract fun clientDao(): ClientDataDao
26 | abstract fun childDataDao(): ChildDataDao
27 |
28 | companion object {
29 | const val DATABASE_NAME = "baby-monitor-database"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/data/Converters.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.data
2 |
3 | import androidx.room.TypeConverter
4 | import co.netguru.baby.monitor.client.feature.voiceAnalysis.VoiceAnalysisOption
5 |
6 | class Converters {
7 | @TypeConverter
8 | fun fromName(name: String) = VoiceAnalysisOption.valueOf(name)
9 |
10 | @TypeConverter
11 | fun optionToString(voiceAnalysisOption: VoiceAnalysisOption) = voiceAnalysisOption.name
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/data/client/ChildDataDao.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.data.client
2 |
3 | import androidx.lifecycle.LiveData
4 | import androidx.room.Dao
5 | import androidx.room.Insert
6 | import androidx.room.OnConflictStrategy
7 | import androidx.room.Query
8 | import co.netguru.baby.monitor.client.feature.voiceAnalysis.VoiceAnalysisOption
9 | import io.reactivex.Maybe
10 |
11 | @Dao
12 | interface ChildDataDao {
13 |
14 | @Query("SELECT * FROM CHILD_DATA LIMIT 1")
15 | fun getChildData(): Maybe
16 |
17 | @Query("SELECT * FROM CHILD_DATA LIMIT 1")
18 | fun getChildLiveData(): LiveData
19 |
20 | @Query("SELECT COUNT(id) FROM CHILD_DATA WHERE address LIKE :address")
21 | fun getCount(address: String): Int
22 |
23 | @Insert(onConflict = OnConflictStrategy.REPLACE)
24 | fun insertChildData(data: ChildDataEntity)
25 |
26 | @Query("UPDATE CHILD_DATA SET name = :name WHERE id = 0")
27 | fun updateChildName(name: String): Int
28 |
29 | @Query("UPDATE CHILD_DATA SET snoozeTimeStamp = :notificationSnoozeTimeStamp WHERE id = 0")
30 | fun updateNotificationSnoozeTimeStamp(notificationSnoozeTimeStamp: Long): Int
31 |
32 | @Query("UPDATE CHILD_DATA SET voiceAnalysisOption = :voiceAnalysisOption WHERE id = 0")
33 | fun updateVoiceAnalysisOption(voiceAnalysisOption: VoiceAnalysisOption)
34 |
35 | @Query("UPDATE CHILD_DATA SET noiseLevel = :noiseLevel WHERE id = 0")
36 | fun updateNoiseLevel(noiseLevel: Int)
37 |
38 | @Query("DELETE FROM CHILD_DATA")
39 | fun deleteAll()
40 | }
41 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/data/client/ChildDataEntity.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.data.client
2 |
3 | import androidx.room.Entity
4 | import androidx.room.PrimaryKey
5 | import co.netguru.baby.monitor.client.feature.noisedetection.NoiseDetector
6 | import co.netguru.baby.monitor.client.feature.voiceAnalysis.VoiceAnalysisOption
7 |
8 | @Entity(tableName = "CHILD_DATA")
9 | data class ChildDataEntity(
10 | val address: String,
11 | var image: String? = null,
12 | val name: String? = null,
13 | val snoozeTimeStamp: Long? = null,
14 | val voiceAnalysisOption: VoiceAnalysisOption = VoiceAnalysisOption.MACHINE_LEARNING,
15 | val noiseLevel: Int = NoiseDetector.DEFAULT_NOISE_LEVEL,
16 | // Right now we are supporting only one child
17 | @PrimaryKey val id: Int = 0
18 | )
19 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/data/client/home/ToolbarState.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.data.client.home
2 |
3 | enum class ToolbarState {
4 | HIDDEN, HISTORY, DEFAULT
5 | }
6 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/data/client/home/log/LogData.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.data.client.home.log
2 |
3 | import org.threeten.bp.LocalDateTime
4 |
5 | sealed class LogData {
6 |
7 | abstract val timeStamp: LocalDateTime
8 |
9 | data class Data(
10 | val action: String,
11 | override val timeStamp: LocalDateTime,
12 | val image: String? = null
13 | ) : LogData()
14 |
15 | data class LogHeader(
16 | override val timeStamp: LocalDateTime
17 | ) : LogData()
18 |
19 | class EndText(override val timeStamp: LocalDateTime) : LogData()
20 | }
21 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/data/client/home/log/LogDataDao.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.data.client.home.log
2 |
3 | import androidx.room.Dao
4 | import androidx.room.Insert
5 | import androidx.room.Query
6 | import io.reactivex.*
7 |
8 | @Dao
9 | interface LogDataDao {
10 |
11 | @Query("SELECT * FROM LOG_DATA")
12 | fun getAllData(): Flowable>
13 |
14 | @Insert
15 | fun insertAll(vararg logs: LogDataEntity)
16 |
17 | @Query("DELETE FROM LOG_DATA")
18 | fun deleteAll()
19 | }
20 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/data/client/home/log/LogDataEntity.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.data.client.home.log
2 |
3 | import androidx.room.ColumnInfo
4 | import androidx.room.Entity
5 | import androidx.room.PrimaryKey
6 | import org.threeten.bp.LocalDateTime
7 |
8 | @Entity(tableName = "LOG_DATA")
9 | data class LogDataEntity(
10 | @ColumnInfo(name = "action") val action: String,
11 | @ColumnInfo(name = "time_stamp") val timeStamp: String = LocalDateTime.now().toString(),
12 | @ColumnInfo(name = "image") val address: String? = null
13 | ) {
14 | @PrimaryKey(autoGenerate = true)
15 | var id: Int? = null
16 |
17 | fun toLogData(image: String?): LogData.Data =
18 | LogData.Data(
19 | action = this.action,
20 | timeStamp = LocalDateTime.parse(this.timeStamp),
21 | image = image ?: ""
22 | )
23 | }
24 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/data/communication/ClientDataDao.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.data.communication
2 |
3 | import androidx.room.*
4 | import co.netguru.baby.monitor.client.feature.voiceAnalysis.VoiceAnalysisOption
5 | import io.reactivex.Maybe
6 |
7 | @Dao
8 | interface ClientDataDao {
9 |
10 | @Insert(onConflict = OnConflictStrategy.REPLACE)
11 | fun insertClient(data: ClientEntity)
12 |
13 | @Query("SELECT * FROM CLIENT_DATA LIMIT 1")
14 | fun getClientData(): Maybe
15 |
16 | @Query("DELETE FROM CLIENT_DATA")
17 | fun deleteAll()
18 |
19 | @Query("UPDATE CLIENT_DATA SET voiceAnalysisOption = :voiceAnalysisOption WHERE id = 0")
20 | fun updateVoiceAnalysisOption(voiceAnalysisOption: VoiceAnalysisOption)
21 |
22 | @Query("UPDATE CLIENT_DATA SET noiseLevel = :noiseLevel WHERE id = 0")
23 | fun updateNoiseLevel(noiseLevel: Int)
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/data/communication/ClientEntity.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.data.communication
2 |
3 | import androidx.room.ColumnInfo
4 | import androidx.room.Entity
5 | import androidx.room.Index
6 | import androidx.room.PrimaryKey
7 | import co.netguru.baby.monitor.client.feature.noisedetection.NoiseDetector
8 | import co.netguru.baby.monitor.client.feature.voiceAnalysis.VoiceAnalysisOption
9 |
10 | @Entity(
11 | tableName = "CLIENT_DATA",
12 | indices = [Index(value = ["firebase_key"], unique = true)]
13 | )
14 | data class ClientEntity(
15 | @ColumnInfo(name = "address") val address: String,
16 | @ColumnInfo(name = "firebase_key") var firebaseKey: String,
17 | val voiceAnalysisOption: VoiceAnalysisOption = VoiceAnalysisOption.MACHINE_LEARNING,
18 | val noiseLevel: Int = NoiseDetector.DEFAULT_NOISE_LEVEL,
19 | // There is only one parent handled right now
20 | @PrimaryKey val id: Int? = 0
21 | )
22 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/data/communication/SingleEvent.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.data.communication
2 |
3 | data class SingleEvent(
4 | private val event: T,
5 | private var dispatched: Boolean = false
6 | ) {
7 | var data: T? = event
8 | get() = if (dispatched) {
9 | null
10 | } else {
11 | dispatched = true
12 | event
13 | }
14 | set(value) {
15 | dispatched = false
16 | field = value
17 | }
18 |
19 | fun fetchData() = event
20 | }
21 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/data/communication/nsd/DiscoveryStatus.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.data.communication.nsd
2 |
3 | enum class DiscoveryStatus {
4 | STARTED, STOPPED
5 | }
6 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/data/communication/websocket/ClientConnectionStatus.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.data.communication.websocket
2 |
3 |
4 | enum class ClientConnectionStatus {
5 | EMPTY,
6 | CLIENT_CONNECTED,
7 | }
8 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/data/server/CameraState.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.data.server
2 |
3 | data class CameraState(
4 | val previewEnabled: Boolean,
5 | val streamingEnabled: Boolean
6 | )
7 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/data/splash/AppStateHandler.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.data.splash
2 |
3 | import android.content.SharedPreferences
4 | import co.netguru.baby.monitor.client.application.di.ConfigurationPreferencesQualifier
5 | import co.netguru.baby.monitor.client.common.extensions.edit
6 | import javax.inject.Inject
7 |
8 | class AppStateHandler @Inject constructor(
9 | @ConfigurationPreferencesQualifier private val prefs: SharedPreferences
10 | ) {
11 | internal var appState: AppState
12 | get() = AppState.valueOf(
13 | prefs.getString(APP_STATE_KEY, null) ?: AppState.FIRST_OPEN.toString()
14 | )
15 | set(value) {
16 | prefs.edit {
17 | putString(APP_STATE_KEY, value.toString())
18 | }
19 | }
20 |
21 | companion object {
22 | private const val APP_STATE_KEY = "APP_STATE_KEY"
23 | }
24 | }
25 |
26 | enum class AppState {
27 | UNDEFINED, SERVER, CLIENT, FIRST_OPEN
28 | }
29 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/analytics/AnalyticsManager.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.analytics
2 |
3 | import android.app.Activity
4 | import androidx.core.os.bundleOf
5 | import com.google.firebase.analytics.FirebaseAnalytics
6 | import timber.log.Timber
7 |
8 | class AnalyticsManager(
9 | private val firebaseAnalytics: FirebaseAnalytics
10 | ) {
11 | fun setCurrentScreen(activity: Activity, screen: Screen) {
12 | firebaseAnalytics.setCurrentScreen(activity, screen.screenName, null)
13 | Timber.d("$SCREEN ${screen.screenName}")
14 | }
15 |
16 | fun logEvent(event: Event) {
17 | when (event) {
18 | is Event.Simple -> firebaseAnalytics.logEvent(event.eventType.eventName, null)
19 | is Event.ParamEvent -> firebaseAnalytics.logEvent(
20 | event.eventType.eventName,
21 | bundleOf(event.param.first.paramName to event.param.second)
22 | )
23 | }
24 | Timber.d("$EVENT ${event.eventType.eventName}")
25 | }
26 |
27 | fun setUserProperty(userProperty: UserProperty) {
28 | firebaseAnalytics.setUserProperty(userProperty.key, userProperty.value)
29 | }
30 |
31 | companion object {
32 | private const val EVENT = "event"
33 | private const val SCREEN = "screen"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/analytics/Event.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.analytics
2 |
3 | import co.netguru.baby.monitor.client.feature.firebasenotification.NotificationType
4 | import java.util.*
5 |
6 | sealed class Event(open val eventType: EventType) {
7 | data class Simple(override val eventType: EventType) : Event(eventType)
8 | sealed class ParamEvent(
9 | override val eventType: EventType,
10 | val param: Pair
11 | ) : Event(eventType) {
12 | data class NotificationSent(val value: NotificationType) : ParamEvent(
13 | EventType.NOTIFICATION_SENT,
14 | EventParam.TYPE to value.name.toLowerCase(Locale.getDefault())
15 | )
16 |
17 | data class NightMode(val value: Boolean) : ParamEvent(
18 | EventType.NIGHT_MODE,
19 | EventParam.IS_ENABLED to value
20 | )
21 | }
22 | }
23 |
24 | enum class EventType(val eventName: String) {
25 | NOTIFICATION_SENT("notification_sent"),
26 | NOTIFICATION_SNOOZE("notification_snooze"),
27 | NOTIFICATION_OPEN_CAMERA("notification_open_camera"),
28 | RESET_APP("reset_app"),
29 | NIGHT_MODE("night_mode"),
30 | VIDEO_STREAM_CONNECTED("video_stream_connected"),
31 | VIDEO_STREAM_ERROR("video_stream_error")
32 | }
33 |
34 | enum class EventParam(val paramName: String) {
35 | TYPE("type"),
36 | IS_ENABLED("is_enabled")
37 | }
38 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/analytics/Screen.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.analytics
2 |
3 | enum class Screen(val screenName: String) {
4 | SPLASH("Splash"),
5 | ONBOARDING("Onboarding"),
6 | INFO_ABOUT_DEVICES("InfoAboutDevices"),
7 | SPECIFY_DEVICE("SpecifyDevice"),
8 | VOICE_RECORDINGS_SETTING("VoiceRecordingsSetting"),
9 | CONNECT_WIFI("ConnectWifi"),
10 | PERMISSION_CAMERA("PermissionCamera"),
11 | PERMISSION_MICROPHONE("PermissionMicrophone"),
12 | PERMISSION_DENIED("PermissionDenied"),
13 | SETUP_INFORMATION("SetupInformation"),
14 | CHILD_MONITOR("ChildMonitor"),
15 | PARENT_DEVICE_INFO("ParentDeviceInfo"),
16 | SERVICE_DISCOVERY("ServiceDiscovery"),
17 | PAIRING("Pairing"),
18 | CONNECTION_FAILED("ConnectionFailed"),
19 | ALL_DONE("AllDone"),
20 | CLIENT_LIVE_CAMERA("ClientLiveCamera"),
21 | CLIENT_DASHBOARD("ClientDashboard"),
22 | CLIENT_ACTIVITY_LOG("ClientActivityLog")
23 | }
24 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/analytics/UserProperty.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.analytics
2 |
3 | import co.netguru.baby.monitor.client.data.splash.AppState
4 | import co.netguru.baby.monitor.client.feature.voiceAnalysis.VoiceAnalysisOption
5 |
6 | sealed class UserProperty(val value: String, val key: String) {
7 | class VoiceAnalysis(voiceAnalysisOption: VoiceAnalysisOption) :
8 | UserProperty(voiceAnalysisOption.name.toLowerCase(), VOICE_ANALYSIS_KEY)
9 |
10 | class AppStateProperty(appState: AppState) :
11 | UserProperty(appState.name.toLowerCase(), APP_STATE_KEY)
12 |
13 | class NoiseLevel(noiseLevel: Int) :
14 | UserProperty(noiseLevel.toString(), NOISE_LEVEL_KEY)
15 |
16 | companion object {
17 | private const val VOICE_ANALYSIS_KEY = "voice_analysis"
18 | private const val APP_STATE_KEY = "app_state"
19 | private const val NOISE_LEVEL_KEY = "noise_level"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/babynotification/BabyEventActionIntentService.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.babynotification
2 |
3 | import android.app.IntentService
4 | import android.content.Intent
5 | import androidx.core.app.NotificationManagerCompat
6 | import androidx.core.os.bundleOf
7 | import androidx.navigation.NavDeepLinkBuilder
8 | import co.netguru.baby.monitor.client.common.NotificationHandler
9 | import co.netguru.baby.monitor.client.feature.babynotification.BabyMonitorMessagingService.Companion.CRYING_NOTIFICATION_ID
10 | import dagger.android.AndroidInjection
11 | import io.reactivex.disposables.CompositeDisposable
12 | import io.reactivex.rxkotlin.addTo
13 | import javax.inject.Inject
14 |
15 | class BabyEventActionIntentService : IntentService(NAME) {
16 |
17 | @Inject
18 | internal lateinit var snoozeNotificationUseCase: SnoozeNotificationUseCase
19 | @Inject
20 | internal lateinit var openCameraUseCase: OpenCameraUseCase
21 | private val disposables = CompositeDisposable()
22 |
23 | override fun onCreate() {
24 | super.onCreate()
25 | AndroidInjection.inject(this)
26 | }
27 |
28 | override fun onHandleIntent(intent: Intent?) {
29 | NotificationManagerCompat.from(this).cancel(CRYING_NOTIFICATION_ID)
30 |
31 | when (intent?.action) {
32 | NotificationHandler.SHOW_CAMERA_ACTION -> openCameraUseCase
33 | .openLiveClientCamera(
34 | NavDeepLinkBuilder(this),
35 | bundleOf(SHOULD_SHOW_SNOOZE_DIALOG to true)
36 | )
37 | NotificationHandler.SNOOZE_ACTION -> snoozeNotificationUseCase
38 | .snoozeNotifications()
39 | .addTo(disposables)
40 | }
41 | }
42 |
43 | override fun onDestroy() {
44 | super.onDestroy()
45 | disposables.dispose()
46 | }
47 |
48 | companion object {
49 | private const val NAME = "CRYING_ACTION_INTENT_SERVICE"
50 | const val SHOULD_SHOW_SNOOZE_DIALOG = "SHOULD_SHOW_SNOOZE_DIALOG"
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/babynotification/OpenCameraUseCase.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.babynotification
2 |
3 | import android.os.Bundle
4 | import androidx.navigation.NavDeepLinkBuilder
5 | import co.netguru.baby.monitor.client.R
6 | import co.netguru.baby.monitor.client.feature.analytics.AnalyticsManager
7 | import co.netguru.baby.monitor.client.feature.analytics.Event
8 | import co.netguru.baby.monitor.client.feature.analytics.EventType
9 | import co.netguru.baby.monitor.client.feature.client.home.ClientHomeActivity
10 | import javax.inject.Inject
11 |
12 | class OpenCameraUseCase @Inject constructor(
13 | private val analyticsManager: AnalyticsManager
14 | ) {
15 | fun openLiveClientCamera(navDeepLinkBuilder: NavDeepLinkBuilder, snoozeDialogArgument: Bundle) {
16 | analyticsManager.logEvent(Event.Simple(EventType.NOTIFICATION_OPEN_CAMERA))
17 | navDeepLinkBuilder
18 | .setComponentName(ClientHomeActivity::class.java)
19 | .setGraph(R.navigation.client_home_nav_graph)
20 | .setDestination(R.id.clientLiveCamera)
21 | .setArguments(snoozeDialogArgument)
22 | .createTaskStackBuilder()
23 | .startActivities()
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/babynotification/SnoozeNotificationUseCase.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.babynotification
2 |
3 | import co.netguru.baby.monitor.client.data.DataRepository
4 | import co.netguru.baby.monitor.client.feature.analytics.AnalyticsManager
5 | import co.netguru.baby.monitor.client.feature.analytics.Event
6 | import co.netguru.baby.monitor.client.feature.analytics.EventType
7 | import io.reactivex.disposables.Disposable
8 | import io.reactivex.schedulers.Schedulers
9 | import timber.log.Timber
10 | import java.util.concurrent.TimeUnit
11 | import javax.inject.Inject
12 |
13 | class SnoozeNotificationUseCase @Inject constructor(
14 | private val dataRepository: DataRepository,
15 | private val analyticsManager: AnalyticsManager
16 | ) {
17 |
18 | fun snoozeNotifications(): Disposable {
19 | analyticsManager.logEvent(Event.Simple(EventType.NOTIFICATION_SNOOZE))
20 | return dataRepository
21 | .updateChildSnoozeTimestamp(System.currentTimeMillis() + SNOOZE_TIME)
22 | .subscribeOn(Schedulers.io())
23 | .subscribe { Timber.i("Notification snooze timestamp updated") }
24 | }
25 |
26 | companion object {
27 | private val SNOOZE_TIME = TimeUnit.MINUTES.toMillis(5)
28 | const val SNOOZE_DIALOG_TAG = "SNOOZE_DIALOG"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/batterylevel/LowBatteryReceiver.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.batterylevel
2 |
3 | import android.content.BroadcastReceiver
4 | import android.content.Context
5 | import android.content.Intent
6 | import android.content.IntentFilter
7 | import co.netguru.baby.monitor.client.feature.server.ChildMonitorFragmentModule
8 | import javax.inject.Inject
9 |
10 | class LowBatteryReceiver @Inject constructor(
11 | @ChildMonitorFragmentModule.LowBatteryHandler private val lowBatteryHandler: () -> Unit
12 | ) : BroadcastReceiver() {
13 | override fun onReceive(context: Context, intent: Intent) {
14 | if (intent.action != Intent.ACTION_BATTERY_LOW)
15 | throw RuntimeException()
16 | lowBatteryHandler()
17 | }
18 |
19 | fun register(context: Context) {
20 | context.registerReceiver(this, IntentFilter(Intent.ACTION_BATTERY_LOW))
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/batterylevel/NotifyLowBatteryUseCase.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.batterylevel
2 |
3 | import co.netguru.baby.monitor.client.feature.firebasenotification.FirebaseNotificationSender
4 | import co.netguru.baby.monitor.client.feature.firebasenotification.NotificationType
5 | import timber.log.Timber
6 | import javax.inject.Inject
7 |
8 | class NotifyLowBatteryUseCase @Inject constructor(
9 | private val notificationSender: FirebaseNotificationSender
10 | ) {
11 | fun notifyLowBattery(title: String, text: String) =
12 | notificationSender.broadcastNotificationToFcm(
13 | title = title,
14 | text = text,
15 | notificationType = NotificationType.LOW_BATTERY_NOTIFICATION
16 | ).also {
17 | Timber.d("notifyLowBattery($title, $text)")
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/client/configuration/AllDoneFragment.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.client.configuration
2 |
3 | import android.os.Bundle
4 | import android.view.View
5 | import androidx.navigation.fragment.findNavController
6 | import co.netguru.baby.monitor.client.R
7 | import co.netguru.baby.monitor.client.common.base.BaseFragment
8 | import co.netguru.baby.monitor.client.feature.analytics.Screen
9 | import kotlinx.android.synthetic.main.fragment_all_done.*
10 |
11 | class AllDoneFragment : BaseFragment() {
12 | override val layoutResource = R.layout.fragment_all_done
13 | override val screen: Screen = Screen.ALL_DONE
14 |
15 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
16 | super.onViewCreated(view, savedInstanceState)
17 | addDoneCtrl.setOnClickListener {
18 | findNavController().navigate(R.id.allDoneToClientHome)
19 | requireActivity().finish()
20 | }
21 | allDoneBackIv.setOnClickListener {
22 | findNavController().navigateUp()
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/client/configuration/ParentDeviceInfoFragment.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.client.configuration
2 |
3 | import android.os.Bundle
4 | import android.view.View
5 | import androidx.navigation.fragment.findNavController
6 | import co.netguru.baby.monitor.client.R
7 | import co.netguru.baby.monitor.client.common.base.BaseFragment
8 | import co.netguru.baby.monitor.client.feature.analytics.Screen
9 | import kotlinx.android.synthetic.main.fragment_parent_device_info.*
10 |
11 | class ParentDeviceInfoFragment : BaseFragment() {
12 | override val layoutResource = R.layout.fragment_parent_device_info
13 | override val screen: Screen = Screen.PARENT_DEVICE_INFO
14 |
15 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
16 | super.onViewCreated(view, savedInstanceState)
17 | secondAppButtonCtrl.setOnClickListener {
18 | findNavController().navigate(R.id.secondAppInfoToServiceDiscovery)
19 | }
20 |
21 | secondAppInfoBackIv.setOnClickListener {
22 | findNavController().navigateUp()
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/client/home/BackButtonState.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.client.home
2 |
3 | data class BackButtonState(
4 | val shouldBeVisible: Boolean,
5 | val shouldShowSnoozeDialog: Boolean
6 | )
7 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/client/home/RestartAppUseCase.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.client.home
2 |
3 | import android.content.Intent
4 | import androidx.appcompat.app.AppCompatActivity
5 | import io.reactivex.Completable
6 | import javax.inject.Inject
7 |
8 | class RestartAppUseCase @Inject constructor() {
9 |
10 | fun restartApp(activity: AppCompatActivity): Completable = Completable.fromAction {
11 | val intent =
12 | activity.baseContext.packageManager.getLaunchIntentForPackage(activity.baseContext.packageName)
13 | ?.apply {
14 | addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
15 | addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
16 | }
17 |
18 | activity.startActivity(intent)
19 | activity.finish()
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/client/home/SendBabyNameUseCase.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.client.home
2 |
3 | import co.netguru.baby.monitor.client.data.DataRepository
4 | import co.netguru.baby.monitor.client.feature.communication.websocket.Message
5 | import co.netguru.baby.monitor.client.feature.communication.websocket.RxWebSocketClient
6 | import io.reactivex.Completable
7 | import timber.log.Timber
8 | import javax.inject.Inject
9 |
10 | class SendBabyNameUseCase @Inject constructor(
11 | private val dataRepository: DataRepository
12 | ) {
13 | fun streamBabyName(client: RxWebSocketClient): Completable =
14 | dataRepository.getChildData()
15 | .map { it.name.orEmpty() }
16 | .flatMapCompletable { name ->
17 | sendBabyName(client, name)
18 | }
19 |
20 | private fun sendBabyName(client: RxWebSocketClient, babyName: String): Completable =
21 | client.send(Message(babyName = babyName))
22 | .doOnError { Timber.w("Couldn't send baby name: $babyName.") }
23 | .doOnComplete { Timber.d("Baby name sent: $babyName.") }
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/client/home/SendFirebaseTokenUseCase.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.client.home
2 |
3 | import co.netguru.baby.monitor.client.feature.communication.websocket.Message
4 | import co.netguru.baby.monitor.client.feature.communication.websocket.RxWebSocketClient
5 | import co.netguru.baby.monitor.client.feature.firebasenotification.FirebaseInstanceManager
6 | import io.reactivex.Completable
7 | import javax.inject.Inject
8 |
9 | class SendFirebaseTokenUseCase @Inject constructor(
10 | private val firebaseInstanceManager: FirebaseInstanceManager
11 | ) {
12 |
13 | fun sendFirebaseToken(client: RxWebSocketClient): Completable =
14 | firebaseInstanceManager.getFirebaseToken()
15 | .flatMapCompletable { token ->
16 | client.send(Message(pushNotificationsToken = token))
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/communication/communication.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.communication
2 |
3 | const val SERVER_PORT = 10001
4 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/communication/internet/CheckInternetConnectionUseCase.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.communication.internet
2 |
3 | import io.reactivex.Single
4 | import timber.log.Timber
5 | import java.io.IOException
6 | import java.net.InetSocketAddress
7 | import java.net.Socket
8 | import javax.inject.Inject
9 |
10 | class CheckInternetConnectionUseCase @Inject constructor() {
11 |
12 | fun hasInternetConnection(): Single {
13 | return Single.fromCallable {
14 | var isConnected = false
15 | try {
16 | val socket = Socket()
17 | val socketAddress = InetSocketAddress(GOOGLE_DNS_IP, PORT)
18 |
19 | socket.connect(socketAddress, TIMEOUT_MS)
20 | socket.close()
21 |
22 | isConnected = true
23 | } catch (e: IOException) {
24 | Timber.i(e)
25 | }
26 | isConnected
27 | }
28 | }
29 | companion object {
30 | private const val TIMEOUT_MS = 1500
31 | private const val PORT = 53
32 | private const val GOOGLE_DNS_IP = "8.8.8.8"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/communication/nsd/DeviceNameProvider.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.communication.nsd
2 |
3 | import com.jaredrummler.android.device.DeviceName
4 | import javax.inject.Inject
5 |
6 | class DeviceNameProvider @Inject constructor() : IDeviceNameProvider {
7 | override fun getDeviceName(): String = DeviceName.getDeviceName()
8 | }
9 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/communication/nsd/IDeviceNameProvider.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.communication.nsd
2 |
3 | interface IDeviceNameProvider {
4 | fun getDeviceName(): String
5 | }
6 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/communication/nsd/NsdServiceDiffUtil.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.communication.nsd
2 |
3 | import android.net.nsd.NsdServiceInfo
4 | import androidx.recyclerview.widget.DiffUtil
5 |
6 | class NsdServiceDiffUtil : DiffUtil.ItemCallback() {
7 | override fun areItemsTheSame(oldItem: NsdServiceInfo, newItem: NsdServiceInfo): Boolean {
8 | return oldItem == newItem
9 | }
10 |
11 | override fun areContentsTheSame(oldItem: NsdServiceInfo, newItem: NsdServiceInfo): Boolean {
12 | return oldItem.host == newItem.host
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/communication/nsd/NsdServiceExceptions.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.communication.nsd
2 |
3 | import java.lang.RuntimeException
4 |
5 | class ResolveFailedException : RuntimeException("Service resolution failed")
6 | class RegistrationFailedException : RuntimeException("Service registration failed")
7 | class StartDiscoveryFailedException : RuntimeException("Service discovery failed")
8 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/communication/nsd/NsdServicesAdapter.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.communication.nsd
2 |
3 | import android.net.nsd.NsdServiceInfo
4 | import android.view.View
5 | import android.view.ViewGroup
6 | import androidx.recyclerview.widget.ListAdapter
7 | import androidx.recyclerview.widget.RecyclerView
8 | import co.netguru.baby.monitor.client.R
9 |
10 | class NsdServicesAdapter(private val onServiceClick: (nsdServiceInfo: NsdServiceInfo) -> Unit) :
11 | ListAdapter(NsdServiceDiffUtil()) {
12 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ServiceViewHolder {
13 | val view = View.inflate(parent.context, R.layout.found_service_item, null)
14 | val layoutParams = RecyclerView.LayoutParams(
15 | ViewGroup.LayoutParams.MATCH_PARENT,
16 | view.resources.getDimension(R.dimen.nsd_service_item_height).toInt()
17 | )
18 | view.layoutParams = layoutParams
19 | return ServiceViewHolder(view)
20 | .apply {
21 | itemView.setOnClickListener {
22 | onServiceClick.invoke(getItem(adapterPosition))
23 | }
24 | }
25 | }
26 |
27 | override fun onBindViewHolder(holder: ServiceViewHolder, position: Int) {
28 | holder.bind(getItem(position))
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/communication/nsd/NsdState.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.communication.nsd
2 |
3 | import android.net.nsd.NsdServiceInfo
4 |
5 | sealed class NsdState {
6 | data class InProgress(
7 | val serviceInfoList: List
8 | ) : NsdState()
9 |
10 | data class Error(val throwable: Throwable) : NsdState()
11 |
12 | data class Completed(
13 | val serviceInfoList: List
14 | ) : NsdState()
15 | }
16 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/communication/nsd/ServiceViewHolder.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.communication.nsd
2 |
3 | import android.net.nsd.NsdServiceInfo
4 | import android.view.View
5 | import androidx.recyclerview.widget.RecyclerView
6 | import co.netguru.baby.monitor.client.R
7 | import kotlinx.android.synthetic.main.found_service_item.view.*
8 |
9 | class ServiceViewHolder(view: View) : RecyclerView.ViewHolder(view) {
10 |
11 | fun bind(nsdServiceInfo: NsdServiceInfo) {
12 | val deviceName = nsdServiceInfo.serviceName.removeSuffix(NsdServiceManager.SERVICE_NAME)
13 | itemView.deviceName.text =
14 | if (deviceName.isNotEmpty()) deviceName else itemView.resources.getString(
15 | R.string.unknown_device
16 | )
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/communication/pairing/ConnectingDevicesFailedFragment.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.communication.pairing
2 |
3 | import android.os.Bundle
4 | import android.view.View
5 | import androidx.activity.OnBackPressedCallback
6 | import androidx.navigation.fragment.findNavController
7 | import co.netguru.baby.monitor.client.R
8 | import co.netguru.baby.monitor.client.common.base.BaseFragment
9 | import co.netguru.baby.monitor.client.feature.analytics.Screen
10 | import kotlinx.android.synthetic.main.fragment_failed_devices_connecting.*
11 |
12 | class ConnectingDevicesFailedFragment : BaseFragment() {
13 | override val layoutResource = R.layout.fragment_failed_devices_connecting
14 | override val screen: Screen = Screen.CONNECTION_FAILED
15 |
16 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
17 | super.onViewCreated(view, savedInstanceState)
18 | configurationFailedTryAgainButton.setOnClickListener {
19 | findNavController().navigate(R.id.connectionFailedToServiceDiscovery)
20 | }
21 | setupOnBackPressedHandling()
22 | }
23 |
24 | private fun setupOnBackPressedHandling() {
25 | requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, object :
26 | OnBackPressedCallback(true) {
27 | override fun handleOnBackPressed() {
28 | findNavController().navigate(R.id.connectionFailedToServiceDiscovery)
29 | }
30 | })
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/communication/pairing/PairingViewModel.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.communication.pairing
2 |
3 | import androidx.lifecycle.LiveData
4 | import androidx.lifecycle.ViewModel
5 | import co.netguru.baby.monitor.client.common.Randomiser
6 | import java.net.URI
7 | import javax.inject.Inject
8 |
9 | class PairingViewModel @Inject constructor(
10 | private val pairingUseCase: PairingUseCase,
11 | randomiser: Randomiser
12 | ) : ViewModel() {
13 |
14 | internal val pairingCompletedState: LiveData = pairingUseCase.pairingCompletedState
15 | val randomPairingCode =
16 | randomiser.getRandomDigits(NUMBER_OF_DIGITS_PAIRING_CODE).joinToString("")
17 |
18 | fun pair(address: URI) {
19 | pairingUseCase.pair(address, randomPairingCode)
20 | }
21 |
22 | fun cancelPairing() {
23 | pairingUseCase.cancelPairing()
24 | }
25 |
26 | override fun onCleared() {
27 | pairingUseCase.dispose()
28 | super.onCleared()
29 | }
30 |
31 | companion object {
32 | private const val NUMBER_OF_DIGITS_PAIRING_CODE = 4
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/communication/pairing/ServiceDiscoveryViewModel.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.communication.pairing
2 |
3 | import android.net.wifi.WifiManager
4 | import androidx.lifecycle.LiveData
5 | import androidx.lifecycle.ViewModel
6 | import co.netguru.baby.monitor.client.feature.communication.nsd.NsdServiceManager
7 | import co.netguru.baby.monitor.client.feature.communication.nsd.NsdState
8 | import javax.inject.Inject
9 |
10 | class ServiceDiscoveryViewModel @Inject constructor(
11 | private val nsdServiceManager: NsdServiceManager
12 | ) : ViewModel() {
13 |
14 | private var multicastLock: WifiManager.MulticastLock? = null
15 | val nsdStateLiveData: LiveData = nsdServiceManager.nsdStateLiveData
16 |
17 | internal fun discoverNsdService(
18 | wifiManager: WifiManager
19 | ) {
20 | acquireMulticastLock(wifiManager)
21 | nsdServiceManager.discoverService()
22 | }
23 |
24 | private fun acquireMulticastLock(wifiManager: WifiManager) {
25 | // Pixel workaround for discovering services
26 | multicastLock = wifiManager.createMulticastLock(MUTLICAST_LOCK_TAG)
27 | multicastLock?.setReferenceCounted(true)
28 | multicastLock?.acquire()
29 | }
30 |
31 | internal fun stopNsdServiceDiscovery() {
32 | releaseMulticastLock()
33 | nsdServiceManager.stopServiceDiscovery()
34 | }
35 |
36 | private fun releaseMulticastLock() {
37 | multicastLock?.release()
38 | multicastLock = null
39 | }
40 |
41 | override fun onCleared() {
42 | nsdServiceManager.stopServiceDiscovery()
43 | super.onCleared()
44 | }
45 |
46 | companion object {
47 | internal const val MUTLICAST_LOCK_TAG = "multicastLock"
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/communication/webrtc/StreamState.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.communication.webrtc
2 |
3 | import org.webrtc.IceCandidate
4 | import org.webrtc.MediaStream
5 | import org.webrtc.PeerConnection
6 |
7 | sealed class StreamState
8 | data class ConnectionState(val connectionState: RtcConnectionState) : StreamState()
9 | data class GatheringState(val gatheringState: PeerConnection.IceGatheringState?) : StreamState()
10 | data class OnIceCandidatesChange(val iceCandidateState: IceCandidateState) : StreamState()
11 | data class OnAddStream(val mediaStream: MediaStream) : StreamState()
12 |
13 | sealed class IceCandidateState
14 | data class OnIceCandidateAdded(val iceCandidate: IceCandidate) : IceCandidateState()
15 | data class OnIceCandidatesRemoved(val iceCandidates: Array?) : IceCandidateState()
16 |
17 | sealed class RtcConnectionState {
18 | object ConnectionOffer : RtcConnectionState()
19 | object Completed : RtcConnectionState()
20 | object Checking : RtcConnectionState()
21 | object Connected : RtcConnectionState()
22 | object Disconnected : RtcConnectionState()
23 | object Error : RtcConnectionState()
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/communication/webrtc/base/RtcMessageHandler.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.communication.webrtc.base
2 |
3 | import co.netguru.baby.monitor.client.feature.communication.websocket.Message
4 |
5 | interface RtcMessageHandler {
6 | fun handleSdpAnswerMessage(sdpData: Message.SdpData)
7 | fun handleIceCandidateMessage(iceCandidateData: Message.IceCandidateData)
8 | fun handleBabyDeviceSdpError(error: String)
9 | }
10 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/communication/webrtc/observers/DefaultObserver.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.communication.webrtc.observers
2 |
3 | import org.webrtc.*
4 |
5 | open class DefaultObserver : PeerConnection.Observer {
6 | override fun onIceCandidate(iceCandidate: IceCandidate) = Unit
7 | override fun onDataChannel(dataChannel: DataChannel?) = Unit
8 | override fun onIceConnectionReceivingChange(receiving: Boolean) = Unit
9 | override fun onIceConnectionChange(iceConnectionState: PeerConnection.IceConnectionState?) = Unit
10 | override fun onIceGatheringChange(iceGatheringState: PeerConnection.IceGatheringState?) = Unit
11 | override fun onAddStream(mediaStream: MediaStream) = Unit
12 | override fun onSignalingChange(signalingState: PeerConnection.SignalingState?) = Unit
13 | override fun onIceCandidatesRemoved(iceCandidates: Array?) = Unit
14 | override fun onRemoveStream(mediaStream: MediaStream?) = Unit
15 | override fun onRenegotiationNeeded() = Unit
16 | override fun onAddTrack(rtpReceiver: RtpReceiver?, mediaStreams: Array?) = Unit
17 | }
18 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/communication/webrtc/observers/DefaultSdpObserver.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.communication.webrtc.observers
2 |
3 | import org.webrtc.SdpObserver
4 | import org.webrtc.SessionDescription
5 |
6 | open class DefaultSdpObserver(
7 | private val onSetFailure: (error: String?) -> Unit = {},
8 | private val onSetSuccess: () -> Unit = {},
9 | private val onCreateSuccess: (sessionDescription: SessionDescription?) -> Unit = {},
10 | private val onCreateFailure: (error: String?) -> Unit = {}
11 | ) : SdpObserver {
12 |
13 | override fun onSetFailure(error: String?) {
14 | onSetFailure.invoke(error)
15 | }
16 |
17 | override fun onSetSuccess() {
18 | onSetSuccess.invoke()
19 | }
20 | override fun onCreateSuccess(sessionDescription: SessionDescription?) {
21 | onCreateSuccess.invoke(sessionDescription)
22 | }
23 | override fun onCreateFailure(error: String?){
24 | onCreateFailure.invoke(error)
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/communication/websocket/Message.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.communication.websocket
2 |
3 | import com.google.gson.annotations.SerializedName
4 |
5 | data class Message(
6 | // Action
7 | @SerializedName("action") val action: String? = null,
8 | // Rtc
9 | @SerializedName("iceCandidate") val iceCandidate: IceCandidateData? = null,
10 | @SerializedName("errorSDP") val sdpError: String? = null,
11 | @SerializedName("offerSDP") val sdpOffer: SdpData? = null,
12 | @SerializedName("answerSDP") val sdpAnswer: SdpData? = null,
13 | // Config
14 | @SerializedName("baby_name") val babyName: String? = null,
15 | @SerializedName("pushNotificationsToken") val pushNotificationsToken: String? = null,
16 | @SerializedName("voiceAnalysisOption") val voiceAnalysisOption: String? = null,
17 | @SerializedName("noiseLevel") val noiseLevel: Int? = null,
18 | // Pairing
19 | @SerializedName("pairingCode") val pairingCode: String? = null,
20 | @SerializedName("pairingResponse") val pairingApproved: Boolean? = null,
21 | @SerializedName("confirmationId") val confirmationId: String? = null
22 | ) {
23 | data class SdpData(
24 | @SerializedName("sdp") val sdp: String,
25 | @SerializedName("type") val type: String
26 | )
27 |
28 | data class IceCandidateData(
29 | @SerializedName("candidate") val sdp: String,
30 | @SerializedName("id") val sdpMid: String,
31 | @SerializedName("label") val sdpMLineIndex: Int
32 | )
33 |
34 | companion object {
35 | const val RESET_ACTION = "reset"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/communication/websocket/MessageController.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.communication.websocket
2 |
3 | import io.reactivex.Observable
4 |
5 | interface MessageController {
6 | fun sendMessage(message: Message)
7 | fun receivedMessages(): Observable
8 | }
9 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/communication/websocket/MessageParser.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.communication.websocket
2 |
3 | import com.google.gson.Gson
4 | import com.google.gson.JsonParseException
5 | import timber.log.Timber
6 | import javax.inject.Inject
7 |
8 | class MessageParser @Inject constructor(
9 | private val gson: Gson
10 | ) {
11 | fun parseWebSocketMessage(event: RxWebSocketClient.Event.Message): Message? {
12 | return try {
13 | gson.fromJson(event.message, Message::class.java)
14 | } catch (e: JsonParseException) {
15 | Timber.w(e)
16 | null
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/debug/DebugState.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.debug
2 |
3 | data class DebugState(
4 | val notificationInformation: String,
5 | val cryingProbability: Float,
6 | val decibels: Int
7 | )
8 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/firebasenotification/FirebaseInstanceManager.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.firebasenotification
2 |
3 | import com.google.firebase.iid.FirebaseInstanceId
4 | import io.reactivex.Single
5 | import javax.inject.Inject
6 |
7 | class FirebaseInstanceManager @Inject constructor(
8 | private val firebaseInstanceId: FirebaseInstanceId
9 | ) {
10 | fun getFirebaseToken(): Single = Single.create { emitter ->
11 | firebaseInstanceId.instanceId.addOnCompleteListener { task ->
12 | if (task.isSuccessful) {
13 | task.result?.let { emitter.onSuccess(it.token) }
14 | } else {
15 | emitter.onError(task.exception ?: Throwable())
16 | }
17 | }
18 | }
19 |
20 | fun invalidateFirebaseToken() {
21 | firebaseInstanceId.deleteInstanceId()
22 | }
23 |
24 | companion object {
25 | internal const val PUSH_NOTIFICATIONS_KEY = "PUSH_NOTIFICATIONS_KEY"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/firebasenotification/Message.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.firebasenotification
2 |
3 | import com.google.gson.annotations.SerializedName
4 |
5 | data class Message(
6 | @SerializedName("to")
7 | val firebaseToken: String,
8 | @SerializedName("notification")
9 | val notification: Notification? = null,
10 | @SerializedName("data")
11 | val data: Data? = null
12 | )
13 |
14 | data class Notification(
15 | @SerializedName("title")
16 | val title: String,
17 | @SerializedName("text")
18 | val text: String
19 | )
20 |
21 | data class Data(
22 | @SerializedName("title")
23 | val title: String,
24 | @SerializedName("text")
25 | val text: String,
26 | @SerializedName("notification_type")
27 | val type: String
28 | )
29 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/firebasenotification/NotificationType.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.firebasenotification
2 |
3 | enum class NotificationType {
4 | CRY_NOTIFICATION,
5 | LOW_BATTERY_NOTIFICATION,
6 | NOISE_NOTIFICATION,
7 | DEFAULT
8 | }
9 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/noisedetection/NoiseDetector.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.noisedetection
2 |
3 | import co.netguru.baby.monitor.client.feature.voiceAnalysis.AacRecorder.Companion.SAMPLING_RATE
4 | import io.reactivex.Single
5 | import timber.log.Timber
6 | import javax.inject.Inject
7 | import kotlin.math.log10
8 | import kotlin.math.roundToInt
9 | import kotlin.math.sqrt
10 |
11 | class NoiseDetector @Inject constructor() {
12 | fun processData(data: ShortArray) = Single.fromCallable {
13 | var decibels = 0.0
14 | if (data.isNotEmpty()) {
15 | var totalSquared = 0.0
16 | for (soundBit in data) {
17 | totalSquared += (soundBit * soundBit).toDouble()
18 | }
19 |
20 | val quadraticMeanPressure = sqrt(totalSquared / data.size)
21 | decibels = FORMULA_CONSTANT * log10(quadraticMeanPressure)
22 | }
23 | Timber.d("Sound Db: $decibels")
24 | return@fromCallable decibels.roundToInt()
25 | }
26 |
27 | companion object {
28 | internal const val DATA_SIZE = SAMPLING_RATE / 10
29 | const val DEFAULT_NOISE_LEVEL = 65
30 | private const val FORMULA_CONSTANT = 20
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/onboarding/FinishOnboardingUseCase.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.onboarding
2 |
3 | import co.netguru.baby.monitor.client.data.DataRepository
4 | import co.netguru.baby.monitor.client.data.splash.AppState
5 | import javax.inject.Inject
6 |
7 | class FinishOnboardingUseCase @Inject constructor(
8 | private val dataRepository: DataRepository
9 | ) {
10 |
11 | fun finishOnboarding() {
12 | dataRepository.saveConfiguration(AppState.UNDEFINED)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/onboarding/InfoAboutDevicesFragment.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.onboarding
2 |
3 | import android.os.Bundle
4 | import android.text.Html
5 | import android.view.View
6 | import androidx.navigation.fragment.findNavController
7 | import co.netguru.baby.monitor.client.R
8 | import co.netguru.baby.monitor.client.common.base.BaseFragment
9 | import co.netguru.baby.monitor.client.feature.analytics.Screen
10 | import kotlinx.android.synthetic.main.fragment_info_about_devices.*
11 |
12 | class InfoAboutDevicesFragment : BaseFragment() {
13 | override val layoutResource = R.layout.fragment_info_about_devices
14 | override val screen: Screen = Screen.INFO_ABOUT_DEVICES
15 |
16 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
17 | super.onViewCreated(view, savedInstanceState)
18 | specifyDeviceDescriptionTv.text = Html.fromHtml(getString(R.string.sync_description))
19 | specifyDeviceBtn.setOnClickListener {
20 | findNavController().navigate(R.id.infoAboutDevicesToSpecifyDevice)
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/onboarding/OnboardingActivity.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.onboarding
2 |
3 | import android.os.Bundle
4 | import androidx.appcompat.app.AppCompatActivity
5 | import androidx.navigation.findNavController
6 | import co.netguru.baby.monitor.client.R
7 |
8 | class OnboardingActivity : AppCompatActivity() {
9 |
10 | override fun onCreate(savedInstanceState: Bundle?) {
11 | super.onCreate(savedInstanceState)
12 | setContentView(R.layout.activity_onboarding)
13 | }
14 |
15 | override fun onBackPressed() {
16 | val controller = findNavController(R.id.onboardingNavigationHostFragment).currentDestination?.id
17 | ?: 0
18 | if (controller == R.id.permissionMicrophone || controller == R.id.permissionMicrophone ||
19 | controller == R.id.setupInformation) {
20 | findNavController(R.id.onboardingNavigationHostFragment).popBackStack(R.id.connectWiFi, false)
21 | } else if (controller == R.id.allDone) {
22 | findNavController(R.id.onboardingNavigationHostFragment).popBackStack(R.id.parentDeviceInfo, false)
23 | } else {
24 | super.onBackPressed()
25 | }
26 | }
27 |
28 | override fun onSupportNavigateUp() =
29 | findNavController(R.id.onboardingNavigationHostFragment).navigateUp()
30 | }
31 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/onboarding/SpecifyDeviceFragment.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.onboarding
2 |
3 | import android.os.Bundle
4 | import android.view.View
5 | import androidx.navigation.fragment.findNavController
6 | import co.netguru.baby.monitor.client.R
7 | import co.netguru.baby.monitor.client.common.base.BaseFragment
8 | import co.netguru.baby.monitor.client.feature.analytics.Screen
9 | import kotlinx.android.synthetic.main.fragment_specify_device.*
10 |
11 | class SpecifyDeviceFragment : BaseFragment() {
12 | override val layoutResource = R.layout.fragment_specify_device
13 | override val screen: Screen = Screen.SPECIFY_DEVICE
14 |
15 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
16 | super.onViewCreated(view, savedInstanceState)
17 | babyCtl.setOnClickListener {
18 | findNavController().navigate(R.id.specifyDeviceToFeatureD)
19 | }
20 | parentCtl.setOnClickListener {
21 | findNavController().navigate(R.id.specifyDeviceToParentDeviceInfo)
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/onboarding/VoiceRecordingsSettingsFragment.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.onboarding
2 |
3 | import android.os.Bundle
4 | import android.view.View
5 | import androidx.lifecycle.ViewModelProvider
6 | import androidx.lifecycle.ViewModelProviders
7 | import androidx.navigation.fragment.findNavController
8 | import co.netguru.baby.monitor.client.R
9 | import co.netguru.baby.monitor.client.common.base.BaseFragment
10 | import co.netguru.baby.monitor.client.feature.analytics.Screen
11 | import co.netguru.baby.monitor.client.feature.settings.ConfigurationViewModel
12 | import kotlinx.android.synthetic.main.fragment_voice_recordings_setting.*
13 | import javax.inject.Inject
14 |
15 | class VoiceRecordingsSettingsFragment : BaseFragment() {
16 | override val layoutResource = R.layout.fragment_voice_recordings_setting
17 | override val screen: Screen = Screen.VOICE_RECORDINGS_SETTING
18 |
19 | @Inject
20 | lateinit var factory: ViewModelProvider.Factory
21 |
22 | private val viewModel by lazy { ViewModelProviders.of(this, factory)[ConfigurationViewModel::class.java] }
23 |
24 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
25 | super.onViewCreated(view, savedInstanceState)
26 | featureDNextBtn.setOnClickListener {
27 | findNavController().navigate(R.id.featureDToConnecting)
28 | }
29 | featureDSwitch.setOnCheckedChangeListener { buttonView, isChecked ->
30 | viewModel.setUploadEnabled(isChecked)
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/onboarding/baby/PermissionCameraFragment.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.onboarding.baby
2 |
3 | import android.Manifest
4 | import androidx.navigation.fragment.findNavController
5 | import co.netguru.baby.monitor.client.R
6 | import co.netguru.baby.monitor.client.common.base.BaseFragment
7 | import co.netguru.baby.monitor.client.common.extensions.allPermissionsGranted
8 | import co.netguru.baby.monitor.client.feature.analytics.Screen
9 |
10 | class PermissionCameraFragment : BaseFragment() {
11 | override val layoutResource = R.layout.fragment_camera_permission
12 | override val screen: Screen = Screen.PERMISSION_CAMERA
13 |
14 | override fun onResume() {
15 | super.onResume()
16 | requestPermissions(permissions, PERMISSIONS_REQUEST_CODE)
17 | }
18 |
19 | override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) {
20 | super.onRequestPermissionsResult(requestCode, permissions, grantResults)
21 | findNavController().navigate(
22 | if (requireContext().allPermissionsGranted(Companion.permissions)) {
23 | R.id.permissionCameraToPermissionMicrophone
24 | } else {
25 | R.id.permissionCameraToPermissionDenied
26 | }
27 | )
28 | }
29 |
30 | companion object {
31 | private const val PERMISSIONS_REQUEST_CODE = 125
32 |
33 | private val permissions = arrayOf(
34 | Manifest.permission.CAMERA
35 | )
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/onboarding/baby/PermissionDenied.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.onboarding.baby
2 |
3 | import android.os.Bundle
4 | import android.view.View
5 | import androidx.navigation.fragment.findNavController
6 | import co.netguru.baby.monitor.client.R
7 | import co.netguru.baby.monitor.client.common.base.BaseFragment
8 | import co.netguru.baby.monitor.client.feature.analytics.Screen
9 | import kotlinx.android.synthetic.main.fragment_denied_permission.*
10 |
11 | class PermissionDenied : BaseFragment() {
12 | override val layoutResource = R.layout.fragment_denied_permission
13 | override val screen: Screen = Screen.PERMISSION_DENIED
14 |
15 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
16 | super.onViewCreated(view, savedInstanceState)
17 | deniedRetryButtonCtrl.setOnClickListener {
18 | findNavController().popBackStack(R.id.connectWiFi, false)
19 | }
20 | deniedSureButtonCtrl.setOnClickListener {
21 | requireActivity().finish()
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/onboarding/baby/PermissionMicrophoneFragment.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.onboarding.baby
2 |
3 | import android.Manifest
4 | import androidx.navigation.fragment.findNavController
5 | import co.netguru.baby.monitor.client.R
6 | import co.netguru.baby.monitor.client.common.base.BaseFragment
7 | import co.netguru.baby.monitor.client.common.extensions.allPermissionsGranted
8 | import co.netguru.baby.monitor.client.feature.analytics.Screen
9 |
10 | class PermissionMicrophoneFragment : BaseFragment() {
11 | override val layoutResource = R.layout.fragment_microphone_permission
12 | override val screen: Screen = Screen.PERMISSION_MICROPHONE
13 |
14 | override fun onResume() {
15 | super.onResume()
16 | requestPermissions(permissions, PERMISSIONS_REQUEST_CODE)
17 | }
18 |
19 | override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) {
20 | super.onRequestPermissionsResult(requestCode, permissions, grantResults)
21 | findNavController().navigate(
22 | if (requireContext().allPermissionsGranted(Companion.permissions)) {
23 | R.id.permissionMicrophoneToSetupInformation
24 | } else {
25 | R.id.permissionMicrophoneToPermissionDenied
26 | }
27 | )
28 | }
29 |
30 | companion object {
31 | private const val PERMISSIONS_REQUEST_CODE = 126
32 |
33 | private val permissions = arrayOf(
34 | Manifest.permission.RECORD_AUDIO
35 | )
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/onboarding/baby/SetupInformationFragment.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.onboarding.baby
2 |
3 | import android.os.Bundle
4 | import android.view.View
5 | import androidx.navigation.fragment.findNavController
6 | import co.netguru.baby.monitor.client.R
7 | import co.netguru.baby.monitor.client.common.base.BaseFragment
8 | import co.netguru.baby.monitor.client.feature.analytics.Screen
9 | import kotlinx.android.synthetic.main.fragment_connecting_setup_information.*
10 |
11 | class SetupInformationFragment : BaseFragment() {
12 | override val layoutResource = R.layout.fragment_connecting_setup_information
13 | override val screen: Screen = Screen.SETUP_INFORMATION
14 |
15 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
16 | super.onViewCreated(view, savedInstanceState)
17 | connectionInformationMbtn.setOnClickListener {
18 | findNavController().navigate(R.id.setupInformationToServer)
19 | requireActivity().finish()
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/onboarding/baby/WifiReceiver.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.onboarding.baby
2 |
3 | import android.content.BroadcastReceiver
4 | import android.content.Context
5 | import android.content.Intent
6 | import android.content.IntentFilter
7 | import android.net.NetworkInfo
8 | import android.net.wifi.WifiManager
9 | import androidx.lifecycle.MutableLiveData
10 | import co.netguru.baby.monitor.client.data.communication.SingleEvent
11 |
12 | class WifiReceiver : BroadcastReceiver() {
13 |
14 | internal val isWifiConnected = MutableLiveData>()
15 |
16 | override fun onReceive(context: Context?, intent: Intent?) {
17 | val action = intent?.action ?: return
18 | if (action == WifiManager.NETWORK_STATE_CHANGED_ACTION) {
19 | val info: NetworkInfo = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO)
20 | isWifiConnected.postValue(SingleEvent(event = info.isConnected))
21 | }
22 | }
23 |
24 | companion object {
25 | val intentFilter = IntentFilter().apply {
26 | addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION)
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/server/ChildMonitorFragmentModule.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.server
2 |
3 | import co.netguru.baby.monitor.client.application.scope.FragmentScope
4 | import dagger.Module
5 | import dagger.Provides
6 | import javax.inject.Qualifier
7 |
8 | @Module
9 | class ChildMonitorFragmentModule {
10 | @Provides
11 | @LowBatteryHandler
12 | @FragmentScope
13 | fun provideLowBatteryHandler(fragment: ChildMonitorFragment): () -> Unit =
14 | fragment::handleLowBattery
15 |
16 | @Qualifier
17 | annotation class LowBatteryHandler
18 | }
19 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/server/ChildMonitorViewModel.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.server
2 |
3 | import androidx.lifecycle.LiveData
4 | import androidx.lifecycle.MutableLiveData
5 | import androidx.lifecycle.ViewModel
6 | import co.netguru.baby.monitor.client.feature.analytics.AnalyticsManager
7 | import co.netguru.baby.monitor.client.feature.analytics.Event
8 | import co.netguru.baby.monitor.client.feature.batterylevel.NotifyLowBatteryUseCase
9 | import io.reactivex.disposables.CompositeDisposable
10 | import io.reactivex.rxkotlin.plusAssign
11 | import io.reactivex.rxkotlin.subscribeBy
12 | import timber.log.Timber
13 | import javax.inject.Inject
14 |
15 | class ChildMonitorViewModel @Inject constructor(
16 | private val notifyLowBatteryUseCase: NotifyLowBatteryUseCase,
17 | private val analyticsManager: AnalyticsManager
18 | ) : ViewModel() {
19 |
20 | private val disposables = CompositeDisposable()
21 |
22 | private val mutableNightModeStatus = MutableLiveData()
23 | internal val nightModeStatus: LiveData = mutableNightModeStatus
24 |
25 | fun notifyLowBattery(title: String, text: String) {
26 | disposables += notifyLowBatteryUseCase.notifyLowBattery(title = title, text = text)
27 | .subscribeBy(
28 | onComplete = {
29 | Timber.d("Low battery notification posted.")
30 | },
31 | onError = { error ->
32 | Timber.w(error, "Error posting low battery notification.")
33 | }
34 | )
35 | }
36 |
37 | fun switchNightMode() {
38 | val currentStatus = mutableNightModeStatus.value == true
39 | analyticsManager.logEvent(Event.ParamEvent.NightMode(!currentStatus))
40 | mutableNightModeStatus.postValue(!currentStatus)
41 | }
42 |
43 | override fun onCleared() {
44 | disposables.clear()
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/server/ReceiveFirebaseTokenUseCase.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.server
2 |
3 | import co.netguru.baby.monitor.client.data.DataRepository
4 | import co.netguru.baby.monitor.client.data.communication.ClientEntity
5 | import io.reactivex.Completable
6 | import javax.inject.Inject
7 |
8 | class ReceiveFirebaseTokenUseCase @Inject constructor(private val dataRepository: DataRepository) {
9 | fun receiveToken(ipAddress: String, token: String): Completable =
10 | dataRepository.insertClientData(ClientEntity(address = ipAddress, firebaseKey = token))
11 | }
12 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/settings/ChangeState.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.settings
2 |
3 | sealed class ChangeState {
4 | object InProgress : ChangeState()
5 | object Completed : ChangeState()
6 | object Failed : ChangeState()
7 | }
8 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/settings/SeekBarState.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.settings
2 |
3 | sealed class SeekBarState {
4 | data class StartTracking(val initialValue: Int) : SeekBarState()
5 | data class ProgressChange(val progress: Int) : SeekBarState()
6 | data class EndTracking(val endValue: Int) : SeekBarState()
7 | }
8 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/splash/SplashViewModel.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.splash
2 |
3 | import androidx.lifecycle.MutableLiveData
4 | import androidx.lifecycle.ViewModel
5 | import co.netguru.baby.monitor.client.data.DataRepository
6 | import co.netguru.baby.monitor.client.data.splash.AppState
7 | import io.reactivex.Single
8 | import io.reactivex.disposables.CompositeDisposable
9 | import io.reactivex.rxkotlin.addTo
10 | import io.reactivex.rxkotlin.subscribeBy
11 | import io.reactivex.rxkotlin.zipWith
12 | import io.reactivex.schedulers.Schedulers
13 | import timber.log.Timber
14 | import java.util.concurrent.TimeUnit
15 | import javax.inject.Inject
16 |
17 | class SplashViewModel @Inject constructor(
18 | private val dataRepository: DataRepository
19 | ) : ViewModel() {
20 | internal val appState = MutableLiveData()
21 |
22 | private val compositeDisposable = CompositeDisposable()
23 |
24 | internal fun getSavedState() {
25 | Single.timer(DELAY_MILLISECONDS, TimeUnit.MILLISECONDS)
26 | .zipWith(dataRepository.getSavedState())
27 | .subscribeOn(Schedulers.io())
28 | .subscribeBy(
29 | onSuccess = {
30 | appState.postValue(it.second)
31 | },
32 | onError = Timber::e
33 | ).addTo(compositeDisposable)
34 | }
35 |
36 | override fun onCleared() {
37 | compositeDisposable.dispose()
38 | super.onCleared()
39 | }
40 |
41 | companion object {
42 | private const val DELAY_MILLISECONDS = 1000L
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/voiceAnalysis/AacRecorder.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.voiceAnalysis
2 |
3 | import android.media.AudioFormat
4 | import android.media.AudioRecord
5 | import android.media.MediaRecorder
6 | import io.reactivex.Observable
7 | import timber.log.Timber
8 | import javax.inject.Inject
9 |
10 | class AacRecorder @Inject constructor() {
11 |
12 | fun startRecording(): Observable = Observable.create { emitter ->
13 | Timber.i("starting recording")
14 | var shouldStopRecording = false
15 | val bufferSize = findBestBufferSize()
16 | val audioRecord = AudioRecord(
17 | MediaRecorder.AudioSource.MIC,
18 | SAMPLING_RATE,
19 | AudioFormat.CHANNEL_IN_MONO,
20 | AudioFormat.ENCODING_PCM_16BIT,
21 | bufferSize
22 | )
23 | Timber.i("recording started")
24 | val buffer = ByteArray(bufferSize)
25 |
26 | emitter.setCancellable {
27 | shouldStopRecording = true
28 | audioRecord.stop()
29 | audioRecord.release()
30 | Timber.i("release")
31 | }
32 |
33 | while (!shouldStopRecording) {
34 | if (audioRecord.recordingState == AudioRecord.RECORDSTATE_STOPPED) audioRecord.startRecording()
35 | audioRecord.read(buffer, 0, buffer.size)
36 | emitter.onNext(buffer)
37 | }
38 | }
39 |
40 | private fun findBestBufferSize(): Int {
41 | var bufferSize = AudioRecord.getMinBufferSize(
42 | SAMPLING_RATE,
43 | AudioFormat.CHANNEL_IN_MONO,
44 | AudioFormat.ENCODING_PCM_16BIT
45 | ) * 2
46 | if (bufferSize == AudioRecord.ERROR || bufferSize == AudioRecord.ERROR_BAD_VALUE) {
47 | bufferSize = SAMPLING_RATE * 2
48 | }
49 | return bufferSize
50 | }
51 |
52 | companion object {
53 | internal const val SAMPLING_RATE = 44_100
54 | internal const val CHANNELS = 1
55 | internal const val BITS_PER_SAMPLE = 16
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/voiceAnalysis/RecordingData.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.voiceAnalysis
2 |
3 | sealed class RecordingData {
4 | data class MachineLearning(val byteArray: ByteArray, val shortArray: ShortArray) :
5 | RecordingData()
6 |
7 | data class NoiseDetection(val shortArray: ShortArray) : RecordingData()
8 | data class Raw(val byteArray: ByteArray) : RecordingData()
9 | }
10 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/voiceAnalysis/VoiceAnalysisOption.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.voiceAnalysis
2 |
3 | enum class VoiceAnalysisOption {
4 | MACHINE_LEARNING,
5 | NOISE_DETECTION
6 | }
7 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/voiceAnalysis/VoiceAnalysisService.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.voiceAnalysis
2 |
3 | import android.content.Intent
4 | import android.os.Binder
5 | import android.widget.Toast
6 | import androidx.annotation.UiThread
7 | import co.netguru.baby.monitor.client.common.base.BaseServiceWithFacade
8 | import co.netguru.baby.monitor.client.common.base.ServiceFacade
9 | import io.reactivex.Single
10 | import timber.log.Timber
11 |
12 | class VoiceAnalysisService : ServiceFacade,
13 | BaseServiceWithFacade() {
14 |
15 | override fun onBind(intent: Intent?) = VoiceAnalysisBinder()
16 |
17 | @UiThread
18 | fun complain(message: String, error: Throwable) {
19 | Timber.w(error, message)
20 | Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
21 | }
22 |
23 | fun saveDataToFile(rawData: ByteArray): Single {
24 | return WavFileGenerator.saveAudio(
25 | applicationContext,
26 | rawData,
27 | AacRecorder.BITS_PER_SAMPLE,
28 | AacRecorder.CHANNELS,
29 | AacRecorder.SAMPLING_RATE
30 | )
31 | }
32 |
33 | inner class VoiceAnalysisBinder : Binder() {
34 | fun startRecording() {
35 | Timber.i("Start recording")
36 | serviceController.startRecording()
37 | }
38 |
39 | fun stopRecording() {
40 | Timber.i("Stop recording")
41 | serviceController.stopRecording()
42 | }
43 |
44 | fun setVoiceAnalysisOption(voiceAnalysisOption: VoiceAnalysisOption) {
45 | serviceController.voiceAnalysisOption = voiceAnalysisOption
46 | }
47 |
48 | fun setNoiseDetectionLevel(level: Int) {
49 | serviceController.noiseLevel = level
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/co/netguru/baby/monitor/client/feature/voiceAnalysis/VoiceAnalysisUseCase.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.voiceAnalysis
2 |
3 | import co.netguru.baby.monitor.client.data.DataRepository
4 | import co.netguru.baby.monitor.client.feature.communication.websocket.Message
5 | import co.netguru.baby.monitor.client.feature.communication.websocket.RxWebSocketClient
6 | import io.reactivex.Completable
7 | import timber.log.Timber
8 | import javax.inject.Inject
9 |
10 | class VoiceAnalysisUseCase @Inject constructor(
11 | private val dataRepository: DataRepository
12 | ) {
13 | fun sendInitialVoiceAnalysisOption(client: RxWebSocketClient): Completable =
14 | dataRepository.getChildData()
15 | .map { it.voiceAnalysisOption }
16 | .flatMapCompletable { voiceAnalysisOption ->
17 | sendVoiceAnalysisOption(client, voiceAnalysisOption)
18 | }
19 |
20 | private fun sendVoiceAnalysisOption(
21 | client: RxWebSocketClient,
22 | voiceAnalysisOption: VoiceAnalysisOption
23 | ): Completable =
24 | client.send(Message(voiceAnalysisOption = voiceAnalysisOption.name))
25 | .doOnError { Timber.w("Couldn't send option: $voiceAnalysisOption.") }
26 | .doOnComplete { Timber.d("Option sent: $voiceAnalysisOption.") }
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/heart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netguru/baby-monitor-client-android/3c3d8838de7ed8a340ebb79a64f249681669abf2/app/src/main/res/drawable-hdpi/heart.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netguru/baby-monitor-client-android/3c3d8838de7ed8a340ebb79a64f249681669abf2/app/src/main/res/drawable-hdpi/logo.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/made_with_love.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netguru/baby-monitor-client-android/3c3d8838de7ed8a340ebb79a64f249681669abf2/app/src/main/res/drawable-hdpi/made_with_love.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/white_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netguru/baby-monitor-client-android/3c3d8838de7ed8a340ebb79a64f249681669abf2/app/src/main/res/drawable-hdpi/white_logo.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/wifi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netguru/baby-monitor-client-android/3c3d8838de7ed8a340ebb79a64f249681669abf2/app/src/main/res/drawable-hdpi/wifi.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/heart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netguru/baby-monitor-client-android/3c3d8838de7ed8a340ebb79a64f249681669abf2/app/src/main/res/drawable-mdpi/heart.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netguru/baby-monitor-client-android/3c3d8838de7ed8a340ebb79a64f249681669abf2/app/src/main/res/drawable-mdpi/logo.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/made_with_love.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netguru/baby-monitor-client-android/3c3d8838de7ed8a340ebb79a64f249681669abf2/app/src/main/res/drawable-mdpi/made_with_love.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/white_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netguru/baby-monitor-client-android/3c3d8838de7ed8a340ebb79a64f249681669abf2/app/src/main/res/drawable-mdpi/white_logo.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/wifi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netguru/baby-monitor-client-android/3c3d8838de7ed8a340ebb79a64f249681669abf2/app/src/main/res/drawable-mdpi/wifi.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v21/button_purple_outline_to_purple_rounded.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | -
5 |
6 |
-
9 |
10 |
11 |
12 |
13 |
14 | -
15 |
16 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v21/button_rounded_ripple.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
-
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | -
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v21/button_white_outline_to_purple_rounded.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | -
5 |
6 |
-
9 |
10 |
11 |
12 |
13 |
14 |
15 | -
16 |
17 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v21/dark_button_rounded_ripple.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/heart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netguru/baby-monitor-client-android/3c3d8838de7ed8a340ebb79a64f249681669abf2/app/src/main/res/drawable-xhdpi/heart.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netguru/baby-monitor-client-android/3c3d8838de7ed8a340ebb79a64f249681669abf2/app/src/main/res/drawable-xhdpi/logo.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/made_with_love.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netguru/baby-monitor-client-android/3c3d8838de7ed8a340ebb79a64f249681669abf2/app/src/main/res/drawable-xhdpi/made_with_love.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/white_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netguru/baby-monitor-client-android/3c3d8838de7ed8a340ebb79a64f249681669abf2/app/src/main/res/drawable-xhdpi/white_logo.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/wifi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netguru/baby-monitor-client-android/3c3d8838de7ed8a340ebb79a64f249681669abf2/app/src/main/res/drawable-xhdpi/wifi.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/heart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netguru/baby-monitor-client-android/3c3d8838de7ed8a340ebb79a64f249681669abf2/app/src/main/res/drawable-xxhdpi/heart.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netguru/baby-monitor-client-android/3c3d8838de7ed8a340ebb79a64f249681669abf2/app/src/main/res/drawable-xxhdpi/logo.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/made_with_love.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netguru/baby-monitor-client-android/3c3d8838de7ed8a340ebb79a64f249681669abf2/app/src/main/res/drawable-xxhdpi/made_with_love.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/white_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netguru/baby-monitor-client-android/3c3d8838de7ed8a340ebb79a64f249681669abf2/app/src/main/res/drawable-xxhdpi/white_logo.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/wifi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netguru/baby-monitor-client-android/3c3d8838de7ed8a340ebb79a64f249681669abf2/app/src/main/res/drawable-xxhdpi/wifi.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/heart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netguru/baby-monitor-client-android/3c3d8838de7ed8a340ebb79a64f249681669abf2/app/src/main/res/drawable-xxxhdpi/heart.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netguru/baby-monitor-client-android/3c3d8838de7ed8a340ebb79a64f249681669abf2/app/src/main/res/drawable-xxxhdpi/logo.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/made_with_love.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netguru/baby-monitor-client-android/3c3d8838de7ed8a340ebb79a64f249681669abf2/app/src/main/res/drawable-xxxhdpi/made_with_love.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/white_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netguru/baby-monitor-client-android/3c3d8838de7ed8a340ebb79a64f249681669abf2/app/src/main/res/drawable-xxxhdpi/white_logo.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/wifi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netguru/baby-monitor-client-android/3c3d8838de7ed8a340ebb79a64f249681669abf2/app/src/main/res/drawable-xxxhdpi/wifi.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/all_circle_white_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/animated_done.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
13 |
20 |
21 |
22 |
23 |
24 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/baby_logo.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/button_purple_outline_to_purple_rounded.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
7 |
8 |
9 | -
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/button_rounded_ripple.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
7 |
8 |
9 | -
10 |
11 |
12 |
13 |
14 |
15 | -
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/button_white_outline_to_purple_rounded.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
7 |
8 |
9 |
10 | -
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/circle_primary_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/circle_primary_dark_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/dark_button_rounded_ripple.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
7 |
8 |
9 | -
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/download.xml:
--------------------------------------------------------------------------------
1 |
6 |
12 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/failed.xml:
--------------------------------------------------------------------------------
1 |
6 |
12 |
19 |
27 |
35 |
36 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/feature_a_indicator.xml:
--------------------------------------------------------------------------------
1 |
6 |
12 |
20 |
28 |
29 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/feature_b_indicator.xml:
--------------------------------------------------------------------------------
1 |
6 |
14 |
20 |
28 |
29 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/feature_c_indicator.xml:
--------------------------------------------------------------------------------
1 |
6 |
14 |
22 |
28 |
29 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_activity.xml:
--------------------------------------------------------------------------------
1 |
6 |
13 |
21 |
28 |
29 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_arrow_back.xml:
--------------------------------------------------------------------------------
1 |
6 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_arrow_down.xml:
--------------------------------------------------------------------------------
1 |
6 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baby.xml:
--------------------------------------------------------------------------------
1 |
6 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_camera.xml:
--------------------------------------------------------------------------------
1 |
6 |
12 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_close.xml:
--------------------------------------------------------------------------------
1 |
6 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_edit.xml:
--------------------------------------------------------------------------------
1 |
6 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_gear_wheel.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_info.xml:
--------------------------------------------------------------------------------
1 |
6 |
12 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_microphone.xml:
--------------------------------------------------------------------------------
1 |
8 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_night_mode.xml:
--------------------------------------------------------------------------------
1 |
6 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_night_mode_active.xml:
--------------------------------------------------------------------------------
1 |
6 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_select_photo_camera.xml:
--------------------------------------------------------------------------------
1 |
6 |
14 |
22 |
30 |
31 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_select_photo_placeholder.xml:
--------------------------------------------------------------------------------
1 |
6 |
10 |
14 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_sound_max.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_sound_min.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_specify_device_checkmark.xml:
--------------------------------------------------------------------------------
1 |
6 |
14 |
22 |
28 |
29 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/monitoring.xml:
--------------------------------------------------------------------------------
1 |
6 |
12 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/phone_item_icon.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/recycler_divider.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/safety.xml:
--------------------------------------------------------------------------------
1 |
6 |
12 |
18 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/res/font/rubik.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
8 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/font/rubik_bold.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netguru/baby-monitor-client-android/3c3d8838de7ed8a340ebb79a64f249681669abf2/app/src/main/res/font/rubik_bold.otf
--------------------------------------------------------------------------------
/app/src/main/res/font/rubik_light.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netguru/baby-monitor-client-android/3c3d8838de7ed8a340ebb79a64f249681669abf2/app/src/main/res/font/rubik_light.otf
--------------------------------------------------------------------------------
/app/src/main/res/font/rubik_medium.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netguru/baby-monitor-client-android/3c3d8838de7ed8a340ebb79a64f249681669abf2/app/src/main/res/font/rubik_medium.otf
--------------------------------------------------------------------------------
/app/src/main/res/font/rubik_regular.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netguru/baby-monitor-client-android/3c3d8838de7ed8a340ebb79a64f249681669abf2/app/src/main/res/font/rubik_regular.otf
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_onboarding.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_server.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
16 |
17 |
25 |
26 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/debug_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
16 |
17 |
23 |
24 |
30 |
31 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/found_service_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
20 |
21 |
29 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_client_activity_log.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
20 |
21 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_client_live_camera.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
16 |
17 |
26 |
27 |
40 |
41 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_log_activity_end_text.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_log_activity_header.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
20 |
21 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/seek_bar_progress.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
16 |
17 |
22 |
23 |
37 |
38 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/toolbar_default.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
19 |
20 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netguru/baby-monitor-client-android/3c3d8838de7ed8a340ebb79a64f249681669abf2/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netguru/baby-monitor-client-android/3c3d8838de7ed8a340ebb79a64f249681669abf2/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netguru/baby-monitor-client-android/3c3d8838de7ed8a340ebb79a64f249681669abf2/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netguru/baby-monitor-client-android/3c3d8838de7ed8a340ebb79a64f249681669abf2/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netguru/baby-monitor-client-android/3c3d8838de7ed8a340ebb79a64f249681669abf2/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netguru/baby-monitor-client-android/3c3d8838de7ed8a340ebb79a64f249681669abf2/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netguru/baby-monitor-client-android/3c3d8838de7ed8a340ebb79a64f249681669abf2/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netguru/baby-monitor-client-android/3c3d8838de7ed8a340ebb79a64f249681669abf2/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netguru/baby-monitor-client-android/3c3d8838de7ed8a340ebb79a64f249681669abf2/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netguru/baby-monitor-client-android/3c3d8838de7ed8a340ebb79a64f249681669abf2/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/navigation/client_home_nav_graph.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
14 |
17 |
18 |
21 |
22 |
23 |
28 |
29 |
34 |
35 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/app/src/main/res/navigation/server_nav_graph.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/values-v21/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
14 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/values/animation.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 500
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #ffffff
4 | #211e35
5 | #9c8fff
6 |
7 | #4d211e35
8 | #80211e35
9 |
10 | #00000000
11 | #FFFFFF
12 | #4DFFFFFF
13 | #000000
14 | #D1000000
15 |
16 | #FAFAFA
17 |
18 | #7c75f6
19 | #999c8fff
20 | #BDBDBD
21 |
22 | #33FFFFFF
23 |
24 | #211f36
25 |
26 | #33FF99
27 | #9b9b9b
28 | #363253
29 | #F44336
30 |
31 |
--------------------------------------------------------------------------------
/app/src/main/res/values/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #9C8FFF
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/resourfes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | - 10sp
5 | - 11sp
6 | - 12sp
7 | - 13sp
8 | - 14sp
9 | - 15sp
10 | - 16sp
11 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
12 |
13 |
17 |
18 |
26 |
27 |
33 |
34 |
--------------------------------------------------------------------------------
/app/src/release/kotlin/co/netguru/baby/monitor/client/application/DebugMetricsHelper.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.application
2 |
3 | import javax.inject.Inject
4 | import javax.inject.Singleton
5 |
6 | /**
7 | * Helper class that initializes a set of debugging tools
8 | * for the debug build type and register crash manager for release type.
9 | * ## Debug type tools:
10 | * - AndroidDevMetrics
11 | * - Stetho
12 | * - StrictMode
13 | * - LeakCanary
14 | * - Timber
15 | *
16 | * ## Release type tools:
17 | * - CrashManager
18 | */
19 | @Singleton
20 | class DebugMetricsHelper @Inject constructor() {
21 |
22 | internal fun init() {
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/release/kotlin/co/netguru/baby/monitor/client/application/RxJavaErrorHandlerImpl.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.application
2 |
3 | import io.reactivex.exceptions.UndeliverableException
4 | import timber.log.Timber
5 |
6 | class RxJavaErrorHandlerImpl : RxJavaErrorHandler() {
7 |
8 | override fun handleUndeliverableException(undeliverableException: UndeliverableException) {
9 | //TODO - decide whether this should be logged and passed or not to used crash reporter
10 | //often occurring might be indication of some problem in library or our codebase but definitely shouldn't crash the app
11 | //Crashlytics.logException(undeliverableException)
12 | Timber.e(undeliverableException)
13 | }
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/app/src/release/kotlin/co/netguru/baby/monitor/client/feature/debug/DebugModule.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.debug
2 |
3 | import io.reactivex.Observable
4 | import javax.inject.Inject
5 |
6 | @Suppress("unused")
7 | class DebugModule @Inject constructor() {
8 | fun sendCryingProbabilityEvent(cryingProbability: Float) = Unit
9 | fun sendNotificationEvent(notificationInformation: String) = Unit
10 | fun sendSoundEvent(decibels: Int) = Unit
11 | fun debugStateObservable(): Observable = Observable.empty()
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/release/kotlin/co/netguru/baby/monitor/client/feature/debug/DebugNotificationManager.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.debug
2 |
3 | import android.content.Context
4 | import javax.inject.Inject
5 |
6 | class DebugNotificationManager @Inject constructor() {
7 | fun show(context: Context) {}
8 | fun clear(context: Context) {}
9 | }
10 |
--------------------------------------------------------------------------------
/app/src/releasePreprod/kotlin/co/netguru/baby/monitor/client/application/DebugMetricsHelper.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.application
2 |
3 | import timber.log.Timber
4 | import javax.inject.Inject
5 | import javax.inject.Singleton
6 |
7 | /**
8 | * Helper class that initializes a set of debugging tools
9 | * for the debug build type and register crash manager for release type.
10 | * ## Debug type tools:
11 | * - AndroidDevMetrics
12 | * - Stetho
13 | * - StrictMode
14 | * - LeakCanary
15 | * - Timber
16 | *
17 | * ## Release type tools:
18 | * - CrashManager
19 | */
20 | @Singleton
21 | class DebugMetricsHelper @Inject constructor() {
22 |
23 | internal fun init() {
24 | Timber.plant(Timber.DebugTree())
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/releasePreprod/kotlin/co/netguru/baby/monitor/client/application/RxJavaErrorHandlerImpl.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.application
2 |
3 | import io.reactivex.exceptions.UndeliverableException
4 | import timber.log.Timber
5 |
6 | class RxJavaErrorHandlerImpl : RxJavaErrorHandler() {
7 |
8 | override fun handleUndeliverableException(undeliverableException: UndeliverableException) {
9 | //TODO - decide whether this should be logged and passed or not to used crash reporter
10 | //often occurring might be indication of some problem in library or our codebase but definitely shouldn't crash the app
11 | //Crashlytics.logException(undeliverableException)
12 | Timber.e(undeliverableException)
13 | }
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/app/src/test/kotlin/co/netguru/baby/monitor/AnswerWithPair.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor
2 |
3 | import org.mockito.internal.stubbing.defaultanswers.ReturnsEmptyValues
4 | import org.mockito.invocation.InvocationOnMock
5 | import org.mockito.stubbing.Answer
6 |
7 | class AnswerWithPair(
8 | private val clazz: Class<*>,
9 | private val pair: Pair, T>
10 | ) : Answer {
11 | private val delegate = ReturnsEmptyValues()
12 |
13 | @Throws(Throwable::class)
14 | override fun answer(invocation: InvocationOnMock): Any = invocation.apply {
15 | return when (method.returnType) {
16 | clazz -> mock
17 | pair.first -> pair.second
18 | else -> delegate.answer(this)
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/app/src/test/kotlin/co/netguru/baby/monitor/RxSchedulersOverrideRule.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor
2 |
3 | import io.reactivex.android.plugins.RxAndroidPlugins
4 | import io.reactivex.plugins.RxJavaPlugins
5 | import io.reactivex.schedulers.Schedulers
6 | import org.junit.rules.TestRule
7 | import org.junit.runner.Description
8 | import org.junit.runners.model.Statement
9 |
10 | class RxSchedulersOverrideRule : TestRule {
11 |
12 | override fun apply(base: Statement, description: Description): Statement {
13 | return object : Statement() {
14 | @Throws(Throwable::class)
15 | override fun evaluate() {
16 | RxAndroidPlugins.setInitMainThreadSchedulerHandler { Schedulers.trampoline() }
17 | RxJavaPlugins.setComputationSchedulerHandler { Schedulers.trampoline() }
18 | RxJavaPlugins.setIoSchedulerHandler { Schedulers.trampoline() }
19 | RxJavaPlugins.setNewThreadSchedulerHandler { Schedulers.trampoline() }
20 | RxJavaPlugins.setSingleSchedulerHandler { Schedulers.trampoline() }
21 | base.evaluate()
22 |
23 | RxJavaPlugins.reset()
24 | RxAndroidPlugins.reset()
25 | }
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/app/src/test/kotlin/co/netguru/baby/monitor/RxTestSchedulerOverrideRule.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor
2 |
3 | import io.reactivex.android.plugins.RxAndroidPlugins
4 | import io.reactivex.plugins.RxJavaPlugins
5 | import io.reactivex.schedulers.Schedulers
6 | import io.reactivex.schedulers.TestScheduler
7 | import org.junit.rules.TestRule
8 | import org.junit.runner.Description
9 | import org.junit.runners.model.Statement
10 |
11 | /**
12 | * Rule helpful while testing blocking/waiting rxjava code.
13 | * All handlers are set to main thread, Computation scheduler is replaced with [TestScheduler]
14 | * accessible through [testScheduler] property.
15 | */
16 | class RxTestSchedulerOverrideRule : TestRule {
17 | val testScheduler = TestScheduler()
18 |
19 | override fun apply(base: Statement, description: Description): Statement {
20 | return object : Statement() {
21 | @Throws(Throwable::class)
22 | override fun evaluate() {
23 | RxAndroidPlugins.setInitMainThreadSchedulerHandler { Schedulers.trampoline() }
24 | RxJavaPlugins.setComputationSchedulerHandler { testScheduler }
25 | RxJavaPlugins.setIoSchedulerHandler { Schedulers.trampoline() }
26 | RxJavaPlugins.setNewThreadSchedulerHandler { Schedulers.trampoline() }
27 | RxJavaPlugins.setSingleSchedulerHandler { Schedulers.trampoline() }
28 | base.evaluate()
29 |
30 | RxJavaPlugins.reset()
31 | RxAndroidPlugins.reset()
32 | }
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/app/src/test/kotlin/co/netguru/baby/monitor/TestUtils.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor
2 |
3 | import com.nhaarman.mockitokotlin2.inOrder
4 | import com.nhaarman.mockitokotlin2.mock
5 |
6 | object TestUtils {
7 | inline fun mockBuilder(mockResult: R) =
8 | mock(defaultAnswer = AnswerWithPair(
9 | T::class.java,
10 | R::class.java to mockResult
11 | )
12 | )
13 |
14 | fun verifyInOrder(mock: T, body: T.() -> Unit) =
15 | with(inOrder(mock)) { verify(mock).body() }
16 | }
17 |
--------------------------------------------------------------------------------
/app/src/test/kotlin/co/netguru/baby/monitor/client/feature/babycrynotification/SnoozeNotificationUseCaseTest.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.babycrynotification
2 |
3 | import co.netguru.baby.monitor.RxSchedulersOverrideRule
4 | import co.netguru.baby.monitor.client.data.DataRepository
5 | import co.netguru.baby.monitor.client.feature.analytics.AnalyticsManager
6 | import co.netguru.baby.monitor.client.feature.analytics.Event
7 | import co.netguru.baby.monitor.client.feature.analytics.EventType
8 | import co.netguru.baby.monitor.client.feature.babynotification.SnoozeNotificationUseCase
9 | import com.nhaarman.mockitokotlin2.*
10 | import io.reactivex.Completable
11 | import org.junit.Rule
12 | import org.junit.Test
13 |
14 | class SnoozeNotificationUseCaseTest {
15 |
16 | @get:Rule
17 | val schedulersRule = RxSchedulersOverrideRule()
18 |
19 | private val dataRepository: DataRepository = mock {
20 | on { updateChildSnoozeTimestamp(any()) }.doReturn(Completable.complete())
21 | }
22 | private val analyticsManager: AnalyticsManager = mock()
23 | private val snoozeNotificationUseCase =
24 | SnoozeNotificationUseCase(
25 | dataRepository,
26 | analyticsManager
27 | )
28 |
29 | @Test
30 | fun `should update snoozeTimestamp on snoozeNotifications`() {
31 | snoozeNotificationUseCase.snoozeNotifications()
32 |
33 | verify(dataRepository).updateChildSnoozeTimestamp(any())
34 | }
35 |
36 | @Test
37 | fun `should send snoozeNotification event to firebase`() {
38 | snoozeNotificationUseCase.snoozeNotifications()
39 |
40 | verify(analyticsManager).logEvent(check {
41 | it is Event.Simple && it.eventType == EventType.NOTIFICATION_SNOOZE
42 | })
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/app/src/test/kotlin/co/netguru/baby/monitor/client/feature/batterylevel/NotifyLowBatteryUseCaseTest.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.batterylevel
2 |
3 | import co.netguru.baby.monitor.RxSchedulersOverrideRule
4 | import co.netguru.baby.monitor.client.feature.firebasenotification.FirebaseNotificationSender
5 | import co.netguru.baby.monitor.client.feature.firebasenotification.NotificationType
6 | import com.nhaarman.mockitokotlin2.mock
7 | import com.nhaarman.mockitokotlin2.verify
8 | import org.junit.Rule
9 | import org.junit.Test
10 |
11 | class NotifyLowBatteryUseCaseTest {
12 |
13 | @get:Rule
14 | val schedulersRule = RxSchedulersOverrideRule()
15 |
16 | private val notificationSender: FirebaseNotificationSender = mock()
17 | private val notifyLowBatteryUseCase = NotifyLowBatteryUseCase(notificationSender)
18 |
19 | @Test
20 | fun `should send low battery notification on notifyLowBattery`() {
21 | val title = "title"
22 | val text = "text"
23 | notifyLowBatteryUseCase.notifyLowBattery(title, text)
24 |
25 | verify(notificationSender).broadcastNotificationToFcm(
26 | title,
27 | text,
28 | NotificationType.LOW_BATTERY_NOTIFICATION
29 | )
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/src/test/kotlin/co/netguru/baby/monitor/client/feature/client/home/RestartAppUseCaseTest.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.client.home
2 |
3 | import android.content.Context
4 | import android.content.Intent
5 | import android.content.pm.PackageManager
6 | import androidx.appcompat.app.AppCompatActivity
7 | import co.netguru.baby.monitor.RxSchedulersOverrideRule
8 | import com.nhaarman.mockitokotlin2.doReturn
9 | import com.nhaarman.mockitokotlin2.mock
10 | import com.nhaarman.mockitokotlin2.verify
11 | import org.junit.Rule
12 | import org.junit.Test
13 |
14 | class RestartAppUseCaseTest {
15 |
16 | @get:Rule
17 | val schedulersRule = RxSchedulersOverrideRule()
18 |
19 | private val restartAppUseCase: RestartAppUseCase = RestartAppUseCase()
20 | private val intent: Intent = mock()
21 | private val packageName = "packageName"
22 | private val activity: AppCompatActivity = mock {
23 | val context: Context = mock {
24 | val packageManagerMock: PackageManager = mock {
25 | on { getLaunchIntentForPackage(packageName) }.doReturn(intent)
26 | }
27 | on { packageManager }.doReturn(packageManagerMock)
28 | on { packageName }.doReturn(packageName)
29 | }
30 | on { baseContext }.doReturn(context)
31 | }
32 |
33 | @Test
34 | fun `should restart App`() {
35 | restartAppUseCase
36 | .restartApp(activity)
37 | .subscribe()
38 |
39 | verify(activity).startActivity(intent)
40 | verify(activity).finish()
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/app/src/test/kotlin/co/netguru/baby/monitor/client/feature/client/home/SendBabyNameUseCaseTest.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.client.home
2 |
3 | import co.netguru.baby.monitor.RxSchedulersOverrideRule
4 | import co.netguru.baby.monitor.client.data.DataRepository
5 | import co.netguru.baby.monitor.client.data.client.ChildDataEntity
6 | import co.netguru.baby.monitor.client.feature.communication.websocket.RxWebSocketClient
7 | import com.nhaarman.mockitokotlin2.*
8 | import io.reactivex.Completable
9 | import io.reactivex.Maybe
10 | import org.junit.Assert.assertEquals
11 | import org.junit.Rule
12 | import org.junit.Test
13 |
14 | class SendBabyNameUseCaseTest {
15 |
16 | @get:Rule
17 | val schedulersRule = RxSchedulersOverrideRule()
18 |
19 | private val childDataEntity = ChildDataEntity("", name = "name")
20 | private val dataRepository: DataRepository = mock {
21 | on { getChildData() }.doReturn(Maybe.just(childDataEntity))
22 | }
23 | private val rxWebSocketClient: RxWebSocketClient = mock {
24 | on { send(any()) }.doReturn(Completable.complete())
25 | }
26 | private val sendBabyNameUseCase = SendBabyNameUseCase(dataRepository)
27 |
28 | @Test
29 | fun `should send baby name message`() {
30 | sendBabyNameUseCase
31 | .streamBabyName(rxWebSocketClient)
32 | .test()
33 | .assertComplete()
34 |
35 | verify(dataRepository).getChildData()
36 | verify(rxWebSocketClient).send(check {
37 | assertEquals(childDataEntity.name, it.babyName)
38 | })
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/app/src/test/kotlin/co/netguru/baby/monitor/client/feature/client/home/SendFirebaseTokenUseCaseTest.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.client.home
2 |
3 | import co.netguru.baby.monitor.client.feature.communication.websocket.Message
4 | import co.netguru.baby.monitor.client.feature.communication.websocket.RxWebSocketClient
5 | import co.netguru.baby.monitor.client.feature.firebasenotification.FirebaseInstanceManager
6 | import com.google.gson.Gson
7 | import com.nhaarman.mockitokotlin2.*
8 | import io.reactivex.Completable
9 | import io.reactivex.Single
10 | import org.junit.Assert
11 | import org.junit.Test
12 |
13 | class SendFirebaseTokenUseCaseTest {
14 |
15 | private val token = "token"
16 | private val firebaseInstanceManager: FirebaseInstanceManager = mock {
17 | on { getFirebaseToken() }.doReturn(Single.just(token))
18 | }
19 | private val rxWebSocketClient: RxWebSocketClient = mock {
20 | on { send(any()) }.doReturn(Completable.complete())
21 | }
22 | private val gson: Gson = mock {
23 | on { toJson(any()) }.doReturn(token)
24 | }
25 | private val sendFirebaseTokenUseCase = SendFirebaseTokenUseCase(firebaseInstanceManager)
26 |
27 | @Test
28 | fun sendFirebaseToken() {
29 | sendFirebaseTokenUseCase.sendFirebaseToken(rxWebSocketClient)
30 | .test()
31 | .assertComplete()
32 |
33 | verify(rxWebSocketClient).send(check {
34 | Assert.assertTrue(it.pushNotificationsToken?.contains(token) == true)
35 | })
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/src/test/kotlin/co/netguru/baby/monitor/client/feature/communication/pairing/ServiceDiscoveryViewModelTest.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.communication.pairing
2 |
3 | import android.net.wifi.WifiManager
4 | import androidx.arch.core.executor.testing.InstantTaskExecutorRule
5 | import co.netguru.baby.monitor.RxSchedulersOverrideRule
6 | import co.netguru.baby.monitor.client.feature.communication.nsd.NsdServiceManager
7 | import com.nhaarman.mockitokotlin2.doReturn
8 | import com.nhaarman.mockitokotlin2.mock
9 | import com.nhaarman.mockitokotlin2.verify
10 | import org.junit.Rule
11 | import org.junit.Test
12 |
13 | class ServiceDiscoveryViewModelTest {
14 |
15 | @get:Rule
16 | val schedulersRule = RxSchedulersOverrideRule()
17 |
18 | @get:Rule
19 | val instantExecutorRule = InstantTaskExecutorRule()
20 |
21 | private val nsdServiceManager: NsdServiceManager = mock()
22 |
23 | private val multicastLock: WifiManager.MulticastLock = mock()
24 | private val wifiManager: WifiManager = mock {
25 | on { createMulticastLock(ServiceDiscoveryViewModel.MUTLICAST_LOCK_TAG) }.doReturn(
26 | multicastLock
27 | )
28 | }
29 |
30 | private val serviceDiscoveryViewModel = ServiceDiscoveryViewModel(
31 | nsdServiceManager
32 | )
33 |
34 | @Test
35 | fun `should start and stop nsdService`() {
36 | serviceDiscoveryViewModel.discoverNsdService(wifiManager)
37 |
38 | verify(nsdServiceManager).discoverService()
39 |
40 | serviceDiscoveryViewModel.stopNsdServiceDiscovery()
41 |
42 | verify(nsdServiceManager).stopServiceDiscovery()
43 | }
44 |
45 | @Test
46 | fun `should handle Wifi MulticastLock while starting and stopping searching`() {
47 | serviceDiscoveryViewModel.discoverNsdService(wifiManager)
48 |
49 | verify(multicastLock).acquire()
50 | verify(multicastLock).setReferenceCounted(true)
51 |
52 | serviceDiscoveryViewModel.stopNsdServiceDiscovery()
53 |
54 | verify(multicastLock).release()
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/app/src/test/kotlin/co/netguru/baby/monitor/client/feature/onboarding/FinishOnboardingUseCaseTest.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.onboarding
2 |
3 | import co.netguru.baby.monitor.client.data.DataRepository
4 | import co.netguru.baby.monitor.client.data.splash.AppState
5 | import com.nhaarman.mockitokotlin2.mock
6 | import com.nhaarman.mockitokotlin2.verify
7 | import org.junit.Test
8 |
9 | class FinishOnboardingUseCaseTest {
10 |
11 | private val dataRepository = mock()
12 | private val finishOnboardingUseCase = FinishOnboardingUseCase(dataRepository)
13 |
14 | @Test
15 | fun `should save undefined state`() {
16 | finishOnboardingUseCase.finishOnboarding()
17 |
18 | verify(dataRepository).saveConfiguration(AppState.UNDEFINED)
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/app/src/test/kotlin/co/netguru/baby/monitor/client/feature/server/ReceiveFirebaseTokenUseCaseTest.kt:
--------------------------------------------------------------------------------
1 | package co.netguru.baby.monitor.client.feature.server
2 |
3 | import co.netguru.baby.monitor.client.data.DataRepository
4 | import com.nhaarman.mockitokotlin2.check
5 | import com.nhaarman.mockitokotlin2.mock
6 | import com.nhaarman.mockitokotlin2.verify
7 | import org.junit.Assert.assertEquals
8 | import org.junit.Test
9 |
10 | class ReceiveFirebaseTokenUseCaseTest {
11 |
12 | private val dataRepository: DataRepository = mock()
13 | private val receiveFirebaseTokenUseCase = ReceiveFirebaseTokenUseCase(dataRepository)
14 |
15 | @Test
16 | fun `should save received token in dataRepository`() {
17 | val address = "address"
18 | val token = "token"
19 | receiveFirebaseTokenUseCase.receiveToken(address, token)
20 |
21 | verify(dataRepository).insertClientData(check {
22 | assertEquals(address, it.address)
23 | assertEquals(token, it.firebaseKey)
24 | })
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker:
--------------------------------------------------------------------------------
1 | mock-maker-inline
--------------------------------------------------------------------------------
/buildsystem/secrets.gradle:
--------------------------------------------------------------------------------
1 | static def varToCamelCase(String envVarName) {
2 | envVarName.split('_').collect { it -> it.toLowerCase().capitalize() }.join()
3 | }
4 |
5 | ext {
6 | if (isBitrise) {
7 | keyProperty = { var ->
8 | prop = System.getenv(var)
9 | if (prop == null || prop.isEmpty()) {
10 | if (bitrise.cli.isPr) {
11 | prop = "undefined"
12 | }
13 | else {
14 | throw new MissingPropertyException("Missing environment variable $var")
15 | }
16 | }
17 | prop
18 | }
19 | } else {
20 | keyConfigPath = "${projectDir.path}/secret.properties"
21 | File keyConfigFile = file(keyConfigPath)
22 | keyProps = new Properties();
23 |
24 | if (keyConfigFile.exists()) {
25 | keyProps.load(keyConfigFile.newInputStream())
26 | } else {
27 | throw new FileNotFoundException("File $keyConfigPath not found")
28 | }
29 |
30 | keyProperty = { var ->
31 | propNameCase = varToCamelCase(var)
32 | prop = keyProps[propNameCase]
33 | if (prop == null || prop.toString().isEmpty())
34 | throw new MissingPropertyException("Missing property $propNameCase")
35 | prop
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/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 | android.enableJetifier=true
10 | android.useAndroidX=true
11 | org.gradle.jvmargs=-Xmx1536m
12 | android.enableR8=true
13 | # When configured, Gradle will run in incubating parallel mode.
14 | # This option should only be used with decoupled projects. More details, visit
15 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
16 | # org.gradle.parallel=true
17 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netguru/baby-monitor-client-android/3c3d8838de7ed8a340ebb79a64f249681669abf2/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Mar 29 11:22:03 CET 2019
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
7 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------