├── .github
└── workflows
│ └── android.yml
├── .gitignore
├── LICENSE
├── README.md
├── app-analytics-fake
├── build.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ └── java
│ └── xyz
│ └── aprildown
│ └── timer
│ └── app
│ └── analytics
│ ├── AnalyticsModule.kt
│ └── AppTrackerImpl.kt
├── app-backup
├── build.gradle
└── src
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── xyz
│ │ │ └── aprildown
│ │ │ └── timer
│ │ │ └── app
│ │ │ └── backup
│ │ │ ├── AppPreferencesProviderImpl.kt
│ │ │ ├── BackupComposables.kt
│ │ │ ├── BackupFragment.kt
│ │ │ ├── BaseBackupViewModel.kt
│ │ │ ├── CloudBackupPreference.kt
│ │ │ ├── ExportFragment.kt
│ │ │ ├── ExportViewModel.kt
│ │ │ ├── ImportFragment.kt
│ │ │ ├── ImportViewModel.kt
│ │ │ └── SafIntentSafeBelt.kt
│ └── res
│ │ ├── layout
│ │ └── layout_cloud_backup_widget.xml
│ │ └── xml
│ │ └── pref_backup.xml
│ └── test
│ └── java
│ └── xyz
│ └── aprildown
│ └── timer
│ └── app
│ └── backup
│ ├── BaseBackupViewModelTest.kt
│ ├── ExportViewModelTest.kt
│ └── ImportViewModelTest.kt
├── app-base
├── build.gradle
└── src
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── xyz
│ │ │ └── aprildown
│ │ │ └── timer
│ │ │ └── app
│ │ │ └── base
│ │ │ ├── data
│ │ │ ├── DarkTheme.kt
│ │ │ ├── FlavorData.kt
│ │ │ ├── FloatingWindowPip.kt
│ │ │ ├── PreferenceData.kt
│ │ │ └── ShowcaseData.kt
│ │ │ ├── media
│ │ │ ├── AsyncRingtonePlayer.kt
│ │ │ ├── AudioFocusManager.kt
│ │ │ ├── Beeper.kt
│ │ │ ├── MediaMetadataUtils.kt
│ │ │ ├── RingtonePreviewKlaxon.kt
│ │ │ ├── Torch.kt
│ │ │ └── VibrateHelper.kt
│ │ │ ├── ui
│ │ │ ├── AppNavigator.kt
│ │ │ ├── AppTheme.kt
│ │ │ ├── BaseActivity.kt
│ │ │ ├── BasePreferenceFragmentCompat.kt
│ │ │ ├── DynamicTheme.kt
│ │ │ ├── EdgeToEdgeUtils.kt
│ │ │ ├── FlavorUiInjector.kt
│ │ │ ├── ListEmptyView.kt
│ │ │ ├── MainCallback.kt
│ │ │ ├── SpecialItemTouchHelperCallback.kt
│ │ │ └── StepUpdater.kt
│ │ │ ├── utils
│ │ │ ├── AppInfoNotificationManager.kt
│ │ │ ├── AppThemeUtils.kt
│ │ │ ├── LogToFileTree.kt
│ │ │ ├── NavigationUtils.kt
│ │ │ ├── ScreenWakeLock.kt
│ │ │ ├── ShortcutHelper.kt
│ │ │ ├── TimeConverter.kt
│ │ │ ├── UiNameTranslators.kt
│ │ │ ├── ViewUtils.kt
│ │ │ ├── WebsiteOpener.kt
│ │ │ └── WeekdaysFormatter.kt
│ │ │ └── widgets
│ │ │ └── TimePickerFix.kt
│ └── res
│ │ ├── anim
│ │ ├── close_enter.xml
│ │ ├── close_exit.xml
│ │ ├── ic_anim_pause_play_interpolator_0.xml
│ │ ├── ic_anim_pause_play_interpolator_1.xml
│ │ ├── ic_anim_play_pause_interpolator_0.xml
│ │ ├── ic_anim_play_pause_interpolator_1.xml
│ │ ├── ic_anim_ringing.xml
│ │ ├── open_enter.xml
│ │ ├── open_exit.xml
│ │ ├── slide_in_bottom.xml
│ │ ├── slide_in_left.xml
│ │ ├── slide_in_right.xml
│ │ ├── slide_out_bottom.xml
│ │ ├── slide_out_left.xml
│ │ └── slide_out_right.xml
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ ├── background_step_number.xml
│ │ ├── ic_ad_off.xml
│ │ ├── ic_add.xml
│ │ ├── ic_anim_arrow_right_indicator.xml
│ │ ├── ic_anim_pause.xml
│ │ ├── ic_anim_pause_to_play.xml
│ │ ├── ic_anim_play.xml
│ │ ├── ic_anim_play_to_pause.xml
│ │ ├── ic_anim_ring.xml
│ │ ├── ic_anim_ringing.xml
│ │ ├── ic_arrow_down.xml
│ │ ├── ic_arrow_left_indicator.xml
│ │ ├── ic_arrow_up.xml
│ │ ├── ic_back.xml
│ │ ├── ic_backup.xml
│ │ ├── ic_beep.xml
│ │ ├── ic_calendar_more_events.xml
│ │ ├── ic_check.xml
│ │ ├── ic_count.xml
│ │ ├── ic_cross.xml
│ │ ├── ic_delete.xml
│ │ ├── ic_dewey_reed.xml
│ │ ├── ic_edit.xml
│ │ ├── ic_exit.xml
│ │ ├── ic_expand.xml
│ │ ├── ic_flashlight.xml
│ │ ├── ic_grid_view.xml
│ │ ├── ic_half.xml
│ │ ├── ic_halt.xml
│ │ ├── ic_heart.xml
│ │ ├── ic_image.xml
│ │ ├── ic_launcher_background.xml
│ │ ├── ic_left.xml
│ │ ├── ic_list_view.xml
│ │ ├── ic_locked.xml
│ │ ├── ic_menu.xml
│ │ ├── ic_minus.xml
│ │ ├── ic_music.xml
│ │ ├── ic_notification.xml
│ │ ├── ic_overflow.xml
│ │ ├── ic_pause.xml
│ │ ├── ic_plus_one.xml
│ │ ├── ic_record_predefined_time.xml
│ │ ├── ic_scheduler.xml
│ │ ├── ic_screen.xml
│ │ ├── ic_settings.xml
│ │ ├── ic_sort.xml
│ │ ├── ic_start.xml
│ │ ├── ic_stat.xml
│ │ ├── ic_stop.xml
│ │ ├── ic_time_panel_elapsed.xml
│ │ ├── ic_time_panel_remaining.xml
│ │ ├── ic_time_panel_step_end_time.xml
│ │ ├── ic_time_panel_timer_end_time.xml
│ │ ├── ic_timer.xml
│ │ ├── ic_timer_duration.xml
│ │ ├── ic_unlocked.xml
│ │ ├── ic_vibration.xml
│ │ ├── ic_voice.xml
│ │ ├── ic_warning.xml
│ │ ├── ic_watch.xml
│ │ ├── settings_about.xml
│ │ ├── settings_audio_focus.xml
│ │ ├── settings_audio_type.xml
│ │ ├── settings_brightness.xml
│ │ ├── settings_cloud_backup.xml
│ │ ├── settings_code.xml
│ │ ├── settings_count.xml
│ │ ├── settings_customize.xml
│ │ ├── settings_day_night.xml
│ │ ├── settings_email.xml
│ │ ├── settings_export.xml
│ │ ├── settings_file.xml
│ │ ├── settings_google_play.xml
│ │ ├── settings_help.xml
│ │ ├── settings_import.xml
│ │ ├── settings_material_you.xml
│ │ ├── settings_media_style_notification.xml
│ │ ├── settings_notifications.xml
│ │ ├── settings_phone_call.xml
│ │ ├── settings_pip.xml
│ │ ├── settings_plus_time.xml
│ │ ├── settings_premium.xml
│ │ ├── settings_rate.xml
│ │ ├── settings_share.xml
│ │ ├── settings_system_settings.xml
│ │ ├── settings_theme.xml
│ │ ├── settings_time_duration.xml
│ │ ├── settings_tips_and_tricks.xml
│ │ ├── settings_tts_bakery.xml
│ │ ├── settings_tutorial.xml
│ │ ├── settings_tweak_time.xml
│ │ ├── settings_voice.xml
│ │ ├── settings_volume_on.xml
│ │ ├── settings_week_start.xml
│ │ └── shortcut_timer.xml
│ │ ├── font
│ │ └── digital_numbers_colon.ttf
│ │ ├── layout
│ │ ├── dialog_time_picker_dialog_fix.xml
│ │ ├── view_list_empty_view.xml
│ │ ├── view_time_picker_fix_edit.xml
│ │ ├── view_time_picker_fix_normal_clock.xml
│ │ └── view_time_picker_fix_normal_spinner.xml
│ │ ├── mipmap-anydpi-v26
│ │ └── app_icon_adaptive.xml
│ │ ├── mipmap-hdpi
│ │ ├── app_icon_round.png
│ │ └── app_icon_square.png
│ │ ├── mipmap-mdpi
│ │ ├── app_icon_round.png
│ │ └── app_icon_square.png
│ │ ├── mipmap-xhdpi
│ │ ├── app_icon_round.png
│ │ └── app_icon_square.png
│ │ ├── mipmap-xxhdpi
│ │ ├── app_icon_round.png
│ │ └── app_icon_square.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── app_icon_round.png
│ │ └── app_icon_square.png
│ │ ├── raw
│ │ └── default_ringtone.mp3
│ │ ├── values-anydpi-v26
│ │ └── drawables.xml
│ │ ├── values-de
│ │ └── strings.xml
│ │ ├── values-es
│ │ └── strings.xml
│ │ ├── values-fr
│ │ └── strings.xml
│ │ ├── values-it
│ │ └── strings.xml
│ │ ├── values-nb-rNO
│ │ └── strings.xml
│ │ ├── values-night
│ │ └── styles.xml
│ │ ├── values-nl
│ │ └── strings.xml
│ │ ├── values-pt
│ │ └── strings.xml
│ │ ├── values-ru
│ │ └── strings.xml
│ │ ├── values-ta
│ │ └── strings.xml
│ │ ├── values-zh-rCN
│ │ └── strings.xml
│ │ ├── values-zh-rHK
│ │ └── strings.xml
│ │ ├── values-zh-rTW
│ │ └── strings.xml
│ │ └── values
│ │ ├── attrs.xml
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── drawables.xml
│ │ ├── ids.xml
│ │ ├── strings.xml
│ │ ├── styles.xml
│ │ └── themes.xml
│ └── test
│ └── java
│ └── xyz
│ └── aprildown
│ └── timer
│ └── app
│ └── base
│ └── utils
│ └── WeekdaysFormatterTest.kt
├── app-intro
├── build.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── xyz
│ │ └── aprildown
│ │ └── timer
│ │ └── app
│ │ └── intro
│ │ ├── Instruction.kt
│ │ ├── InstructionIndicators.kt
│ │ ├── IntroActivity.kt
│ │ ├── IntroPanelView.kt
│ │ └── start
│ │ ├── InstructionViews.kt
│ │ ├── Insturctions.kt
│ │ ├── StartEditInstructionView.kt
│ │ ├── StartListInstructionView.kt
│ │ └── StartRunInstructionView.kt
│ └── res
│ ├── drawable-land
│ └── gradient_intro_panel_view_top.xml
│ ├── drawable
│ └── gradient_intro_panel_view_top.xml
│ ├── layout-land
│ └── activity_intro.xml
│ ├── layout
│ ├── activity_intro.xml
│ ├── layout_intro_start_edit.xml
│ ├── layout_intro_start_list.xml
│ ├── layout_intro_start_run.xml
│ └── view_intro_panel.xml
│ ├── menu
│ └── intro_edit.xml
│ ├── values
│ ├── dimens.xml
│ └── ids.xml
│ ├── xml-land
│ └── activity_intro_scene.xml
│ └── xml
│ └── activity_intro_scene.xml
├── app-scheduler
├── build.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── xyz
│ │ └── aprildown
│ │ └── timer
│ │ └── app
│ │ └── scheduler
│ │ ├── EditSchedulerFragment.kt
│ │ ├── SchedulerAdapter.kt
│ │ ├── SchedulerFragment.kt
│ │ └── VisibleScheduler.kt
│ └── res
│ ├── layout
│ ├── fragment_edit_scheduler.xml
│ ├── fragment_scheduler.xml
│ └── item_scheduler.xml
│ └── menu
│ └── edit_scheduler.xml
├── app-settings
├── build.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── xyz
│ │ └── aprildown
│ │ └── timer
│ │ └── app
│ │ └── settings
│ │ ├── BakedCountPreference.kt
│ │ ├── LogFragment.kt
│ │ ├── PhoneCallPreference.kt
│ │ ├── SettingsFragment.kt
│ │ └── theme
│ │ ├── CustomThemeDialog.kt
│ │ └── ThemeFragment.kt
│ └── res
│ ├── drawable
│ ├── theme_bg_round_corner_line.xml
│ └── theme_white_ripple.xml
│ ├── layout
│ ├── dialog_custom_theme.xml
│ ├── fragment_log.xml
│ ├── item_theme_color.xml
│ ├── item_theme_group.xml
│ ├── layout_baked_count_widget.xml
│ └── widget_color_swatch.xml
│ ├── values-night
│ └── colors.xml
│ ├── values
│ └── colors.xml
│ └── xml
│ └── pref_settings.xml
├── app-tasker
├── build.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── xyz
│ │ └── aprildown
│ │ └── timer
│ │ └── app
│ │ └── tasker
│ │ ├── BroadcastReceiverActionTweak.kt
│ │ ├── Constants.kt
│ │ ├── TaskerEditActivity.kt
│ │ ├── TaskerEditViewModel.kt
│ │ ├── TaskerModule.kt
│ │ ├── TaskerRunPresenter.kt
│ │ └── TaskerTimerEventActivity.kt
│ └── res
│ ├── layout
│ └── activity_tasker_edit.xml
│ └── menu
│ └── tasker_edit.xml
├── app-timer-edit
├── build.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── xyz
│ │ └── aprildown
│ │ └── timer
│ │ └── app
│ │ └── timer
│ │ └── edit
│ │ ├── BehaviourSettingsView.kt
│ │ ├── EditActivity.kt
│ │ ├── EditActivityMoreDialog.kt
│ │ ├── EditableFooter.kt
│ │ ├── EditableGroup.kt
│ │ ├── EditableGroupEnd.kt
│ │ ├── EditableStep.kt
│ │ ├── RingtonePickerActivity.kt
│ │ ├── SampleTimers.kt
│ │ ├── StepInfoView.kt
│ │ ├── StepTouchHelper.kt
│ │ ├── UpdateStepDialog.kt
│ │ ├── media
│ │ ├── BeepDialog.kt
│ │ ├── HalfDialog.kt
│ │ ├── VibrationDialog.kt
│ │ └── VoiceDialog.kt
│ │ ├── utils
│ │ └── SaveInstanceHelper.kt
│ │ └── voice
│ │ ├── ArrayAdapter.java
│ │ ├── VoiceVariableContentView.kt
│ │ ├── VoiceVariableDialog.kt
│ │ └── VoiceVariableTableView.kt
│ └── res
│ ├── color
│ └── voice_variable_chip_stroke.xml
│ ├── drawable
│ ├── background_step_info.xml
│ └── voice_variable_table_divider.xml
│ ├── layout-land
│ └── layout_voice_variable_content.xml
│ ├── layout-sw600dp
│ └── dialog_voice_variable.xml
│ ├── layout
│ ├── activity_edit_timer.xml
│ ├── activity_ringtone_picker.xml
│ ├── dialog_edit_more.xml
│ ├── dialog_half_option.xml
│ ├── dialog_update_step.xml
│ ├── dialog_vibration_count.xml
│ ├── dialog_vibration_pattern.xml
│ ├── dialog_voice_content.xml
│ ├── dialog_voice_variable.xml
│ ├── item_edit_group.xml
│ ├── item_edit_group_end.xml
│ ├── item_edit_step.xml
│ ├── layout_edit_add_buttons.xml
│ ├── layout_voice_variable.xml
│ ├── layout_voice_variable_content.xml
│ ├── layout_voice_variable_dialog_buttons.xml
│ ├── layout_voice_variable_table.xml
│ ├── layout_voice_variable_usage.xml
│ ├── list_item_voice_variable_auto_completion.xml
│ └── view_step_info.xml
│ ├── menu
│ └── edit.xml
│ ├── values-night
│ └── colors.xml
│ └── values
│ ├── colors.xml
│ ├── dimens.xml
│ └── styles.xml
├── app-timer-list
├── build.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── xyz
│ │ └── aprildown
│ │ └── timer
│ │ └── app
│ │ └── timer
│ │ └── list
│ │ ├── CollapsedViewHolder.kt
│ │ ├── ExpandedViewHolder.kt
│ │ ├── FolderToolbar.kt
│ │ ├── MutableTimerItem.kt
│ │ ├── TimerAdapter.kt
│ │ ├── TimerFragment.kt
│ │ ├── TimerPicker.kt
│ │ └── record
│ │ ├── CalendarEventView.kt
│ │ ├── RecordCalendarFragment.kt
│ │ ├── RecordFragment.kt
│ │ ├── RecordOverviewFragment.kt
│ │ ├── RecordTimelineFragment.kt
│ │ ├── RecordTimelineMarker.kt
│ │ ├── RecordTimersButton.kt
│ │ └── RecordViewUtils.kt
│ └── res
│ ├── drawable
│ └── background_record_calendar_circle.xml
│ ├── layout-land
│ └── fragment_record.xml
│ ├── layout
│ ├── fragment_record.xml
│ ├── fragment_record_calendar.xml
│ ├── fragment_record_overview.xml
│ ├── fragment_record_timeline.xml
│ ├── fragment_timer.xml
│ ├── fragment_timer_picker.xml
│ ├── list_item_calendar_day_event.xml
│ ├── list_item_record_calendar.xml
│ ├── list_item_record_calendar_legend.xml
│ ├── list_item_timer_collapsed.xml
│ ├── list_item_timer_collapsed_grid.xml
│ ├── list_item_timer_expanded.xml
│ ├── list_item_timer_expanded_gird.xml
│ ├── list_item_timer_picker_folder.xml
│ ├── list_item_timer_picker_timer.xml
│ ├── view_calendar_event.xml
│ ├── view_folder_toolbar.xml
│ ├── view_record_timeline_marker.xml
│ ├── view_tip_missed_timer.xml
│ └── view_tip_whitelist.xml
│ ├── menu
│ ├── record_predefined_time.xml
│ └── timer.xml
│ ├── values-w480dp
│ └── integer.xml
│ ├── values-w600dp
│ └── integer.xml
│ ├── values-w720dp
│ └── integer.xml
│ ├── values-w840dp
│ └── integer.xml
│ └── values
│ ├── dimens.xml
│ └── integer.xml
├── app-timer-one
├── build.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── xyz
│ │ └── aprildown
│ │ └── timer
│ │ └── app
│ │ └── timer
│ │ └── one
│ │ ├── BaseOneFragment.kt
│ │ ├── FiveActionsView.kt
│ │ ├── OneActivity.kt
│ │ ├── OneFragment.kt
│ │ ├── PipHelper.kt
│ │ ├── float
│ │ ├── FloatViewTouchListener.kt
│ │ ├── Floater.kt
│ │ ├── FloatingTimer.kt
│ │ └── FloatingWindowPipFragment.kt
│ │ ├── layout
│ │ ├── OneLayoutFragment.kt
│ │ ├── TimePanelDialog.kt
│ │ ├── TweakTimeLayout.kt
│ │ └── one
│ │ │ └── OneLayoutOneFragment.kt
│ │ └── step
│ │ ├── OffsetEndsItemDecoration.kt
│ │ ├── StepListListeners.kt
│ │ ├── StepListView.kt
│ │ ├── VisibleGroup.kt
│ │ ├── VisibleGroupEnd.kt
│ │ └── VisibleStep.kt
│ └── res
│ ├── layout-land
│ └── fragment_one.xml
│ ├── layout
│ ├── activity_one.xml
│ ├── dialog_one_create_shortcut.xml
│ ├── dialog_time_panel_picker.xml
│ ├── fragment_floating_window_pip.xml
│ ├── fragment_one.xml
│ ├── fragment_one_layout.xml
│ ├── item_step_group.xml
│ ├── item_step_group_end.xml
│ ├── item_step_step.xml
│ ├── layout_five_actions.xml
│ ├── layout_floating_window.xml
│ ├── layout_one_settings_one.xml
│ ├── layout_one_widget_time_panel.xml
│ ├── layout_one_widget_timing_bar.xml
│ ├── layout_pip.xml
│ ├── layout_tweak_time_main_button.xml
│ └── layout_tweak_time_tweak_button.xml
│ ├── menu
│ └── one_tweak_time.xml
│ ├── navigation
│ └── nav_graph_one.xml
│ ├── values-night
│ └── colors.xml
│ └── values
│ ├── colors.xml
│ ├── dimens.xml
│ └── styles.xml
├── app-timer-run
├── build.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── xyz
│ │ └── aprildown
│ │ └── timer
│ │ └── app
│ │ └── timer
│ │ └── run
│ │ ├── MachineNotif.kt
│ │ ├── MachineService.kt
│ │ ├── MachineServiceModule.kt
│ │ ├── NotificationBuilders.kt
│ │ ├── PhantomActivity.kt
│ │ ├── PhoneCallReceiver.kt
│ │ ├── ServiceWakeLock.kt
│ │ ├── receiver
│ │ └── SchedulerReceiver.kt
│ │ └── screen
│ │ └── ScreenActivity.kt
│ └── res
│ ├── layout-land
│ └── activity_screen.xml
│ ├── layout
│ ├── activity_screen.xml
│ └── layout_notif.xml
│ ├── values-night
│ └── colors.xml
│ └── values
│ ├── colors.xml
│ └── themes.xml
├── app
├── build.gradle
├── dependencies
│ ├── dogReleaseRuntimeClasspath.txt
│ ├── googleReleaseRuntimeClasspath.txt
│ └── otherReleaseRuntimeClasspath.txt
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── io
│ │ └── github
│ │ └── deweyreed
│ │ └── timer
│ │ ├── TestApp.kt
│ │ ├── TestAppComponent.kt
│ │ ├── TestRunner.kt
│ │ └── ui
│ │ ├── EditActivityTest.kt
│ │ └── TaskerEditActivityTest.kt
│ ├── debug
│ └── java
│ │ └── io
│ │ └── github
│ │ └── deweyreed
│ │ └── timer
│ │ └── ui
│ │ └── DebugSetUp.kt
│ ├── dog
│ └── java
│ │ └── io
│ │ └── github
│ │ └── deweyreed
│ │ └── timer
│ │ ├── FlavorDataImpl.kt
│ │ └── FlavorModule.kt
│ ├── dogRelease
│ └── generated
│ │ └── baselineProfiles
│ │ ├── baseline-prof.txt
│ │ └── startup-prof.txt
│ ├── google
│ └── java
│ │ └── io
│ │ └── github
│ │ └── deweyreed
│ │ └── timer
│ │ ├── FlavorDataImpl.kt
│ │ └── FlavorModule.kt
│ ├── googleRelease
│ └── generated
│ │ └── baselineProfiles
│ │ ├── baseline-prof.txt
│ │ └── startup-prof.txt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── io
│ │ │ └── github
│ │ │ └── deweyreed
│ │ │ └── timer
│ │ │ ├── App.kt
│ │ │ ├── di
│ │ │ └── OtherModule.kt
│ │ │ ├── ui
│ │ │ ├── AppNavigatorImpl.kt
│ │ │ ├── DrawerDividerItem.kt
│ │ │ ├── DrawerItemTint.kt
│ │ │ ├── MainActivity.kt
│ │ │ └── single
│ │ │ │ ├── AboutFragment.kt
│ │ │ │ └── HelpFragment.kt
│ │ │ └── utils
│ │ │ └── DynamicThemeDelegate.kt
│ └── res
│ │ ├── layout
│ │ ├── activity_main.xml
│ │ ├── content_main.xml
│ │ ├── dialog_tts_test.xml
│ │ └── fragment_about.xml
│ │ ├── navigation
│ │ └── nav_graph.xml
│ │ ├── values-night
│ │ └── colors.xml
│ │ ├── values
│ │ ├── attrs.xml
│ │ ├── colors.xml
│ │ └── themes.xml
│ │ └── xml
│ │ ├── auto_backup_rules.xml
│ │ ├── data_extraction_rules.xml
│ │ ├── locales_config.xml
│ │ ├── pref_about.xml
│ │ └── pref_help.xml
│ ├── other
│ └── java
│ │ └── io
│ │ └── github
│ │ └── deweyreed
│ │ └── timer
│ │ ├── FlavorDataImpl.kt
│ │ └── FlavorModule.kt
│ ├── otherRelease
│ └── generated
│ │ └── baselineProfiles
│ │ ├── baseline-prof.txt
│ │ └── startup-prof.txt
│ └── release
│ └── java
│ └── io
│ └── github
│ └── deweyreed
│ └── timer
│ └── ui
│ └── DebugSetUp.kt
├── baselineProfile
├── build.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ └── java
│ └── io
│ └── github
│ └── deweyreed
│ └── timer
│ └── baselineprofile
│ ├── BaselineProfileGenerator.kt
│ └── StartupBenchmarks.kt
├── build.gradle
├── component-key
├── build.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ ├── androidx
│ │ └── appcompat
│ │ │ └── widget
│ │ │ └── TooltipCompatFix.kt
│ └── xyz
│ │ └── aprildown
│ │ └── timer
│ │ └── component
│ │ └── key
│ │ ├── DurationPicker.kt
│ │ ├── ImageActionMapper.kt
│ │ ├── ImagePreviewActivity.kt
│ │ ├── ListItem.kt
│ │ ├── ListItemWithLayout.kt
│ │ ├── MaterialPopupMenuHelper.kt
│ │ ├── NameLoopView.kt
│ │ ├── PreferenceStyleListFragment.kt
│ │ ├── RoundTextView.kt
│ │ ├── SimpleInputDialog.kt
│ │ ├── TimePanelLayout.kt
│ │ └── behaviour
│ │ ├── BehaviourLayout.kt
│ │ ├── BehaviourLayoutUtils.kt
│ │ ├── BehaviourTypeUtils.kt
│ │ └── EditableBehaviourLayout.kt
│ └── res
│ ├── drawable
│ └── divider_normal_flexbox.xml
│ ├── layout
│ ├── dialog_simple_input.xml
│ ├── item_time_panel.xml
│ ├── layout_behaviour.xml
│ ├── layout_editable_behaviour.xml
│ ├── layout_editable_behaviour_image.xml
│ ├── layout_list_item.xml
│ ├── layout_list_item_with_layout.xml
│ ├── layout_name_loop.xml
│ ├── layout_time_picker_panel.xml
│ ├── layout_time_picker_scroll.xml
│ ├── mpm_popup_menu_switch.xml
│ ├── view_behavior_chip.xml
│ ├── view_list_item.xml
│ ├── view_list_item_with_layout.xml
│ ├── view_list_item_with_layout_check.xml
│ ├── view_list_item_with_layout_radio.xml
│ ├── view_list_item_with_layout_switch.xml
│ └── view_list_item_with_layout_text.xml
│ └── values
│ ├── attrs.xml
│ ├── dimens.xml
│ ├── styles.xml
│ └── themes.xml
├── component-main
├── build.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── xyz
│ │ └── aprildown
│ │ └── timer
│ │ └── workshop
│ │ ├── Monika.kt
│ │ └── WhitelistFragment.kt
│ └── res
│ └── xml
│ └── pref_whitelist.xml
├── component-settings
├── build.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── xyz
│ │ └── aprildown
│ │ └── timer
│ │ └── component
│ │ └── settings
│ │ ├── DarkThemeDialog.kt
│ │ └── TweakTimeDialog.kt
│ └── res
│ └── layout
│ ├── dialog_dark_theme.xml
│ ├── dialog_tweak_time.xml
│ └── layout_tweak_time_item.xml
├── component-tts
├── build.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ └── java
│ └── com
│ └── github
│ └── deweyreed
│ └── timer
│ └── component
│ └── tts
│ ├── TtsBakery.kt
│ ├── TtsBakeryDiskCache.kt
│ ├── TtsBakeryWorker.kt
│ └── TtsSpeaker.kt
├── compose_compiler_config.conf
├── data
├── build.gradle
├── schemas
│ └── xyz.aprildown.timer.data.db.MachineDatabase
│ │ ├── 1.json
│ │ ├── 2.json
│ │ ├── 3.json
│ │ ├── 4.json
│ │ ├── 5.json
│ │ ├── 6.json
│ │ ├── 7.json
│ │ └── 8.json
└── src
│ ├── androidTest
│ └── java
│ │ └── xyz
│ │ └── aprildown
│ │ └── timer
│ │ └── data
│ │ ├── db
│ │ └── MachineDatabaseMigratingTest.kt
│ │ └── repositories
│ │ ├── AppDataRepositoryImplTest.kt
│ │ ├── NotifierRepositoryImplTest.kt
│ │ ├── SchedulerRepositoryImplTest.kt
│ │ ├── TimerRepositoryImplTest.kt
│ │ └── TimerStampRepositoryImplTest.kt
│ └── main
│ ├── AndroidManifest.xml
│ └── java
│ └── xyz
│ └── aprildown
│ └── timer
│ └── data
│ ├── datas
│ ├── AppDataData.kt
│ ├── BehaviourData.kt
│ ├── FolderData.kt
│ ├── SchedulerData.kt
│ ├── StepData.kt
│ ├── TimerData.kt
│ ├── TimerMoreData.kt
│ └── TimerStampData.kt
│ ├── db
│ ├── Converters.kt
│ ├── Daos.kt
│ └── MachineDatabase.kt
│ ├── di
│ ├── DataModule.kt
│ └── RepoModule.kt
│ ├── job
│ ├── JobDirector.kt
│ ├── NewJobCreator.kt
│ └── SchedulerJob.kt
│ ├── json
│ ├── BehaviourDataJsonAdapter.kt
│ ├── PolymorphicJsonAdapterFactory.java
│ └── TimerMoreDataJsonAdapter.kt
│ ├── mappers
│ ├── AppDataMapper.kt
│ ├── BehaviourMapper.kt
│ ├── FolderMapper.kt
│ ├── MapperUtils.kt
│ ├── SchedulerMapper.kt
│ ├── StepMapper.kt
│ ├── TimerMapper.kt
│ ├── TimerMoreMapper.kt
│ └── TimerStampMapper.kt
│ └── repositories
│ ├── AppDataRepositoryImpl.kt
│ ├── FolderRepositoryImpl.kt
│ ├── NotifierRepositoryImpl.kt
│ ├── PreferencesRepoImpl.kt
│ ├── SchedulerExecutorImpl.kt
│ ├── SchedulerRepositoryImpl.kt
│ ├── TimerRepositoryImpl.kt
│ └── TimerStampRepositoryImpl.kt
├── detekt-config.yml
├── detekt.gradle
├── domain
├── build.gradle
└── src
│ ├── main
│ ├── AndroidManifest.xml
│ └── java
│ │ └── xyz
│ │ └── aprildown
│ │ ├── timer
│ │ └── domain
│ │ │ ├── Mapper.kt
│ │ │ ├── TestData.kt
│ │ │ ├── TimeUtils.kt
│ │ │ ├── di
│ │ │ └── CoroutinesDispatcherModule.kt
│ │ │ ├── entities
│ │ │ ├── AppDataEntity.kt
│ │ │ ├── BehaviourEntity.kt
│ │ │ ├── FolderEntity.kt
│ │ │ ├── SchedulerEntity.kt
│ │ │ ├── StepEntity.kt
│ │ │ ├── TimerEntity.kt
│ │ │ └── TimerStampEntity.kt
│ │ │ ├── repositories
│ │ │ ├── AppDataRepository.kt
│ │ │ ├── AppPreferencesProvider.kt
│ │ │ ├── FolderRepository.kt
│ │ │ ├── NotifierRepository.kt
│ │ │ ├── PreferencesRepository.kt
│ │ │ ├── SchedulerExecutor.kt
│ │ │ ├── SchedulerRepository.kt
│ │ │ ├── TaskerEventTrigger.kt
│ │ │ ├── TimerRepository.kt
│ │ │ └── TimerStampRepository.kt
│ │ │ ├── usecases
│ │ │ ├── CoroutinesUseCase.kt
│ │ │ ├── Fruit.kt
│ │ │ ├── data
│ │ │ │ ├── ExportAppData.kt
│ │ │ │ ├── ImportAppData.kt
│ │ │ │ └── NotifyDataChanged.kt
│ │ │ ├── folder
│ │ │ │ ├── AddFolder.kt
│ │ │ │ ├── DeleteFolder.kt
│ │ │ │ ├── FolderSortByRule.kt
│ │ │ │ ├── GetFolders.kt
│ │ │ │ ├── RecentFolder.kt
│ │ │ │ └── UpdateFolder.kt
│ │ │ ├── home
│ │ │ │ └── TipManager.kt
│ │ │ ├── notifier
│ │ │ │ ├── GetNotifier.kt
│ │ │ │ └── SaveNotifier.kt
│ │ │ ├── record
│ │ │ │ ├── AddTimerStamp.kt
│ │ │ │ └── GetRecords.kt
│ │ │ ├── scheduler
│ │ │ │ ├── AddScheduler.kt
│ │ │ │ ├── DeleteScheduler.kt
│ │ │ │ ├── GetScheduler.kt
│ │ │ │ ├── GetSchedulers.kt
│ │ │ │ ├── SaveScheduler.kt
│ │ │ │ └── SetSchedulerEnable.kt
│ │ │ └── timer
│ │ │ │ ├── AddTimer.kt
│ │ │ │ ├── ChangeTimerFolder.kt
│ │ │ │ ├── DeleteTimer.kt
│ │ │ │ ├── FindTimerInfo.kt
│ │ │ │ ├── GetTimer.kt
│ │ │ │ ├── GetTimerInfo.kt
│ │ │ │ ├── GetTimerInfoFlow.kt
│ │ │ │ ├── SaveTimer.kt
│ │ │ │ └── ShareTimer.kt
│ │ │ └── utils
│ │ │ ├── AppConfig.kt
│ │ │ ├── AppTracker.kt
│ │ │ ├── ApplicationScope.kt
│ │ │ ├── Constants.kt
│ │ │ └── FileUtils.kt
│ │ └── tools
│ │ └── helper
│ │ └── SharedPreferencesHelper.kt
│ └── test
│ └── java
│ └── xyz
│ └── aprildown
│ └── timer
│ └── domain
│ ├── TimeUtilsTest.kt
│ ├── repositories
│ └── TestPreferencesRepository.kt
│ └── usecases
│ ├── data
│ └── DataUseCaseTest.kt
│ ├── folder
│ └── FolderUseCaseTest.kt
│ ├── notifier
│ └── NotifierUseCaseTest.kt
│ ├── record
│ └── RecordUseCasesTest.kt
│ ├── scheduler
│ ├── SchedulerRepeatContentTest.kt
│ └── SchedulerUseCasesTest.kt
│ └── timer
│ └── TimerUseCasesTest.kt
├── fastlane
└── metadata
│ └── android
│ └── en-US
│ ├── full_description.txt
│ ├── images
│ ├── featureGraphic.jpg
│ ├── icon.png
│ ├── phoneScreenshots
│ │ ├── 1.png
│ │ ├── 2.png
│ │ ├── 3.png
│ │ └── 4.png
│ ├── sevenInchScreenshots
│ │ └── 1.png
│ └── tenInchScreenshots
│ │ └── 1.png
│ ├── short_description.txt
│ └── title.txt
├── flavor-google
├── build.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── xyz
│ │ └── aprildown
│ │ └── timer
│ │ ├── app
│ │ └── analytics
│ │ │ ├── AnalyticsModule.kt
│ │ │ └── AppTrackerImpl.kt
│ │ └── flavor
│ │ └── google
│ │ ├── BillingActivity.kt
│ │ ├── BillingFragment.kt
│ │ ├── BillingSupervisor.kt
│ │ ├── FlavorUiInjectorImpl.kt
│ │ ├── NavigationUtils.kt
│ │ ├── backup
│ │ ├── BackupRepoImpl.kt
│ │ ├── CloudBackupFragment.kt
│ │ ├── CloudBackupViewModel.kt
│ │ ├── CloudBackupWorker.kt
│ │ ├── RestoreFragment.kt
│ │ └── usecases
│ │ │ ├── AutoCloudBackup.kt
│ │ │ ├── CloudBackup.kt
│ │ │ ├── CloudBackupState.kt
│ │ │ ├── GetRestoreReferences.kt
│ │ │ └── RestoreFromCloud.kt
│ │ ├── count
│ │ ├── BakedCountDialog.kt
│ │ ├── BakedCountViewModel.kt
│ │ ├── ClearBakedCountResources.kt
│ │ └── LoadBakedCountResources.kt
│ │ └── utils
│ │ ├── FileUtils.kt
│ │ ├── FirebaseExceptionUtils.kt
│ │ ├── FirebaseStorageSetUp.kt
│ │ └── IapPromotionDialog.kt
│ └── res
│ ├── drawable
│ ├── backup_auto_backup.xml
│ ├── backup_cloud_restore.xml
│ ├── backup_cloud_state.xml
│ ├── backup_delete.xml
│ ├── backup_sign_in.xml
│ └── backup_sign_out.xml
│ ├── layout
│ ├── activity_billing.xml
│ ├── dialog_baked_count.xml
│ ├── dialog_loading.xml
│ ├── dialog_title_subscription.xml
│ ├── fragment_billing.xml
│ ├── fragment_restore.xml
│ └── list_item_restore.xml
│ ├── menu
│ └── flavor_help.xml
│ ├── navigation
│ └── backup_graph.xml
│ ├── values
│ └── strings.xml
│ └── xml
│ └── pref_cloud_back.xml
├── functions
└── index.js
├── gradle.properties
├── gradle
├── libs.versions.toml
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── images
├── google-play-badge.png
└── showcase.jpg
├── presentation
├── build.gradle
└── src
│ ├── androidTest
│ └── java
│ │ └── xyz
│ │ └── aprildown
│ │ └── timer
│ │ └── presentation
│ │ └── stream
│ │ └── MachinePresenterTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── xyz
│ │ │ └── aprildown
│ │ │ └── timer
│ │ │ └── presentation
│ │ │ ├── BaseViewModel.kt
│ │ │ ├── StreamMachineIntentProvider.kt
│ │ │ ├── di
│ │ │ └── ViewModelModule.kt
│ │ │ ├── edit
│ │ │ └── EditViewModel.kt
│ │ │ ├── intro
│ │ │ └── IntroViewModel.kt
│ │ │ ├── one
│ │ │ └── OneViewModel.kt
│ │ │ ├── scheduler
│ │ │ ├── EditSchedulerViewModel.kt
│ │ │ ├── SchedulerReceiverPresenter.kt
│ │ │ └── SchedulerViewModel.kt
│ │ │ ├── screen
│ │ │ └── ScreenViewModel.kt
│ │ │ ├── stream
│ │ │ ├── MachineContract.kt
│ │ │ ├── MachinePresenter.kt
│ │ │ ├── StreamState.kt
│ │ │ ├── TimerIndex.kt
│ │ │ ├── TimerMachine.kt
│ │ │ ├── TimerMachineHelper.kt
│ │ │ ├── TimerMachineListener.kt
│ │ │ └── task
│ │ │ │ ├── CountDownTimerTask.kt
│ │ │ │ ├── StopwatchTask.kt
│ │ │ │ ├── Task.kt
│ │ │ │ ├── TaskManager.kt
│ │ │ │ └── TickListener.kt
│ │ │ └── timer
│ │ │ ├── RecordViewModel.kt
│ │ │ ├── TimerPickerViewModel.kt
│ │ │ └── TimerViewModel.kt
│ └── res
│ │ └── values
│ │ └── ids.xml
│ └── test
│ └── java
│ └── xyz
│ └── aprildown
│ └── timer
│ └── presentation
│ ├── edit
│ └── EditViewModelTest.kt
│ ├── one
│ └── OneViewModelTest.kt
│ ├── scheduler
│ ├── EditSchedulerViewModelTest.kt
│ ├── SchedulerReceiverPresenterTest.kt
│ └── SchedulerViewModelTest.kt
│ ├── screen
│ └── ScreenViewModelTest.kt
│ ├── stream
│ ├── MachinePresenterUnitTest.kt
│ └── TimerMachineHelperKtTest.kt
│ └── timer
│ ├── RecordViewModelTest.kt
│ └── TimerViewModelTest.kt
└── settings.gradle
/.github/workflows/android.yml:
--------------------------------------------------------------------------------
1 | name: Android CI
2 |
3 | on:
4 | push:
5 | branches: [ "main", "develop" ]
6 | pull_request:
7 | branches: [ "main", "develop" ]
8 |
9 | jobs:
10 | build:
11 |
12 | runs-on: ubuntu-latest
13 |
14 | steps:
15 | - uses: actions/checkout@v3
16 | - name: Set up JDK 17
17 | uses: actions/setup-java@v3
18 | with:
19 | java-version: '17'
20 | distribution: 'temurin'
21 | cache: gradle
22 |
23 | - name: Grant execute permission for gradlew
24 | run: chmod +x gradlew
25 | - name: Guard dependencies
26 | run: ./gradlew dependencyGuard
27 | - name: Build with Gradle
28 | run: ./gradlew app:assembleDogDebug
29 |
--------------------------------------------------------------------------------
/app-analytics-fake/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.android.library)
3 | alias(libs.plugins.kotlin.android)
4 | alias(libs.plugins.kotlin.kapt)
5 | alias(libs.plugins.hilt)
6 | }
7 |
8 | android {
9 | namespace 'xyz.aprildown.timer.app.analytics'
10 | }
11 |
12 | dependencies {
13 | implementation project(':app-base')
14 |
15 | implementation libs.hilt.android
16 | kapt libs.hilt.compiler
17 | }
18 |
--------------------------------------------------------------------------------
/app-analytics-fake/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/app-analytics-fake/src/main/java/xyz/aprildown/timer/app/analytics/AnalyticsModule.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.app.analytics
2 |
3 | import dagger.Binds
4 | import dagger.Module
5 | import dagger.hilt.InstallIn
6 | import dagger.hilt.components.SingletonComponent
7 | import xyz.aprildown.timer.domain.utils.AppTracker
8 |
9 | @Module
10 | @InstallIn(SingletonComponent::class)
11 | internal abstract class AnalyticsModule {
12 | @Binds
13 | abstract fun bindAppTracker(impl: AppTrackerImpl): AppTracker
14 | }
15 |
--------------------------------------------------------------------------------
/app-analytics-fake/src/main/java/xyz/aprildown/timer/app/analytics/AppTrackerImpl.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.app.analytics
2 |
3 | import android.content.Context
4 | import dagger.Reusable
5 | import timber.log.Timber
6 | import xyz.aprildown.timer.domain.utils.AppTracker
7 | import javax.inject.Inject
8 |
9 | @Reusable
10 | internal class AppTrackerImpl @Inject constructor() : AppTracker {
11 | override fun init(context: Context) = Unit
12 |
13 | override fun trackError(throwable: Throwable, message: String?) {
14 | if (message != null) {
15 | Timber.e(throwable, message)
16 | } else {
17 | Timber.e(throwable)
18 | }
19 | }
20 |
21 | override suspend fun hasCrashedInLastSession(): Boolean {
22 | return false
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app-backup/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.android.library)
3 | alias(libs.plugins.kotlin.android)
4 | alias(libs.plugins.kotlin.kapt)
5 | alias(libs.plugins.hilt)
6 | }
7 |
8 | android {
9 | namespace 'xyz.aprildown.timer.app.backup'
10 | buildFeatures {
11 | compose true
12 | }
13 | composeOptions {
14 | kotlinCompilerExtensionVersion = libs.versions.composeCompiler.get()
15 | }
16 | }
17 |
18 | dependencies {
19 | implementation project(':app-base')
20 | implementation project(':component-key')
21 |
22 | testImplementation libs.junit
23 | testImplementation libs.kotlin.coroutines.test
24 | testImplementation libs.bundles.mockito.core
25 |
26 | implementation libs.androidx.preference
27 |
28 | implementation platform(libs.compose.bom)
29 | implementation libs.bundles.compose
30 |
31 | implementation libs.hilt.android
32 | kapt libs.hilt.compiler
33 |
34 | implementation libs.okio
35 | implementation libs.permission
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/app-backup/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app-backup/src/main/java/xyz/aprildown/timer/app/backup/CloudBackupPreference.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.app.backup
2 |
3 | import android.content.Context
4 | import android.util.AttributeSet
5 | import android.widget.ImageView
6 | import androidx.core.view.isVisible
7 | import androidx.preference.Preference
8 | import androidx.preference.PreferenceViewHolder
9 | import xyz.aprildown.timer.domain.utils.Constants
10 | import xyz.aprildown.tools.helper.safeSharedPreference
11 |
12 | internal class CloudBackupPreference(
13 | context: Context,
14 | attrs: AttributeSet? = null
15 | ) : Preference(context, attrs) {
16 | override fun onBindViewHolder(holder: PreferenceViewHolder) {
17 | super.onBindViewHolder(holder)
18 | val imageView = holder.findViewById(R.id.image) as? ImageView? ?: return
19 | imageView.isVisible =
20 | !context.safeSharedPreference.getBoolean(Constants.PREF_HAS_BACKUP_SUB, false)
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app-backup/src/main/res/layout/layout_cloud_backup_widget.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
--------------------------------------------------------------------------------
/app-base/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/app-base/src/main/java/xyz/aprildown/timer/app/base/data/FlavorData.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.app.base.data
2 |
3 | interface FlavorData {
4 |
5 | enum class Flavor {
6 | Dog, Google, Other,
7 | }
8 |
9 | val flavor: Flavor
10 |
11 | val email: String get() = "ligrsidfd@gmail.com"
12 | val appDownloadLink: String
13 | }
14 |
--------------------------------------------------------------------------------
/app-base/src/main/java/xyz/aprildown/timer/app/base/ui/MainCallback.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.app.base.ui
2 |
3 | import android.os.Bundle
4 | import android.view.View
5 | import androidx.annotation.IdRes
6 | import com.google.android.material.floatingactionbutton.FloatingActionButton
7 |
8 | interface MainCallback {
9 | interface ActivityCallback {
10 | val actionFab: FloatingActionButton
11 | val snackbarView: View
12 | fun enterTimerScreen(itemView: View, id: Int)
13 | fun enterEditScreen(timerId: Int, folderId: Long)
14 | fun restartWithDestination(
15 | @IdRes destinationId: Int,
16 | destinationArguments: Bundle = Bundle.EMPTY
17 | )
18 |
19 | fun recreateThemeItem()
20 | }
21 |
22 | interface FragmentCallback {
23 | fun onFabClick(view: View)
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/app-base/src/main/java/xyz/aprildown/timer/app/base/ui/StepUpdater.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.app.base.ui
2 |
3 | import androidx.fragment.app.DialogFragment
4 | import xyz.aprildown.timer.domain.entities.StepEntity
5 |
6 | interface StepUpdater {
7 | fun updateStep(step: StepEntity.Step, onUpdate: (StepEntity.Step) -> Unit): DialogFragment
8 | }
9 |
--------------------------------------------------------------------------------
/app-base/src/main/java/xyz/aprildown/timer/app/base/utils/UiNameTranslators.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.app.base.utils
2 |
3 | import android.content.Context
4 | import xyz.aprildown.timer.app.base.R
5 | import xyz.aprildown.timer.domain.entities.FolderEntity
6 |
7 | fun FolderEntity.getDisplayName(context: Context): String {
8 | return when {
9 | isDefault -> context.getString(R.string.folder_default)
10 | isTrash -> context.getString(R.string.folder_trash)
11 | else -> name
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/app-base/src/main/java/xyz/aprildown/timer/app/base/utils/ViewUtils.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.app.base.utils
2 |
3 | import android.widget.TextView
4 | import com.github.deweyreed.tools.helper.setTextIfChanged
5 |
6 | fun TextView.setTime(value: Long?) {
7 | setTextIfChanged((value ?: 0).produceTime())
8 | }
9 |
--------------------------------------------------------------------------------
/app-base/src/main/res/anim/close_enter.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
14 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/app-base/src/main/res/anim/close_exit.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
14 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/app-base/src/main/res/anim/ic_anim_pause_play_interpolator_0.xml:
--------------------------------------------------------------------------------
1 |
15 |
16 |
18 |
--------------------------------------------------------------------------------
/app-base/src/main/res/anim/ic_anim_pause_play_interpolator_1.xml:
--------------------------------------------------------------------------------
1 |
15 |
16 |
18 |
--------------------------------------------------------------------------------
/app-base/src/main/res/anim/ic_anim_play_pause_interpolator_0.xml:
--------------------------------------------------------------------------------
1 |
15 |
16 |
18 |
--------------------------------------------------------------------------------
/app-base/src/main/res/anim/ic_anim_play_pause_interpolator_1.xml:
--------------------------------------------------------------------------------
1 |
15 |
16 |
18 |
--------------------------------------------------------------------------------
/app-base/src/main/res/anim/ic_anim_ringing.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
--------------------------------------------------------------------------------
/app-base/src/main/res/anim/open_enter.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
14 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/app-base/src/main/res/anim/open_exit.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
14 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/app-base/src/main/res/anim/slide_in_bottom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
13 |
14 |
--------------------------------------------------------------------------------
/app-base/src/main/res/anim/slide_in_left.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
--------------------------------------------------------------------------------
/app-base/src/main/res/anim/slide_in_right.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
--------------------------------------------------------------------------------
/app-base/src/main/res/anim/slide_out_bottom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
13 |
14 |
--------------------------------------------------------------------------------
/app-base/src/main/res/anim/slide_out_left.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
--------------------------------------------------------------------------------
/app-base/src/main/res/anim/slide_out_right.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/background_step_number.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/ic_ad_off.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/ic_add.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/ic_arrow_down.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/ic_arrow_left_indicator.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/ic_arrow_up.xml:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/ic_back.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/ic_backup.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/ic_beep.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/ic_calendar_more_events.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/ic_check.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/ic_count.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/ic_cross.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/ic_delete.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/ic_dewey_reed.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/ic_edit.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/ic_exit.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/ic_expand.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/ic_flashlight.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/ic_grid_view.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/ic_half.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/ic_halt.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/ic_heart.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/ic_image.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
13 |
16 |
19 |
20 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/ic_left.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/ic_list_view.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/ic_locked.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/ic_menu.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/ic_minus.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/ic_music.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/ic_notification.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/ic_overflow.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/ic_pause.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/ic_plus_one.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/ic_record_predefined_time.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/ic_scheduler.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/ic_screen.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/ic_sort.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/ic_start.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/ic_stat.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/ic_stop.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/ic_time_panel_elapsed.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/ic_time_panel_remaining.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/ic_time_panel_step_end_time.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/ic_time_panel_timer_end_time.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/ic_timer.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/ic_timer_duration.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/ic_unlocked.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/ic_vibration.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/ic_voice.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/ic_warning.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/ic_watch.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/settings_about.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/settings_audio_type.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/settings_brightness.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/settings_cloud_backup.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/settings_code.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/settings_count.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/settings_customize.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/settings_day_night.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/settings_email.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/settings_export.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/settings_file.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/settings_google_play.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/settings_help.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/settings_import.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/settings_material_you.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/settings_media_style_notification.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/settings_notifications.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/settings_phone_call.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/settings_pip.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/settings_plus_time.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/settings_premium.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/settings_rate.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/settings_share.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/settings_theme.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/settings_time_duration.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/settings_tips_and_tricks.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/settings_tutorial.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/settings_tweak_time.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/settings_voice.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/settings_volume_on.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/settings_week_start.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/app-base/src/main/res/drawable/shortcut_timer.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
13 |
14 |
--------------------------------------------------------------------------------
/app-base/src/main/res/font/digital_numbers_colon.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timer-machine/timer-machine-android/699caa3dc0065722a4e6f0a76b02009e253dc280/app-base/src/main/res/font/digital_numbers_colon.ttf
--------------------------------------------------------------------------------
/app-base/src/main/res/layout/dialog_time_picker_dialog_fix.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
--------------------------------------------------------------------------------
/app-base/src/main/res/layout/view_time_picker_fix_normal_clock.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/app-base/src/main/res/layout/view_time_picker_fix_normal_spinner.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/app-base/src/main/res/mipmap-anydpi-v26/app_icon_adaptive.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app-base/src/main/res/mipmap-hdpi/app_icon_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timer-machine/timer-machine-android/699caa3dc0065722a4e6f0a76b02009e253dc280/app-base/src/main/res/mipmap-hdpi/app_icon_round.png
--------------------------------------------------------------------------------
/app-base/src/main/res/mipmap-hdpi/app_icon_square.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timer-machine/timer-machine-android/699caa3dc0065722a4e6f0a76b02009e253dc280/app-base/src/main/res/mipmap-hdpi/app_icon_square.png
--------------------------------------------------------------------------------
/app-base/src/main/res/mipmap-mdpi/app_icon_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timer-machine/timer-machine-android/699caa3dc0065722a4e6f0a76b02009e253dc280/app-base/src/main/res/mipmap-mdpi/app_icon_round.png
--------------------------------------------------------------------------------
/app-base/src/main/res/mipmap-mdpi/app_icon_square.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timer-machine/timer-machine-android/699caa3dc0065722a4e6f0a76b02009e253dc280/app-base/src/main/res/mipmap-mdpi/app_icon_square.png
--------------------------------------------------------------------------------
/app-base/src/main/res/mipmap-xhdpi/app_icon_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timer-machine/timer-machine-android/699caa3dc0065722a4e6f0a76b02009e253dc280/app-base/src/main/res/mipmap-xhdpi/app_icon_round.png
--------------------------------------------------------------------------------
/app-base/src/main/res/mipmap-xhdpi/app_icon_square.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timer-machine/timer-machine-android/699caa3dc0065722a4e6f0a76b02009e253dc280/app-base/src/main/res/mipmap-xhdpi/app_icon_square.png
--------------------------------------------------------------------------------
/app-base/src/main/res/mipmap-xxhdpi/app_icon_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timer-machine/timer-machine-android/699caa3dc0065722a4e6f0a76b02009e253dc280/app-base/src/main/res/mipmap-xxhdpi/app_icon_round.png
--------------------------------------------------------------------------------
/app-base/src/main/res/mipmap-xxhdpi/app_icon_square.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timer-machine/timer-machine-android/699caa3dc0065722a4e6f0a76b02009e253dc280/app-base/src/main/res/mipmap-xxhdpi/app_icon_square.png
--------------------------------------------------------------------------------
/app-base/src/main/res/mipmap-xxxhdpi/app_icon_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timer-machine/timer-machine-android/699caa3dc0065722a4e6f0a76b02009e253dc280/app-base/src/main/res/mipmap-xxxhdpi/app_icon_round.png
--------------------------------------------------------------------------------
/app-base/src/main/res/mipmap-xxxhdpi/app_icon_square.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timer-machine/timer-machine-android/699caa3dc0065722a4e6f0a76b02009e253dc280/app-base/src/main/res/mipmap-xxxhdpi/app_icon_square.png
--------------------------------------------------------------------------------
/app-base/src/main/res/raw/default_ringtone.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timer-machine/timer-machine-android/699caa3dc0065722a4e6f0a76b02009e253dc280/app-base/src/main/res/raw/default_ringtone.mp3
--------------------------------------------------------------------------------
/app-base/src/main/res/values-anydpi-v26/drawables.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | - @mipmap/app_icon_adaptive
4 | - @mipmap/app_icon_adaptive
5 |
--------------------------------------------------------------------------------
/app-base/src/main/res/values-night/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app-base/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app-base/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | #9C27B0
5 | #6A0080
6 | #FFFFFF
7 |
8 | #03A9F4
9 | #007AC1
10 | #FFFFFF
11 |
12 | @color/md_amber_500
13 |
14 |
--------------------------------------------------------------------------------
/app-base/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 56dp
4 |
5 |
6 | 88dp
7 |
8 | 8dp
9 |
10 |
11 |
--------------------------------------------------------------------------------
/app-base/src/main/res/values/drawables.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | - @mipmap/app_icon_square
4 | - @mipmap/app_icon_round
5 |
--------------------------------------------------------------------------------
/app-base/src/main/res/values/ids.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/app-intro/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.android.library)
3 | alias(libs.plugins.kotlin.android)
4 | alias(libs.plugins.kotlin.kapt)
5 | alias(libs.plugins.hilt)
6 | }
7 |
8 | android {
9 | namespace 'xyz.aprildown.timer.app.intro'
10 | }
11 |
12 | dependencies {
13 | implementation project(':app-base')
14 | implementation project(':component-key')
15 | implementation project(':app-timer-list')
16 | implementation project(':app-timer-edit')
17 | implementation project(':app-timer-one')
18 |
19 | implementation libs.hilt.android
20 | kapt libs.hilt.compiler
21 |
22 | implementation libs.flexbox
23 | implementation libs.konfetti
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/app-intro/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app-intro/src/main/res/drawable-land/gradient_intro_panel_view_top.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
--------------------------------------------------------------------------------
/app-intro/src/main/res/drawable/gradient_intro_panel_view_top.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
--------------------------------------------------------------------------------
/app-intro/src/main/res/menu/intro_edit.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
--------------------------------------------------------------------------------
/app-intro/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 120dp
4 |
5 |
--------------------------------------------------------------------------------
/app-intro/src/main/res/values/ids.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app-scheduler/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.android.library)
3 | alias(libs.plugins.kotlin.android)
4 | alias(libs.plugins.kotlin.kapt)
5 | alias(libs.plugins.hilt)
6 | }
7 |
8 | android {
9 | namespace 'xyz.aprildown.timer.app.scheduler'
10 | }
11 |
12 | dependencies {
13 | implementation project(':app-base')
14 |
15 | implementation libs.hilt.android
16 | kapt libs.hilt.compiler
17 |
18 | implementation libs.materialPopupMenu
19 | implementation libs.toggleButtonGroup
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/app-scheduler/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app-scheduler/src/main/res/layout/fragment_scheduler.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
16 |
17 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/app-scheduler/src/main/res/menu/edit_scheduler.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app-settings/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.android.library)
3 | alias(libs.plugins.kotlin.android)
4 | alias(libs.plugins.kotlin.kapt)
5 | alias(libs.plugins.hilt)
6 | }
7 |
8 | android {
9 | namespace 'xyz.aprildown.timer.app.settings'
10 | }
11 |
12 | dependencies {
13 | implementation project(':app-base')
14 | implementation project(':component-key')
15 | implementation project(':component-settings')
16 | implementation project(':component-tts')
17 |
18 | implementation libs.androidx.preference
19 |
20 | implementation libs.hilt.android
21 | kapt libs.hilt.compiler
22 |
23 | implementation libs.fastAdapter
24 |
25 | implementation libs.materialDialog.core
26 | implementation libs.materialDialog.common
27 | implementation libs.permission
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/app-settings/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app-settings/src/main/java/xyz/aprildown/timer/app/settings/BakedCountPreference.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.app.settings
2 |
3 | import android.content.Context
4 | import android.util.AttributeSet
5 | import android.widget.ImageView
6 | import androidx.core.view.isVisible
7 | import androidx.preference.Preference
8 | import androidx.preference.PreferenceViewHolder
9 | import xyz.aprildown.timer.domain.utils.Constants
10 | import xyz.aprildown.tools.helper.safeSharedPreference
11 |
12 | internal class BakedCountPreference(
13 | context: Context,
14 | attrs: AttributeSet? = null
15 | ) : Preference(context, attrs) {
16 | override fun onBindViewHolder(holder: PreferenceViewHolder) {
17 | super.onBindViewHolder(holder)
18 | val imageView = holder.findViewById(R.id.image) as? ImageView? ?: return
19 | imageView.isVisible =
20 | !context.safeSharedPreference.getBoolean(Constants.PREF_HAS_PRO, false)
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app-settings/src/main/res/drawable/theme_bg_round_corner_line.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app-settings/src/main/res/drawable/theme_white_ripple.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | -
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/app-settings/src/main/res/layout/fragment_log.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/app-settings/src/main/res/layout/item_theme_group.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
--------------------------------------------------------------------------------
/app-settings/src/main/res/layout/layout_baked_count_widget.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
--------------------------------------------------------------------------------
/app-settings/src/main/res/layout/widget_color_swatch.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
--------------------------------------------------------------------------------
/app-settings/src/main/res/values-night/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #4DFFFFFF
4 |
5 |
--------------------------------------------------------------------------------
/app-settings/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #61000000
4 |
5 |
--------------------------------------------------------------------------------
/app-tasker/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.android.library)
3 | alias(libs.plugins.kotlin.android)
4 | alias(libs.plugins.kotlin.kapt)
5 | alias(libs.plugins.hilt)
6 | }
7 |
8 | android {
9 | namespace 'xyz.aprildown.timer.app.tasker'
10 | }
11 |
12 | dependencies {
13 | implementation project(':app-base')
14 |
15 | implementation libs.hilt.android
16 | kapt libs.hilt.compiler
17 |
18 | implementation libs.taskerPlugin
19 | }
20 |
--------------------------------------------------------------------------------
/app-tasker/src/main/java/xyz/aprildown/timer/app/tasker/Constants.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.app.tasker
2 |
3 | import android.os.Bundle
4 | import xyz.aprildown.timer.domain.entities.TimerEntity
5 |
6 | internal const val TASKER_TIMER_ID = "timer_id"
7 | internal const val TASKER_ACTION = "action"
8 | internal const val TASKER_ACTION_START = "start"
9 | internal const val TASKER_ACTION_STOP = "stop"
10 |
11 | internal fun Bundle.getTaskerTimerId(): Int = getInt(TASKER_TIMER_ID, TimerEntity.NULL_ID)
12 | internal fun Bundle.getTaskerTimerAction(): String = getString(TASKER_ACTION, TASKER_ACTION_START)
13 |
--------------------------------------------------------------------------------
/app-tasker/src/main/java/xyz/aprildown/timer/app/tasker/TaskerModule.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.app.tasker
2 |
3 | import dagger.Binds
4 | import dagger.Module
5 | import dagger.hilt.InstallIn
6 | import dagger.hilt.components.SingletonComponent
7 | import xyz.aprildown.timer.domain.repositories.TaskerEventTrigger
8 |
9 | @InstallIn(SingletonComponent::class)
10 | @Module
11 | internal abstract class TaskerModule {
12 | @Binds
13 | abstract fun bindTaskerEventTrigger(impl: TaskerEventTriggerImpl): TaskerEventTrigger
14 | }
15 |
--------------------------------------------------------------------------------
/app-tasker/src/main/java/xyz/aprildown/timer/app/tasker/TaskerRunPresenter.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.app.tasker
2 |
3 | import android.content.Intent
4 | import xyz.aprildown.timer.domain.usecases.timer.FindTimerInfo
5 | import xyz.aprildown.timer.presentation.StreamMachineIntentProvider
6 | import javax.inject.Inject
7 |
8 | internal class TaskerRunPresenter @Inject constructor(
9 | private val findTimerInfo: FindTimerInfo,
10 | private val streamMachineIntentProvider: StreamMachineIntentProvider
11 | ) {
12 | suspend fun isValidTimerId(timerId: Int): Boolean {
13 | return findTimerInfo(timerId) != null
14 | }
15 |
16 | fun start(timerId: Int): Intent = streamMachineIntentProvider.startIntent(timerId)
17 |
18 | fun stop(timerId: Int): Intent = streamMachineIntentProvider.resetIntent(timerId)
19 | }
20 |
--------------------------------------------------------------------------------
/app-tasker/src/main/res/menu/tasker_edit.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app-timer-edit/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.android.library)
3 | alias(libs.plugins.kotlin.android)
4 | alias(libs.plugins.kotlin.kapt)
5 | alias(libs.plugins.hilt)
6 | }
7 |
8 | android {
9 | namespace 'xyz.aprildown.timer.app.timer.edit'
10 | }
11 |
12 | dependencies {
13 | implementation project(':app-base')
14 | implementation project(':component-key')
15 |
16 | implementation libs.hilt.android
17 | kapt libs.hilt.compiler
18 |
19 | implementation libs.fastAdapter
20 |
21 | implementation libs.flexbox
22 | implementation libs.materialPopupMenu
23 | implementation libs.ultimateRingtonePicker
24 | implementation libs.twoWayNestedScrollView
25 | }
26 |
--------------------------------------------------------------------------------
/app-timer-edit/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app-timer-edit/src/main/java/xyz/aprildown/timer/app/timer/edit/StepInfoView.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.app.timer.edit
2 |
3 | import android.content.Context
4 | import android.util.AttributeSet
5 | import android.view.View
6 | import android.widget.FrameLayout
7 | import com.google.android.material.chip.Chip
8 | import xyz.aprildown.timer.app.base.utils.produceTime
9 |
10 | internal class StepInfoView(
11 | context: Context,
12 | attrs: AttributeSet? = null
13 | ) : FrameLayout(context, attrs) {
14 |
15 | private val durationChip: Chip
16 |
17 | init {
18 |
19 | View.inflate(context, R.layout.view_step_info, this)
20 |
21 | durationChip = findViewById(R.id.chipStepInfoDuration)
22 | }
23 |
24 | fun setDuration(durationInMilli: Long) {
25 | durationChip.text = durationInMilli.produceTime()
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app-timer-edit/src/main/res/color/voice_variable_chip_stroke.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app-timer-edit/src/main/res/drawable/background_step_info.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app-timer-edit/src/main/res/drawable/voice_variable_table_divider.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/app-timer-edit/src/main/res/layout/dialog_update_step.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/app-timer-edit/src/main/res/layout/dialog_voice_variable.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app-timer-edit/src/main/res/layout/list_item_voice_variable_auto_completion.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
--------------------------------------------------------------------------------
/app-timer-edit/src/main/res/values-night/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #14FFFFFF
4 |
5 |
--------------------------------------------------------------------------------
/app-timer-edit/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #14000000
4 |
5 |
--------------------------------------------------------------------------------
/app-timer-edit/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 8dp
4 |
5 |
--------------------------------------------------------------------------------
/app-timer-list/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.android.library)
3 | alias(libs.plugins.kotlin.android)
4 | alias(libs.plugins.kotlin.kapt)
5 | alias(libs.plugins.hilt)
6 | }
7 |
8 | android {
9 | namespace 'xyz.aprildown.timer.app.timer.list'
10 | }
11 |
12 | dependencies {
13 | implementation project(':app-base')
14 | implementation project(':component-key')
15 |
16 | implementation libs.hilt.android
17 | kapt libs.hilt.compiler
18 |
19 | implementation libs.fastAdapter
20 | implementation libs.fastAdapter.binding
21 | implementation libs.fastAdapter.expandable
22 |
23 | implementation libs.calendarView
24 | implementation libs.chart
25 | implementation libs.materialPopupMenu
26 | implementation libs.permission
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/app-timer-list/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app-timer-list/src/main/java/xyz/aprildown/timer/app/timer/list/MutableTimerItem.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.app.timer.list
2 |
3 | import xyz.aprildown.timer.domain.entities.TimerEntity
4 | import xyz.aprildown.timer.domain.entities.TimerInfo
5 | import xyz.aprildown.timer.presentation.stream.StreamState
6 |
7 | internal data class MutableTimerItem(
8 | val timerInfo: TimerInfo,
9 | var timerItem: TimerEntity?,
10 | var state: StreamState,
11 | var isExpanded: Boolean
12 | ) {
13 | val timerId = timerInfo.id
14 | val timerName = timerInfo.name
15 | }
16 |
--------------------------------------------------------------------------------
/app-timer-list/src/main/java/xyz/aprildown/timer/app/timer/list/record/RecordViewUtils.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.app.timer.list.record
2 |
3 | import android.view.View
4 |
5 | internal fun View.animateShowGraphs() {
6 | animate()
7 | .withStartAction { alpha = 0f }
8 | .alpha(1f)
9 | .start()
10 | }
11 |
12 | internal fun View.animateHideGraphs() {
13 | animate()
14 | .alpha(0f)
15 | .start()
16 | }
17 |
--------------------------------------------------------------------------------
/app-timer-list/src/main/res/drawable/background_record_calendar_circle.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app-timer-list/src/main/res/layout/list_item_calendar_day_event.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
--------------------------------------------------------------------------------
/app-timer-list/src/main/res/menu/record_predefined_time.xml:
--------------------------------------------------------------------------------
1 |
2 |
19 |
--------------------------------------------------------------------------------
/app-timer-list/src/main/res/menu/timer.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app-timer-list/src/main/res/values-w480dp/integer.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 3
4 |
5 |
--------------------------------------------------------------------------------
/app-timer-list/src/main/res/values-w600dp/integer.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4
4 |
5 |
--------------------------------------------------------------------------------
/app-timer-list/src/main/res/values-w720dp/integer.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 5
4 |
5 |
--------------------------------------------------------------------------------
/app-timer-list/src/main/res/values-w840dp/integer.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 6
4 |
5 |
--------------------------------------------------------------------------------
/app-timer-list/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 2dp
4 |
5 |
--------------------------------------------------------------------------------
/app-timer-list/src/main/res/values/integer.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 2
4 |
5 |
--------------------------------------------------------------------------------
/app-timer-one/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.android.library)
3 | alias(libs.plugins.kotlin.android)
4 | alias(libs.plugins.kotlin.kapt)
5 | alias(libs.plugins.hilt)
6 | }
7 |
8 | android {
9 | namespace 'xyz.aprildown.timer.app.timer.one'
10 | }
11 |
12 | dependencies {
13 | implementation project(':app-base')
14 | implementation project(':component-key')
15 | implementation project(':component-settings')
16 |
17 | implementation libs.androidx.dynamicAnimation
18 |
19 | implementation libs.hilt.android
20 | kapt libs.hilt.compiler
21 |
22 | implementation libs.fastAdapter
23 |
24 | implementation libs.chromeMenu
25 | implementation libs.flexbox
26 | implementation libs.materialPopupMenu
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/app-timer-one/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app-timer-one/src/main/java/xyz/aprildown/timer/app/timer/one/step/StepListListeners.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.app.timer.one.step
2 |
3 | import com.mikepenz.fastadapter.IItem
4 |
5 | internal interface CurrentPositionCallback {
6 | val currentPosition: Int
7 | }
8 |
9 | internal interface OnStepLongClickListener {
10 | fun onStepLongClick(item: IItem<*>)
11 | fun onStepTimeLongClick(item: IItem<*>)
12 | fun onStepDoubleTap(item: IItem<*>)
13 | }
14 |
--------------------------------------------------------------------------------
/app-timer-one/src/main/java/xyz/aprildown/timer/app/timer/one/step/VisibleGroupEnd.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.app.timer.one.step
2 |
3 | import android.view.View
4 | import androidx.recyclerview.widget.RecyclerView
5 | import com.mikepenz.fastadapter.items.AbstractItem
6 | import com.mikepenz.fastadapter.utils.DefaultIdDistributorImpl
7 | import xyz.aprildown.timer.app.timer.one.R
8 | import xyz.aprildown.timer.app.base.R as RBase
9 |
10 | internal class VisibleGroupEnd : AbstractItem() {
11 |
12 | override val layoutRes: Int = R.layout.item_step_group_end
13 | override val type: Int = RBase.id.type_step_group_end
14 | override fun getViewHolder(v: View): ViewHolder = ViewHolder(v)
15 |
16 | /**
17 | * Although we need unique identifiers,
18 | * FastAdapter will handles all for us since it decrements from -2 [DefaultIdDistributorImpl].
19 | */
20 |
21 | // override var identifier: Long = -1
22 |
23 | class ViewHolder(view: View) : RecyclerView.ViewHolder(view)
24 | }
25 |
--------------------------------------------------------------------------------
/app-timer-one/src/main/res/layout/fragment_one_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app-timer-one/src/main/res/layout/item_step_group_end.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/app-timer-one/src/main/res/layout/layout_one_widget_time_panel.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app-timer-one/src/main/res/layout/layout_one_widget_timing_bar.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
--------------------------------------------------------------------------------
/app-timer-one/src/main/res/layout/layout_tweak_time_main_button.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app-timer-one/src/main/res/layout/layout_tweak_time_tweak_button.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app-timer-one/src/main/res/menu/one_tweak_time.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app-timer-one/src/main/res/navigation/nav_graph_one.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
15 |
16 |
--------------------------------------------------------------------------------
/app-timer-one/src/main/res/values-night/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #14FFFFFF
4 |
5 |
--------------------------------------------------------------------------------
/app-timer-one/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #14000000
4 |
5 |
--------------------------------------------------------------------------------
/app-timer-one/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 156dp
4 |
--------------------------------------------------------------------------------
/app-timer-one/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
13 |
14 |
20 |
21 |
--------------------------------------------------------------------------------
/app-timer-run/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.android.library)
3 | alias(libs.plugins.kotlin.android)
4 | alias(libs.plugins.kotlin.kapt)
5 | alias(libs.plugins.hilt)
6 | }
7 |
8 | android {
9 | namespace 'xyz.aprildown.timer.app.timer.run'
10 | }
11 |
12 | dependencies {
13 | implementation project(':data')
14 | implementation project(':app-base')
15 | implementation project(':component-tts')
16 |
17 | implementation libs.androidx.media
18 |
19 | implementation libs.hilt.android
20 | kapt libs.hilt.compiler
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/app-timer-run/src/main/java/xyz/aprildown/timer/app/timer/run/MachineServiceModule.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.app.timer.run
2 |
3 | import dagger.Binds
4 | import dagger.Module
5 | import dagger.hilt.InstallIn
6 | import dagger.hilt.components.SingletonComponent
7 | import xyz.aprildown.timer.presentation.stream.MachineContract
8 | import xyz.aprildown.timer.presentation.stream.MachinePresenter
9 |
10 | @Module
11 | @InstallIn(SingletonComponent::class)
12 | internal abstract class MachineServiceModule {
13 | @Binds
14 | abstract fun provideMachineServicePresenter(presenter: MachinePresenter): MachineContract.Presenter
15 | }
16 |
--------------------------------------------------------------------------------
/app-timer-run/src/main/res/values-night/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | @color/color_notification_background_dark
4 |
5 |
--------------------------------------------------------------------------------
/app-timer-run/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFFFFF
4 | #000000
5 | @color/color_notification_background_light
6 |
7 |
--------------------------------------------------------------------------------
/app-timer-run/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/io/github/deweyreed/timer/TestApp.kt:
--------------------------------------------------------------------------------
1 | package io.github.deweyreed.timer
2 |
3 | import android.app.Application
4 | import dagger.hilt.android.testing.CustomTestApplication
5 | import xyz.aprildown.theme.Theme
6 | import xyz.aprildown.timer.app.base.R as RBase
7 |
8 | /**
9 | * We use a separate App for tests to prevent initializing dependency injection.
10 | */
11 | open class TestApp : Application() {
12 | override fun onCreate() {
13 | super.onCreate()
14 | Theme.init(context = this, themeRes = RBase.style.AppTheme)
15 | }
16 | }
17 |
18 | @CustomTestApplication(TestApp::class)
19 | interface HiltTestApplication
20 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/io/github/deweyreed/timer/TestRunner.kt:
--------------------------------------------------------------------------------
1 | package io.github.deweyreed.timer
2 |
3 | import android.app.Application
4 | import android.content.Context
5 | import androidx.test.runner.AndroidJUnitRunner
6 |
7 | @Suppress("unused")
8 | class TestRunner : AndroidJUnitRunner() {
9 | override fun newApplication(cl: ClassLoader?, name: String?, context: Context?): Application {
10 | return super.newApplication(cl, HiltTestApplication_Application::class.java.name, context)
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/debug/java/io/github/deweyreed/timer/ui/DebugSetUp.kt:
--------------------------------------------------------------------------------
1 | package io.github.deweyreed.timer.ui
2 |
3 | import android.view.View
4 | import io.github.deweyreed.timer.R
5 |
6 | fun MainActivity.setUpDebug() {
7 | findViewById(R.id.toolbar).let {
8 | it.setOnClickListener {
9 |
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/dog/java/io/github/deweyreed/timer/FlavorDataImpl.kt:
--------------------------------------------------------------------------------
1 | package io.github.deweyreed.timer
2 |
3 | import dagger.Reusable
4 | import xyz.aprildown.timer.app.base.data.FlavorData
5 | import javax.inject.Inject
6 |
7 | @Reusable
8 | class FlavorDataImpl @Inject constructor() : FlavorData {
9 | override val flavor: FlavorData.Flavor = FlavorData.Flavor.Dog
10 | override val appDownloadLink: String =
11 | "https://github.com/timer-machine/timer-machine-android/releases"
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/dog/java/io/github/deweyreed/timer/FlavorModule.kt:
--------------------------------------------------------------------------------
1 | package io.github.deweyreed.timer
2 |
3 | import dagger.Binds
4 | import dagger.Module
5 | import dagger.hilt.InstallIn
6 | import dagger.hilt.components.SingletonComponent
7 | import xyz.aprildown.timer.app.base.data.FlavorData
8 |
9 | @Module
10 | @InstallIn(SingletonComponent::class)
11 | abstract class FlavorModule {
12 | @Binds
13 | abstract fun bindFlavorData(impl: FlavorDataImpl): FlavorData
14 | }
15 |
--------------------------------------------------------------------------------
/app/src/google/java/io/github/deweyreed/timer/FlavorDataImpl.kt:
--------------------------------------------------------------------------------
1 | package io.github.deweyreed.timer
2 |
3 | import dagger.Reusable
4 | import xyz.aprildown.timer.app.base.data.FlavorData
5 | import javax.inject.Inject
6 |
7 | @Reusable
8 | class FlavorDataImpl @Inject constructor() : FlavorData {
9 | override val flavor: FlavorData.Flavor = FlavorData.Flavor.Google
10 | override val appDownloadLink: String =
11 | "https://play.google.com/store/apps/details?id=io.github.deweyreed.timer.google"
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
14 |
15 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_about.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values-night/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFF
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #757575
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/auto_backup_rules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/data_extraction_rules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/locales_config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/other/java/io/github/deweyreed/timer/FlavorDataImpl.kt:
--------------------------------------------------------------------------------
1 | package io.github.deweyreed.timer
2 |
3 | import dagger.Reusable
4 | import xyz.aprildown.timer.app.base.data.FlavorData
5 | import javax.inject.Inject
6 |
7 | @Reusable
8 | class FlavorDataImpl @Inject constructor() : FlavorData {
9 | override val flavor: FlavorData.Flavor = FlavorData.Flavor.Other
10 | override val appDownloadLink: String =
11 | "https://github.com/timer-machine/timer-machine-android/releases"
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/other/java/io/github/deweyreed/timer/FlavorModule.kt:
--------------------------------------------------------------------------------
1 | package io.github.deweyreed.timer
2 |
3 | import dagger.Binds
4 | import dagger.Module
5 | import dagger.hilt.InstallIn
6 | import dagger.hilt.components.SingletonComponent
7 | import xyz.aprildown.timer.app.base.data.FlavorData
8 |
9 | @Module
10 | @InstallIn(SingletonComponent::class)
11 | abstract class FlavorModule {
12 | @Binds
13 | abstract fun bindFlavorData(impl: FlavorDataImpl): FlavorData
14 | }
15 |
--------------------------------------------------------------------------------
/app/src/release/java/io/github/deweyreed/timer/ui/DebugSetUp.kt:
--------------------------------------------------------------------------------
1 | package io.github.deweyreed.timer.ui
2 |
3 | @Suppress("unused")
4 | fun MainActivity.setUpDebug() = Unit
5 |
--------------------------------------------------------------------------------
/baselineProfile/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/component-key/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.android.library)
3 | alias(libs.plugins.kotlin.android)
4 | alias(libs.plugins.kotlin.parcelize)
5 | }
6 |
7 | android {
8 | namespace 'xyz.aprildown.timer.component.key'
9 | buildFeatures {
10 | compose true
11 | }
12 | composeOptions {
13 | kotlinCompilerExtensionVersion = libs.versions.composeCompiler.get()
14 | }
15 | }
16 |
17 | dependencies {
18 | implementation project(':app-base')
19 |
20 | implementation libs.androidx.preference
21 |
22 | implementation platform(libs.compose.bom)
23 | implementation libs.bundles.compose
24 |
25 | implementation libs.flexbox
26 | implementation libs.hmsPicker
27 | implementation libs.materialPopupMenu
28 | implementation libs.scrollHmsPicker
29 | implementation libs.coil
30 | implementation libs.coil.compose
31 | implementation libs.zoomable
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/component-key/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/component-key/src/main/java/androidx/appcompat/widget/TooltipCompatFix.kt:
--------------------------------------------------------------------------------
1 | package androidx.appcompat.widget
2 |
3 | import android.annotation.SuppressLint
4 | import android.os.Build
5 | import android.view.View
6 |
7 | object TooltipCompatFix {
8 | /**
9 | * Tooltip' platform implementation on Android O(API 26) is broken and crashes.
10 | * So I use the compat version on all pre-Q devices.
11 | *
12 | * https://issuetracker.google.com/issues/64461213
13 | * [TooltipCompat.setTooltipText]
14 | */
15 | @SuppressLint("RestrictedApi")
16 | fun setTooltipText(view: View, tooltipText: CharSequence?) {
17 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
18 | view.tooltipText = tooltipText
19 | } else {
20 | TooltipCompatHandler.setTooltipText(view, tooltipText)
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/component-key/src/main/java/xyz/aprildown/timer/component/key/ImageActionMapper.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.component.key
2 |
3 | import androidx.core.net.toUri
4 | import coil.map.Mapper
5 | import coil.request.Options
6 | import xyz.aprildown.timer.domain.entities.ImageAction
7 | import xyz.aprildown.timer.domain.entities.ResourceContentType
8 | import java.io.File
9 |
10 | class ImageActionMapper : Mapper {
11 | override fun map(data: ImageAction, options: Options): Any {
12 | return when (data.type) {
13 | ResourceContentType.CanonicalPath -> File(data.data)
14 | ResourceContentType.RelativePath -> error("Relative path can't be loaded")
15 | ResourceContentType.Uri -> data.data.toUri()
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/component-key/src/main/res/drawable/divider_normal_flexbox.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
--------------------------------------------------------------------------------
/component-key/src/main/res/layout/dialog_simple_input.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
15 |
16 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/component-key/src/main/res/layout/layout_behaviour.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
15 |
16 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/component-key/src/main/res/layout/layout_editable_behaviour_image.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
--------------------------------------------------------------------------------
/component-key/src/main/res/layout/layout_list_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
--------------------------------------------------------------------------------
/component-key/src/main/res/layout/layout_list_item_with_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
--------------------------------------------------------------------------------
/component-key/src/main/res/layout/layout_time_picker_panel.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
14 |
--------------------------------------------------------------------------------
/component-key/src/main/res/layout/layout_time_picker_scroll.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
14 |
--------------------------------------------------------------------------------
/component-key/src/main/res/layout/view_behavior_chip.xml:
--------------------------------------------------------------------------------
1 |
2 |
19 |
--------------------------------------------------------------------------------
/component-key/src/main/res/layout/view_list_item_with_layout_check.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
--------------------------------------------------------------------------------
/component-key/src/main/res/layout/view_list_item_with_layout_radio.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
--------------------------------------------------------------------------------
/component-key/src/main/res/layout/view_list_item_with_layout_switch.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
--------------------------------------------------------------------------------
/component-key/src/main/res/layout/view_list_item_with_layout_text.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
--------------------------------------------------------------------------------
/component-key/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 8dp
4 | 4dp
5 |
6 | 8dp
7 | 56dp
8 | 64dp
9 | 88dp
10 |
11 |
--------------------------------------------------------------------------------
/component-key/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/component-key/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
--------------------------------------------------------------------------------
/component-main/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.android.library)
3 | alias(libs.plugins.kotlin.android)
4 | }
5 |
6 | android {
7 | namespace 'xyz.aprildown.timer.workshop'
8 | }
9 |
10 | dependencies {
11 | implementation project(':app-base')
12 |
13 | implementation libs.androidx.preference
14 |
15 | implementation libs.fastAdapter
16 | implementation libs.fastAdapter.binding
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/component-main/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/component-main/src/main/res/xml/pref_whitelist.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
13 |
14 |
18 |
19 |
--------------------------------------------------------------------------------
/component-settings/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.android.library)
3 | alias(libs.plugins.kotlin.android)
4 | }
5 |
6 | android {
7 | namespace 'xyz.aprildown.timer.component.settings'
8 | }
9 |
10 | dependencies {
11 | implementation project(':app-base')
12 | implementation project(':component-key')
13 | }
14 |
--------------------------------------------------------------------------------
/component-settings/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/component-tts/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.android.library)
3 | alias(libs.plugins.kotlin.android)
4 | alias(libs.plugins.kotlin.kapt)
5 | alias(libs.plugins.hilt)
6 | }
7 |
8 | android {
9 | namespace 'com.github.deweyreed.timer.component.tts'
10 | }
11 |
12 | dependencies {
13 | implementation project(':app-base')
14 |
15 | implementation libs.androidx.work
16 |
17 | implementation libs.hilt.android
18 | kapt libs.hilt.compiler
19 | implementation libs.hilt.work
20 | kapt libs.hilt.work.compiler
21 |
22 | implementation libs.diskLruCache
23 | }
24 |
--------------------------------------------------------------------------------
/component-tts/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/compose_compiler_config.conf:
--------------------------------------------------------------------------------
1 | kotlin.collections.*
2 | xyz.aprildown.timer.domain.usecases.Fruit
3 |
--------------------------------------------------------------------------------
/data/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/data/src/main/java/xyz/aprildown/timer/data/datas/AppDataData.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.data.datas
2 |
3 | import androidx.annotation.Keep
4 | import com.squareup.moshi.Json
5 | import com.squareup.moshi.JsonClass
6 |
7 | @Keep
8 | @JsonClass(generateAdapter = true)
9 | internal data class AppDataData(
10 | @Json(name = "folders")
11 | val folders: List = emptyList(),
12 |
13 | @Json(name = "timers")
14 | val timers: List = emptyList(),
15 |
16 | @Json(name = "notifier")
17 | val notifier: StepData.Step? = null,
18 |
19 | @Json(name = "timerStamps")
20 | val timerStamps: List = emptyList(),
21 |
22 | @Json(name = "schedulers")
23 | val schedulers: List = emptyList(),
24 |
25 | @Json(name = "prefs")
26 | val prefs: Map = emptyMap()
27 | )
28 |
--------------------------------------------------------------------------------
/data/src/main/java/xyz/aprildown/timer/data/datas/BehaviourData.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.data.datas
2 |
3 | import androidx.annotation.Keep
4 | import com.squareup.moshi.Json
5 | import xyz.aprildown.timer.domain.entities.BehaviourType
6 |
7 | @Keep
8 | internal data class BehaviourData(
9 | @Json(name = "type")
10 | val type: BehaviourType,
11 |
12 | @Json(name = "label")
13 | val label: String = "",
14 |
15 | @Json(name = "content")
16 | val content: String = "",
17 |
18 | @Json(name = "loop")
19 | val loop: Boolean = true
20 | )
21 |
--------------------------------------------------------------------------------
/data/src/main/java/xyz/aprildown/timer/data/datas/FolderData.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.data.datas
2 |
3 | import androidx.room.ColumnInfo
4 | import androidx.room.Entity
5 | import androidx.room.PrimaryKey
6 | import com.squareup.moshi.Json
7 | import com.squareup.moshi.JsonClass
8 |
9 | @JsonClass(generateAdapter = true)
10 | @Entity(tableName = "Folder")
11 | internal data class FolderData(
12 | @Json(name = "id")
13 | @PrimaryKey(autoGenerate = true)
14 | @ColumnInfo(name = "id")
15 | val id: Long,
16 |
17 | @Json(name = "name")
18 | @ColumnInfo(name = "name")
19 | val name: String
20 |
21 | )
22 |
--------------------------------------------------------------------------------
/data/src/main/java/xyz/aprildown/timer/data/datas/TimerMoreData.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.data.datas
2 |
3 | import androidx.annotation.Keep
4 | import com.squareup.moshi.Json
5 | import xyz.aprildown.timer.domain.entities.TimerEntity
6 |
7 | @Keep
8 | internal data class TimerMoreData(
9 | @Json(name = "showNotif")
10 | val showNotif: Boolean = true,
11 |
12 | @Json(name = "notifCount")
13 | val notifCount: Boolean = true,
14 |
15 | @Json(name = "triggerTimerId")
16 | val triggerTimerId: Int = TimerEntity.NULL_ID
17 | )
18 |
--------------------------------------------------------------------------------
/data/src/main/java/xyz/aprildown/timer/data/di/DataModule.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.data.di
2 |
3 | import android.content.Context
4 | import android.content.SharedPreferences
5 | import dagger.Module
6 | import dagger.Provides
7 | import dagger.Reusable
8 | import dagger.hilt.InstallIn
9 | import dagger.hilt.android.qualifiers.ApplicationContext
10 | import dagger.hilt.components.SingletonComponent
11 | import xyz.aprildown.timer.data.db.MachineDatabase
12 | import xyz.aprildown.tools.helper.safeSharedPreference
13 | import javax.inject.Singleton
14 |
15 | @Module
16 | @InstallIn(SingletonComponent::class)
17 | object DataModule {
18 | @Singleton
19 | @Provides
20 | fun provideDatabase(@ApplicationContext context: Context): MachineDatabase {
21 | return MachineDatabase.createPersistentDatabase(context)
22 | }
23 |
24 | @Reusable
25 | @Provides
26 | fun provideSharedPreferences(@ApplicationContext context: Context): SharedPreferences {
27 | return context.safeSharedPreference
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/data/src/main/java/xyz/aprildown/timer/data/job/JobDirector.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.data.job
2 |
3 | import android.app.Application
4 | import com.evernote.android.job.JobManager
5 |
6 | fun Application.initJob() {
7 | JobManager.create(this).addJobCreator(NewJobCreator())
8 | }
9 |
--------------------------------------------------------------------------------
/data/src/main/java/xyz/aprildown/timer/data/job/NewJobCreator.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.data.job
2 |
3 | import com.evernote.android.job.Job
4 | import com.evernote.android.job.JobCreator
5 |
6 | internal class NewJobCreator : JobCreator {
7 | override fun create(tag: String): Job? = when {
8 | tag.startsWith(SchedulerJob.TAG_PREFIX) -> SchedulerJob()
9 | else -> null
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/data/src/main/java/xyz/aprildown/timer/data/mappers/BehaviourMapper.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.data.mappers
2 |
3 | import dagger.Reusable
4 | import xyz.aprildown.timer.data.datas.BehaviourData
5 | import xyz.aprildown.timer.domain.Mapper
6 | import xyz.aprildown.timer.domain.entities.BehaviourEntity
7 | import javax.inject.Inject
8 |
9 | @Reusable
10 | internal class BehaviourMapper @Inject constructor() : Mapper() {
11 | override fun mapFrom(from: BehaviourData): BehaviourEntity {
12 | return BehaviourEntity(
13 | type = from.type,
14 | str1 = from.label,
15 | str2 = from.content,
16 | bool = from.loop
17 | )
18 | }
19 |
20 | override fun mapTo(from: BehaviourEntity): BehaviourData {
21 | return BehaviourData(
22 | type = from.type,
23 | label = from.str1,
24 | content = from.str2,
25 | loop = from.bool
26 | )
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/data/src/main/java/xyz/aprildown/timer/data/mappers/FolderMapper.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.data.mappers
2 |
3 | import dagger.Reusable
4 | import xyz.aprildown.timer.data.datas.FolderData
5 | import xyz.aprildown.timer.domain.Mapper
6 | import xyz.aprildown.timer.domain.entities.FolderEntity
7 | import javax.inject.Inject
8 |
9 | @Reusable
10 | internal class FolderMapper @Inject constructor() : Mapper() {
11 |
12 | override fun mapFrom(from: FolderData): FolderEntity {
13 | return FolderEntity(
14 | id = from.id,
15 | name = from.name
16 | )
17 | }
18 |
19 | override fun mapTo(from: FolderEntity): FolderData {
20 | return FolderData(
21 | id = from.id,
22 | name = from.name
23 | )
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/data/src/main/java/xyz/aprildown/timer/data/mappers/MapperUtils.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.data.mappers
2 |
3 | import xyz.aprildown.timer.domain.Mapper
4 |
5 | internal fun Data.fromWithMapper(mapper: Mapper): Entity =
6 | mapper.mapFrom(this)
7 |
8 | internal fun List.fromWithMapper(mapper: Mapper): List =
9 | mapper.mapFrom(this)
10 |
11 | internal fun Entity.toWithMapper(mapper: Mapper): Data =
12 | mapper.mapTo(this)
13 |
14 | internal fun List.toWithMapper(mapper: Mapper): List =
15 | mapper.mapTo(this)
16 |
--------------------------------------------------------------------------------
/data/src/main/java/xyz/aprildown/timer/data/mappers/TimerMoreMapper.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.data.mappers
2 |
3 | import dagger.Reusable
4 | import xyz.aprildown.timer.data.datas.TimerMoreData
5 | import xyz.aprildown.timer.domain.Mapper
6 | import xyz.aprildown.timer.domain.entities.TimerMoreEntity
7 | import javax.inject.Inject
8 |
9 | @Reusable
10 | internal class TimerMoreMapper @Inject constructor() : Mapper() {
11 |
12 | override fun mapFrom(from: TimerMoreData): TimerMoreEntity {
13 | return TimerMoreEntity(
14 | showNotif = from.showNotif,
15 | notifCount = from.notifCount,
16 | triggerTimerId = from.triggerTimerId
17 | )
18 | }
19 |
20 | override fun mapTo(from: TimerMoreEntity): TimerMoreData {
21 | return TimerMoreData(
22 | showNotif = from.showNotif,
23 | notifCount = from.notifCount,
24 | triggerTimerId = from.triggerTimerId
25 | )
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/data/src/main/java/xyz/aprildown/timer/data/mappers/TimerStampMapper.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.data.mappers
2 |
3 | import dagger.Reusable
4 | import xyz.aprildown.timer.data.datas.TimerStampData
5 | import xyz.aprildown.timer.domain.Mapper
6 | import xyz.aprildown.timer.domain.entities.TimerStampEntity
7 | import javax.inject.Inject
8 |
9 | @Reusable
10 | internal class TimerStampMapper @Inject constructor() : Mapper() {
11 | override fun mapFrom(from: TimerStampData): TimerStampEntity {
12 | return TimerStampEntity(
13 | id = from.id,
14 | timerId = from.timerId,
15 | start = if (from.start == 0L) from.end else from.start,
16 | end = from.end
17 | )
18 | }
19 |
20 | override fun mapTo(from: TimerStampEntity): TimerStampData {
21 | return TimerStampData(
22 | id = from.id,
23 | timerId = from.timerId,
24 | start = from.start,
25 | end = from.end
26 | )
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/detekt-config.yml:
--------------------------------------------------------------------------------
1 | complexity:
2 | ComplexCondition:
3 | threshold: 6
4 | CyclomaticComplexMethod:
5 | active: false
6 | LargeClass:
7 | threshold: 2000
8 | LongMethod:
9 | active: false
10 | LongParameterList:
11 | functionThreshold: 22
12 | constructorThreshold: 22
13 | NestedBlockDepth:
14 | active: false
15 | TooManyFunctions:
16 | active: false
17 |
18 | exceptions:
19 | TooGenericExceptionCaught:
20 | active: false
21 | TooGenericExceptionThrown:
22 | active: false
23 |
24 | naming:
25 | FunctionNaming:
26 | ignoreAnnotated:
27 | - "Composable"
28 |
29 | style:
30 | LoopWithTooManyJumpStatements:
31 | maxJumpCount: 2
32 | MagicNumber:
33 | active: false
34 | ReturnCount:
35 | active: false
36 | ThrowsCount:
37 | max: 10
38 | UnnecessaryAbstractClass:
39 | active: false
40 | UnusedPrivateMember:
41 | active: false
42 |
--------------------------------------------------------------------------------
/detekt.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: "io.gitlab.arturbosch.detekt"
2 |
3 | dependencies {
4 | detektPlugins libs.detekt.formatting
5 | }
6 |
7 | detekt {
8 | toolVersion = libs.versions.detekt.get()
9 | config.setFrom(buildscript.sourceFile.getParent().toString() + "/detekt-config.yml")
10 | buildUponDefaultConfig = true
11 | }
12 |
13 | tasks.named("detekt").configure {
14 | reports {
15 | xml.required.set(false)
16 | txt.required.set(false)
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/domain/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/domain/src/main/java/xyz/aprildown/timer/domain/Mapper.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.domain
2 |
3 | abstract class Mapper {
4 | open fun mapFrom(from: Data): Entity {
5 | throw UnsupportedOperationException()
6 | }
7 |
8 | fun mapFrom(from: List): List {
9 | return from.map { mapFrom(it) }
10 | }
11 |
12 | open fun mapTo(from: Entity): Data {
13 | throw UnsupportedOperationException()
14 | }
15 |
16 | fun mapTo(from: List): List {
17 | return from.map { mapTo(it) }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/domain/src/main/java/xyz/aprildown/timer/domain/entities/AppDataEntity.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.domain.entities
2 |
3 | /**
4 | * Only for export and import to whole app's data.
5 | */
6 |
7 | data class AppDataEntity(
8 | val folders: List = emptyList(),
9 | val timers: List = emptyList(),
10 | val notifier: StepEntity.Step? = null,
11 | val timerStamps: List = emptyList(),
12 | val schedulers: List = emptyList(),
13 | val prefs: Map = emptyMap()
14 | )
15 |
--------------------------------------------------------------------------------
/domain/src/main/java/xyz/aprildown/timer/domain/entities/FolderEntity.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.domain.entities
2 |
3 | data class FolderEntity(
4 | val id: Long,
5 | val name: String
6 | ) {
7 |
8 | val isDefault: Boolean get() = id == FOLDER_DEFAULT
9 | val isTrash: Boolean get() = id == FOLDER_TRASH
10 |
11 | companion object {
12 | const val NEW_ID = 0L
13 |
14 | const val FOLDER_DEFAULT = 1L // Long.MAX_VALUE is the max value of SQLite.
15 | const val FOLDER_TRASH = 2L
16 | }
17 | }
18 |
19 | enum class FolderSortBy {
20 | AddedNewest, AddedOldest, RunNewest, RunOldest, AToZ, ZToA,
21 | }
22 |
--------------------------------------------------------------------------------
/domain/src/main/java/xyz/aprildown/timer/domain/entities/StepEntity.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.domain.entities
2 |
3 | enum class StepType {
4 | NORMAL, NOTIFIER, START, END
5 | }
6 |
7 | sealed class StepEntity {
8 | data class Step(
9 | val label: String,
10 | val length: Long,
11 | val behaviour: List = emptyList(),
12 | val type: StepType = StepType.NORMAL
13 | ) : StepEntity()
14 |
15 | data class Group(
16 | val name: String,
17 | val loop: Int,
18 | val steps: List
19 | ) : StepEntity()
20 | }
21 |
--------------------------------------------------------------------------------
/domain/src/main/java/xyz/aprildown/timer/domain/entities/TimerStampEntity.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.domain.entities
2 |
3 | import xyz.aprildown.timer.domain.TimeUtils.toEpochMilli
4 | import java.time.LocalDateTime
5 | import java.time.LocalTime
6 | import java.time.Month
7 | import java.time.YearMonth
8 |
9 | data class TimerStampEntity(
10 | val id: Int,
11 | val timerId: Int,
12 | val start: Long,
13 | val end: Long
14 | ) {
15 |
16 | val duration: Long get() = if (start == 0L) 0 else end - start
17 |
18 | constructor(timerId: Int, startTime: Long) : this(
19 | NEW_ID,
20 | timerId,
21 | startTime,
22 | System.currentTimeMillis()
23 | )
24 |
25 | companion object {
26 | const val NEW_ID = 0
27 | const val NULL_ID = 0
28 |
29 | fun getMinDateMilli(): Long {
30 | return LocalDateTime.of(
31 | YearMonth.of(2018, Month.NOVEMBER).atDay(1),
32 | LocalTime.MIN
33 | ).toEpochMilli()
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/domain/src/main/java/xyz/aprildown/timer/domain/repositories/AppPreferencesProvider.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.domain.repositories
2 |
3 | interface AppPreferencesProvider {
4 | fun getAppPreferences(): Map
5 | fun applyAppPreferences(prefs: Map)
6 | }
7 |
--------------------------------------------------------------------------------
/domain/src/main/java/xyz/aprildown/timer/domain/repositories/FolderRepository.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.domain.repositories
2 |
3 | import xyz.aprildown.timer.domain.entities.FolderEntity
4 |
5 | interface FolderRepository {
6 | suspend fun getFolders(): List
7 |
8 | suspend fun addFolder(item: FolderEntity): Long
9 |
10 | suspend fun updateFolder(item: FolderEntity): Boolean
11 |
12 | suspend fun deleteFolder(id: Long)
13 | }
14 |
--------------------------------------------------------------------------------
/domain/src/main/java/xyz/aprildown/timer/domain/repositories/NotifierRepository.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.domain.repositories
2 |
3 | import xyz.aprildown.timer.domain.entities.StepEntity
4 |
5 | interface NotifierRepository {
6 | suspend fun get(): StepEntity.Step
7 | suspend fun set(item: StepEntity.Step?): Boolean
8 |
9 | companion object {
10 | const val NAMED_DEFAULT_NOTIFIER_NAME = "default_notifier_name"
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/domain/src/main/java/xyz/aprildown/timer/domain/repositories/PreferencesRepository.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.domain.repositories
2 |
3 | interface PreferencesRepository {
4 | suspend fun contains(key: String): Boolean
5 |
6 | suspend fun getBoolean(key: String, default: Boolean): Boolean
7 | suspend fun setBoolean(key: String, value: Boolean)
8 |
9 | suspend fun getInt(key: String, default: Int): Int
10 | suspend fun setInt(key: String, value: Int)
11 |
12 | suspend fun getLong(key: String, default: Long): Long
13 | suspend fun setLong(key: String, value: Long)
14 |
15 | suspend fun getNullableString(key: String, default: String? = null): String?
16 | suspend fun getNonNullString(key: String, default: String): String
17 | suspend fun setString(key: String, value: String?)
18 | }
19 |
--------------------------------------------------------------------------------
/domain/src/main/java/xyz/aprildown/timer/domain/repositories/SchedulerExecutor.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.domain.repositories
2 |
3 | import xyz.aprildown.timer.domain.entities.SchedulerEntity
4 | import xyz.aprildown.timer.domain.usecases.scheduler.SetSchedulerEnable
5 |
6 | interface SchedulerExecutor {
7 | /**
8 | * @return scheduled job's trigger time
9 | */
10 | fun schedule(scheduler: SchedulerEntity): SetSchedulerEnable.Result
11 |
12 | /**
13 | * @return canceled job count
14 | */
15 | fun cancel(scheduler: SchedulerEntity): SetSchedulerEnable.Result
16 | }
17 |
--------------------------------------------------------------------------------
/domain/src/main/java/xyz/aprildown/timer/domain/repositories/SchedulerRepository.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.domain.repositories
2 |
3 | import xyz.aprildown.timer.domain.entities.SchedulerEntity
4 |
5 | interface SchedulerRepository {
6 | suspend fun items(): List
7 | suspend fun item(id: Int): SchedulerEntity?
8 | suspend fun add(item: SchedulerEntity): Int
9 | suspend fun save(item: SchedulerEntity): Boolean
10 | suspend fun delete(id: Int)
11 | suspend fun setSchedulerEnable(id: Int, enable: Int)
12 | suspend fun schedulersWithTimerId(id: Int): List
13 | }
14 |
--------------------------------------------------------------------------------
/domain/src/main/java/xyz/aprildown/timer/domain/repositories/TaskerEventTrigger.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.domain.repositories
2 |
3 | interface TaskerEventTrigger {
4 | fun timerStart(id: Int)
5 | fun timerEnd(id: Int)
6 | }
7 |
--------------------------------------------------------------------------------
/domain/src/main/java/xyz/aprildown/timer/domain/repositories/TimerRepository.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.domain.repositories
2 |
3 | import kotlinx.coroutines.flow.Flow
4 | import xyz.aprildown.timer.domain.entities.TimerEntity
5 | import xyz.aprildown.timer.domain.entities.TimerInfo
6 |
7 | interface TimerRepository {
8 | suspend fun items(): List
9 | suspend fun item(id: Int): TimerEntity?
10 | suspend fun add(item: TimerEntity): Int
11 | suspend fun save(item: TimerEntity): Boolean
12 | suspend fun delete(id: Int)
13 | suspend fun getTimerInfoByTimerId(timerId: Int): TimerInfo?
14 | fun getTimerInfoFlow(folderId: Long): Flow>
15 | suspend fun getTimerInfo(folderId: Long): List
16 | suspend fun changeTimerFolder(timerId: Int, folderId: Long)
17 | suspend fun moveFolderTimersToAnother(originalFolderId: Long, targetFolderId: Long)
18 | }
19 |
--------------------------------------------------------------------------------
/domain/src/main/java/xyz/aprildown/timer/domain/usecases/CoroutinesUseCase.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.domain.usecases
2 |
3 | import kotlinx.coroutines.CoroutineDispatcher
4 | import kotlinx.coroutines.withContext
5 |
6 | abstract class CoroutinesUseCase(protected val dispatcher: CoroutineDispatcher) {
7 |
8 | protected abstract suspend fun create(params: Params): Result
9 |
10 | suspend fun execute(params: Params): Result = withContext(dispatcher) {
11 | create(params)
12 | }
13 |
14 | suspend operator fun invoke(params: Params): Result = execute(params)
15 | }
16 |
17 | suspend operator fun CoroutinesUseCase.invoke(): Result = invoke(Unit)
18 |
--------------------------------------------------------------------------------
/domain/src/main/java/xyz/aprildown/timer/domain/usecases/Fruit.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.domain.usecases
2 |
3 | sealed class Fruit {
4 |
5 | data class Ripe(val data: T) : Fruit()
6 | data class Rotten(val exception: Throwable) : Fruit() {
7 | constructor(message: String) : this(IllegalStateException(message))
8 | }
9 |
10 | override fun toString(): String {
11 | return when (this) {
12 | is Ripe<*> -> "Success[data=$data]"
13 | is Rotten -> "Rotten[exception=$exception]"
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/domain/src/main/java/xyz/aprildown/timer/domain/usecases/data/NotifyDataChanged.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.domain.usecases.data
2 |
3 | import dagger.Reusable
4 | import xyz.aprildown.timer.domain.repositories.AppDataRepository
5 | import xyz.aprildown.timer.domain.utils.fireAndForget
6 | import javax.inject.Inject
7 |
8 | @Reusable
9 | class NotifyDataChanged @Inject constructor(
10 | private val repo: AppDataRepository
11 | ) {
12 | operator fun invoke() {
13 | fireAndForget {
14 | repo.notifyDataChanged()
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/domain/src/main/java/xyz/aprildown/timer/domain/usecases/notifier/GetNotifier.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.domain.usecases.notifier
2 |
3 | import dagger.Reusable
4 | import kotlinx.coroutines.CoroutineDispatcher
5 | import xyz.aprildown.timer.domain.di.IoDispatcher
6 | import xyz.aprildown.timer.domain.entities.StepEntity
7 | import xyz.aprildown.timer.domain.repositories.NotifierRepository
8 | import xyz.aprildown.timer.domain.usecases.CoroutinesUseCase
9 | import javax.inject.Inject
10 |
11 | @Reusable
12 | class GetNotifier @Inject constructor(
13 | @IoDispatcher dispatcher: CoroutineDispatcher,
14 | private val repository: NotifierRepository
15 | ) : CoroutinesUseCase(dispatcher) {
16 | override suspend fun create(params: Unit): StepEntity.Step {
17 | return repository.get()
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/domain/src/main/java/xyz/aprildown/timer/domain/usecases/notifier/SaveNotifier.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.domain.usecases.notifier
2 |
3 | import dagger.Reusable
4 | import kotlinx.coroutines.CoroutineDispatcher
5 | import xyz.aprildown.timer.domain.di.IoDispatcher
6 | import xyz.aprildown.timer.domain.entities.StepEntity
7 | import xyz.aprildown.timer.domain.repositories.AppDataRepository
8 | import xyz.aprildown.timer.domain.repositories.NotifierRepository
9 | import xyz.aprildown.timer.domain.usecases.CoroutinesUseCase
10 | import javax.inject.Inject
11 |
12 | @Reusable
13 | class SaveNotifier @Inject constructor(
14 | @IoDispatcher dispatcher: CoroutineDispatcher,
15 | private val repository: NotifierRepository,
16 | private val appDataRepository: AppDataRepository
17 | ) : CoroutinesUseCase(dispatcher) {
18 | override suspend fun create(params: StepEntity.Step): Boolean {
19 | return repository.set(params).also {
20 | appDataRepository.notifyDataChanged()
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/domain/src/main/java/xyz/aprildown/timer/domain/usecases/scheduler/GetScheduler.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.domain.usecases.scheduler
2 |
3 | import dagger.Reusable
4 | import kotlinx.coroutines.CoroutineDispatcher
5 | import xyz.aprildown.timer.domain.di.IoDispatcher
6 | import xyz.aprildown.timer.domain.entities.SchedulerEntity
7 | import xyz.aprildown.timer.domain.entities.TimerEntity
8 | import xyz.aprildown.timer.domain.repositories.SchedulerRepository
9 | import xyz.aprildown.timer.domain.usecases.CoroutinesUseCase
10 | import javax.inject.Inject
11 |
12 | @Reusable
13 | class GetScheduler @Inject constructor(
14 | @IoDispatcher dispatcher: CoroutineDispatcher,
15 | private val repository: SchedulerRepository
16 | ) : CoroutinesUseCase(dispatcher) {
17 | override suspend fun create(params: Int): SchedulerEntity? {
18 | return if (params == TimerEntity.NULL_ID) {
19 | null
20 | } else {
21 | repository.item(params)
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/domain/src/main/java/xyz/aprildown/timer/domain/usecases/scheduler/GetSchedulers.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.domain.usecases.scheduler
2 |
3 | import dagger.Reusable
4 | import kotlinx.coroutines.CoroutineDispatcher
5 | import xyz.aprildown.timer.domain.di.IoDispatcher
6 | import xyz.aprildown.timer.domain.entities.SchedulerEntity
7 | import xyz.aprildown.timer.domain.repositories.SchedulerRepository
8 | import xyz.aprildown.timer.domain.usecases.CoroutinesUseCase
9 | import javax.inject.Inject
10 |
11 | @Reusable
12 | class GetSchedulers @Inject constructor(
13 | @IoDispatcher dispatcher: CoroutineDispatcher,
14 | private val repository: SchedulerRepository
15 | ) : CoroutinesUseCase>(dispatcher) {
16 | override suspend fun create(params: Unit): List {
17 | return repository.items()
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/domain/src/main/java/xyz/aprildown/timer/domain/usecases/timer/AddTimer.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.domain.usecases.timer
2 |
3 | import dagger.Reusable
4 | import kotlinx.coroutines.CoroutineDispatcher
5 | import xyz.aprildown.timer.domain.di.IoDispatcher
6 | import xyz.aprildown.timer.domain.entities.TimerEntity
7 | import xyz.aprildown.timer.domain.repositories.AppDataRepository
8 | import xyz.aprildown.timer.domain.repositories.TimerRepository
9 | import xyz.aprildown.timer.domain.usecases.CoroutinesUseCase
10 | import javax.inject.Inject
11 |
12 | @Reusable
13 | class AddTimer @Inject constructor(
14 | @IoDispatcher dispatcher: CoroutineDispatcher,
15 | private val repository: TimerRepository,
16 | private val appDataRepository: AppDataRepository
17 | ) : CoroutinesUseCase(dispatcher) {
18 | override suspend fun create(params: TimerEntity): Int {
19 | return repository.add(params).also {
20 | appDataRepository.notifyDataChanged()
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/domain/src/main/java/xyz/aprildown/timer/domain/usecases/timer/FindTimerInfo.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.domain.usecases.timer
2 |
3 | import dagger.Reusable
4 | import kotlinx.coroutines.CoroutineDispatcher
5 | import xyz.aprildown.timer.domain.di.IoDispatcher
6 | import xyz.aprildown.timer.domain.entities.TimerEntity
7 | import xyz.aprildown.timer.domain.entities.TimerInfo
8 | import xyz.aprildown.timer.domain.repositories.TimerRepository
9 | import xyz.aprildown.timer.domain.usecases.CoroutinesUseCase
10 | import javax.inject.Inject
11 |
12 | @Reusable
13 | class FindTimerInfo @Inject constructor(
14 | @IoDispatcher dispatcher: CoroutineDispatcher,
15 | private val repository: TimerRepository
16 | ) : CoroutinesUseCase(dispatcher) {
17 | override suspend fun create(params: Int): TimerInfo? {
18 | if (params == TimerEntity.NULL_ID) return null
19 | return repository.getTimerInfoByTimerId(params)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/domain/src/main/java/xyz/aprildown/timer/domain/usecases/timer/GetTimer.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.domain.usecases.timer
2 |
3 | import dagger.Reusable
4 | import kotlinx.coroutines.CoroutineDispatcher
5 | import xyz.aprildown.timer.domain.di.IoDispatcher
6 | import xyz.aprildown.timer.domain.entities.TimerEntity
7 | import xyz.aprildown.timer.domain.repositories.TimerRepository
8 | import xyz.aprildown.timer.domain.usecases.CoroutinesUseCase
9 | import javax.inject.Inject
10 |
11 | @Reusable
12 | class GetTimer @Inject constructor(
13 | @IoDispatcher dispatcher: CoroutineDispatcher,
14 | private val repository: TimerRepository
15 | ) : CoroutinesUseCase(dispatcher) {
16 | override suspend fun create(params: Int): TimerEntity? {
17 | return if (params == TimerEntity.NULL_ID) {
18 | null
19 | } else {
20 | repository.item(params)
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/domain/src/main/java/xyz/aprildown/timer/domain/utils/AppConfig.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.domain.utils
2 |
3 | import xyz.aprildown.timer.domain.BuildConfig
4 |
5 | object AppConfig {
6 | val showFirstTimeInfo: Boolean = BuildConfig.SHOW_FIRST_TIME
7 | val openDebug: Boolean = BuildConfig.OPEN_DEBUG
8 | }
9 |
--------------------------------------------------------------------------------
/domain/src/main/java/xyz/aprildown/timer/domain/utils/AppTracker.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.domain.utils
2 |
3 | import android.content.Context
4 |
5 | interface AppTracker {
6 | fun init(context: Context)
7 | fun trackError(throwable: Throwable, message: String? = null)
8 | suspend fun hasCrashedInLastSession(): Boolean
9 | }
10 |
--------------------------------------------------------------------------------
/domain/src/main/java/xyz/aprildown/timer/domain/utils/ApplicationScope.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.domain.utils
2 |
3 | import kotlinx.coroutines.CoroutineScope
4 | import kotlinx.coroutines.Dispatchers
5 | import kotlinx.coroutines.SupervisorJob
6 | import kotlinx.coroutines.launch
7 | import kotlin.coroutines.CoroutineContext
8 | import kotlin.coroutines.EmptyCoroutineContext
9 |
10 | private val applicationScope by lazy {
11 | CoroutineScope(SupervisorJob() + Dispatchers.Default)
12 | }
13 |
14 | fun fireAndForget(
15 | context: CoroutineContext = EmptyCoroutineContext,
16 | block: suspend () -> Unit
17 | ) {
18 | applicationScope.launch(context) {
19 | block.invoke()
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/domain/src/main/java/xyz/aprildown/timer/domain/utils/FileUtils.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.domain.utils
2 |
3 | import java.io.File
4 |
5 | fun File.ensureDirExistence(): File {
6 | if (!exists()) mkdirs()
7 | return this
8 | }
9 |
10 | fun File.ensureNewFile(): File {
11 | parentFile?.ensureDirExistence()
12 | if (exists()) delete()
13 | createNewFile()
14 | return this
15 | }
16 |
--------------------------------------------------------------------------------
/domain/src/main/java/xyz/aprildown/tools/helper/SharedPreferencesHelper.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.tools.helper
2 |
3 | import android.content.Context
4 | import android.content.SharedPreferences
5 | import androidx.preference.PreferenceManager
6 |
7 | /**
8 | * This file is just for backward-compatibility.
9 | */
10 |
11 | val Context.safeSharedPreference: SharedPreferences
12 | get() = PreferenceManager.getDefaultSharedPreferences(this)
13 |
--------------------------------------------------------------------------------
/domain/src/test/java/xyz/aprildown/timer/domain/usecases/scheduler/SchedulerRepeatContentTest.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.domain.usecases.scheduler
2 |
3 | import org.junit.Assert.assertEquals
4 | import org.junit.Test
5 | import xyz.aprildown.timer.domain.entities.SchedulerEntity
6 | import java.security.SecureRandom
7 |
8 | class SchedulerRepeatContentTest {
9 |
10 | @Test
11 | fun test() {
12 | val random = SecureRandom()
13 | List(50) { random.nextInt(128) }.forEach {
14 | val days = SchedulerEntity.everyDayToDays(it)
15 | val value = SchedulerEntity.daysToEveryDay(days)
16 | assertEquals(it, value)
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/featureGraphic.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timer-machine/timer-machine-android/699caa3dc0065722a4e6f0a76b02009e253dc280/fastlane/metadata/android/en-US/images/featureGraphic.jpg
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timer-machine/timer-machine-android/699caa3dc0065722a4e6f0a76b02009e253dc280/fastlane/metadata/android/en-US/images/icon.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timer-machine/timer-machine-android/699caa3dc0065722a4e6f0a76b02009e253dc280/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timer-machine/timer-machine-android/699caa3dc0065722a4e6f0a76b02009e253dc280/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/phoneScreenshots/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timer-machine/timer-machine-android/699caa3dc0065722a4e6f0a76b02009e253dc280/fastlane/metadata/android/en-US/images/phoneScreenshots/3.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/phoneScreenshots/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timer-machine/timer-machine-android/699caa3dc0065722a4e6f0a76b02009e253dc280/fastlane/metadata/android/en-US/images/phoneScreenshots/4.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/sevenInchScreenshots/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timer-machine/timer-machine-android/699caa3dc0065722a4e6f0a76b02009e253dc280/fastlane/metadata/android/en-US/images/sevenInchScreenshots/1.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/tenInchScreenshots/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timer-machine/timer-machine-android/699caa3dc0065722a4e6f0a76b02009e253dc280/fastlane/metadata/android/en-US/images/tenInchScreenshots/1.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/short_description.txt:
--------------------------------------------------------------------------------
1 | A highly customizable interval timer
2 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/title.txt:
--------------------------------------------------------------------------------
1 | TimeR Machine
2 |
--------------------------------------------------------------------------------
/flavor-google/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/flavor-google/src/main/java/xyz/aprildown/timer/app/analytics/AnalyticsModule.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.app.analytics
2 |
3 | import dagger.Binds
4 | import dagger.Module
5 | import dagger.hilt.InstallIn
6 | import dagger.hilt.components.SingletonComponent
7 | import xyz.aprildown.timer.domain.utils.AppTracker
8 |
9 | @Module
10 | @InstallIn(SingletonComponent::class)
11 | internal abstract class AnalyticsModule {
12 | @Binds
13 | abstract fun bindAppTracker(impl: AppTrackerImpl): AppTracker
14 | }
15 |
--------------------------------------------------------------------------------
/flavor-google/src/main/java/xyz/aprildown/timer/app/analytics/AppTrackerImpl.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.app.analytics
2 |
3 | import android.content.Context
4 | import com.google.firebase.crashlytics.ktx.crashlytics
5 | import com.google.firebase.ktx.Firebase
6 | import dagger.Reusable
7 | import xyz.aprildown.timer.domain.utils.AppTracker
8 | import javax.inject.Inject
9 |
10 | @Reusable
11 | internal class AppTrackerImpl @Inject constructor() : AppTracker {
12 | override fun init(context: Context) = Unit
13 |
14 | override fun trackError(throwable: Throwable, message: String?) {
15 | if (message != null) {
16 | Firebase.crashlytics.recordException(IllegalStateException(message, throwable))
17 | } else {
18 | Firebase.crashlytics.recordException(throwable)
19 | }
20 | }
21 |
22 | override suspend fun hasCrashedInLastSession(): Boolean {
23 | return Firebase.crashlytics.didCrashOnPreviousExecution()
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/flavor-google/src/main/java/xyz/aprildown/timer/flavor/google/utils/FileUtils.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.flavor.google.utils
2 |
3 | import java.io.File
4 |
5 | internal fun File.ensureNewFile(): File {
6 | if (exists()) {
7 | delete()
8 | }
9 | createNewFile()
10 | return this
11 | }
12 |
--------------------------------------------------------------------------------
/flavor-google/src/main/java/xyz/aprildown/timer/flavor/google/utils/FirebaseExceptionUtils.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.flavor.google.utils
2 |
3 | internal fun Throwable.causeFirstMessage(): String {
4 | fun Throwable.getCurrentMessage(): String? {
5 | return localizedMessage ?: message
6 | }
7 |
8 | return cause?.getCurrentMessage() ?: getCurrentMessage().toString()
9 | }
10 |
--------------------------------------------------------------------------------
/flavor-google/src/main/java/xyz/aprildown/timer/flavor/google/utils/FirebaseStorageSetUp.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.flavor.google.utils
2 |
3 | import com.google.firebase.ktx.Firebase
4 | import com.google.firebase.storage.ktx.storage
5 |
6 | internal fun setUpFirebaseStorage() {
7 | Firebase.storage.run {
8 | // We don't retry because our upload, download and query operations are synchronized.
9 | maxUploadRetryTimeMillis = 0
10 | maxDownloadRetryTimeMillis = 0
11 | maxOperationRetryTimeMillis = 0
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/flavor-google/src/main/res/drawable/backup_auto_backup.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/flavor-google/src/main/res/drawable/backup_cloud_restore.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/flavor-google/src/main/res/drawable/backup_cloud_state.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/flavor-google/src/main/res/drawable/backup_delete.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/flavor-google/src/main/res/drawable/backup_sign_in.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/flavor-google/src/main/res/drawable/backup_sign_out.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/flavor-google/src/main/res/layout/dialog_loading.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/flavor-google/src/main/res/layout/list_item_restore.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
--------------------------------------------------------------------------------
/flavor-google/src/main/res/menu/flavor_help.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
--------------------------------------------------------------------------------
/flavor-google/src/main/res/navigation/backup_graph.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
12 |
17 |
18 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timer-machine/timer-machine-android/699caa3dc0065722a4e6f0a76b02009e253dc280/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/images/google-play-badge.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timer-machine/timer-machine-android/699caa3dc0065722a4e6f0a76b02009e253dc280/images/google-play-badge.png
--------------------------------------------------------------------------------
/images/showcase.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timer-machine/timer-machine-android/699caa3dc0065722a4e6f0a76b02009e253dc280/images/showcase.jpg
--------------------------------------------------------------------------------
/presentation/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/presentation/src/main/java/xyz/aprildown/timer/presentation/BaseViewModel.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.presentation
2 |
3 | import androidx.annotation.CallSuper
4 | import androidx.lifecycle.ViewModel
5 | import kotlinx.coroutines.CoroutineDispatcher
6 | import kotlinx.coroutines.CoroutineScope
7 | import kotlinx.coroutines.SupervisorJob
8 | import kotlinx.coroutines.cancel
9 | import kotlin.coroutines.CoroutineContext
10 |
11 | abstract class SimpleViewModel : ViewModel()
12 |
13 | abstract class BaseViewModel(
14 | mainDispatcher: CoroutineDispatcher
15 | ) : SimpleViewModel(), CoroutineScope {
16 |
17 | override val coroutineContext: CoroutineContext = SupervisorJob() + mainDispatcher
18 |
19 | @CallSuper
20 | override fun onCleared() {
21 | cancel()
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/presentation/src/main/java/xyz/aprildown/timer/presentation/StreamMachineIntentProvider.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.presentation
2 |
3 | import android.content.Intent
4 | import xyz.aprildown.timer.presentation.stream.TimerIndex
5 |
6 | interface StreamMachineIntentProvider {
7 | fun bindIntent(): Intent
8 | fun startIntent(id: Int, index: TimerIndex? = null): Intent
9 | fun pauseIntent(id: Int): Intent
10 | fun decreIntent(id: Int): Intent
11 | fun increIntent(id: Int): Intent
12 | fun moveIntent(id: Int, index: TimerIndex): Intent
13 | fun resetIntent(id: Int): Intent
14 | fun adjustTimeIntent(id: Int, amount: Long): Intent
15 | }
16 |
--------------------------------------------------------------------------------
/presentation/src/main/java/xyz/aprildown/timer/presentation/di/ViewModelModule.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.presentation.di
2 |
3 | import android.app.Application
4 | import dagger.Module
5 | import dagger.Provides
6 | import dagger.hilt.InstallIn
7 | import dagger.hilt.components.SingletonComponent
8 | import xyz.aprildown.timer.presentation.R
9 | import javax.inject.Named
10 |
11 | @Module
12 | @InstallIn(SingletonComponent::class)
13 | internal object ViewModelModule {
14 | const val DEFAULT_TIMER_NAME = "default_timer_name"
15 |
16 | @Provides
17 | @Named(DEFAULT_TIMER_NAME)
18 | fun provideDefaultTimerName(context: Application): String {
19 | return context.getString(R.string.edit_default_timer_name)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/presentation/src/main/java/xyz/aprildown/timer/presentation/intro/IntroViewModel.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.presentation.intro
2 |
3 | import dagger.hilt.android.lifecycle.HiltViewModel
4 | import kotlinx.coroutines.CoroutineDispatcher
5 | import kotlinx.coroutines.launch
6 | import xyz.aprildown.timer.domain.di.MainDispatcher
7 | import xyz.aprildown.timer.domain.entities.TimerEntity
8 | import xyz.aprildown.timer.domain.usecases.timer.AddTimer
9 | import xyz.aprildown.timer.presentation.BaseViewModel
10 | import javax.inject.Inject
11 |
12 | @HiltViewModel
13 | class IntroViewModel @Inject constructor(
14 | @MainDispatcher mainDispatcher: CoroutineDispatcher,
15 | private val addTimer: AddTimer,
16 | ) : BaseViewModel(mainDispatcher) {
17 |
18 | private val addedTimers = mutableListOf()
19 |
20 | fun addSampleTimer(timer: TimerEntity) = launch {
21 | if (timer in addedTimers) return@launch
22 |
23 | addTimer(timer)
24 |
25 | addedTimers += timer
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/presentation/src/main/java/xyz/aprildown/timer/presentation/stream/StreamState.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.presentation.stream
2 |
3 | enum class StreamState {
4 | RUNNING, PAUSED, RESET;
5 |
6 | val isRunning get() = this == RUNNING
7 | val isPaused get() = this == PAUSED
8 | val isReset get() = this == RESET
9 | }
10 |
--------------------------------------------------------------------------------
/presentation/src/main/java/xyz/aprildown/timer/presentation/stream/TimerMachineListener.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.presentation.stream
2 |
3 | /**
4 | * timerId will be 0 if the listener is registered with addListener.
5 | * It will be the real timerId if it's registered with addAllListener.
6 | */
7 | interface TimerMachineListener {
8 | fun begin(timerId: Int)
9 | fun started(timerId: Int, index: TimerIndex)
10 | fun paused(timerId: Int)
11 | fun updated(timerId: Int, time: Long)
12 | fun finished(timerId: Int)
13 | fun end(timerId: Int, forced: Boolean)
14 | }
15 |
--------------------------------------------------------------------------------
/presentation/src/main/java/xyz/aprildown/timer/presentation/stream/task/TickListener.kt:
--------------------------------------------------------------------------------
1 | package xyz.aprildown.timer.presentation.stream.task
2 |
3 | internal interface TickListener {
4 | /**
5 | * Don't use newTime as counter. It may tick twice in one second.
6 | */
7 | fun onNewTime(newTime: Long)
8 | }
9 |
--------------------------------------------------------------------------------
/presentation/src/main/res/values/ids.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------