├── .gitignore ├── .idea ├── .gitignore ├── codeStyles │ ├── Project.xml │ └── codeStyleConfig.xml ├── compiler.xml ├── inspectionProfiles │ └── Project_Default.xml ├── jarRepositories.xml ├── kotlinc.xml └── misc.xml ├── LICENSE ├── PRIVACY-POLICY.md ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── sztorm │ │ └── notecalendar │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── icon_launcher-playstore.png │ ├── java │ │ └── com │ │ │ └── sztorm │ │ │ └── notecalendar │ │ │ ├── AppNotificationManager.kt │ │ │ ├── AppPermissionCode.kt │ │ │ ├── AppPermissionManager.kt │ │ │ ├── AppSettings.kt │ │ │ ├── AppSharedData.kt │ │ │ ├── Arguments.kt │ │ │ ├── ConfirmationPreference.kt │ │ │ ├── CustomThemeSettingsFragment.kt │ │ │ ├── DayFragment.kt │ │ │ ├── DayItem.kt │ │ │ ├── DayListAdapter.kt │ │ │ ├── DayNoteAddFragment.kt │ │ │ ├── DayNoteEmptyFragment.kt │ │ │ ├── DayNoteFragment.kt │ │ │ ├── DefaultActivityLifecycleCallbacks.kt │ │ │ ├── FragmentSetter.kt │ │ │ ├── LogTags.kt │ │ │ ├── MainActivity.kt │ │ │ ├── MainFragmentType.kt │ │ │ ├── MonthFragment.kt │ │ │ ├── NoteCalendarApplication.kt │ │ │ ├── NoteData.kt │ │ │ ├── NoteNotificationData.kt │ │ │ ├── NoteNotificationReceiver.kt │ │ │ ├── OnSwipeListener.kt │ │ │ ├── RootSettingsFragment.kt │ │ │ ├── ScheduleNoteNotificationArguments.kt │ │ │ ├── StartingViewType.kt │ │ │ ├── ThemePaintable.kt │ │ │ ├── ThemePainter.kt │ │ │ ├── ThemeValues.kt │ │ │ ├── ThemedSimpleListDialog.kt │ │ │ ├── WeekDayFragment.kt │ │ │ ├── WeekDayTextColors.kt │ │ │ ├── WeekFragment.kt │ │ │ ├── calendarview │ │ │ ├── DayViewContainer.kt │ │ │ └── ThemedDayBinder.kt │ │ │ ├── eventsubjects │ │ │ ├── DialogCancelSubject.kt │ │ │ ├── DialogClickSubject.kt │ │ │ ├── DialogDismissSubject.kt │ │ │ ├── DialogKeyListenerSubject.kt │ │ │ ├── DialogMultiChoiceClickSubject.kt │ │ │ ├── DialogShowSubject.kt │ │ │ ├── NegativeButtonClickSubject.kt │ │ │ ├── NeutralButtonClickSubject.kt │ │ │ ├── PositiveButtonClickSubject.kt │ │ │ ├── ValueChangeSubject.kt │ │ │ └── ViewCreatedSubject.kt │ │ │ ├── helpers │ │ │ ├── AlarmManagerHelper.kt │ │ │ ├── ColorStateListHelper.kt │ │ │ ├── ContextHelper.kt │ │ │ ├── DateHelper.kt │ │ │ ├── DialogFragmentHelper.kt │ │ │ ├── DrawableHelper.kt │ │ │ ├── IntentHelper.kt │ │ │ ├── PreferenceFragmentCompatHelper.kt │ │ │ └── ViewHelper.kt │ │ │ ├── repositories │ │ │ └── NoteRepository.kt │ │ │ ├── simplelistpreference │ │ │ ├── SimpleListDialog.kt │ │ │ ├── SimpleListDialogAdapter.kt │ │ │ └── SimpleListPreference.kt │ │ │ ├── themedpreferences │ │ │ ├── ThemedColorPickerPreference.kt │ │ │ ├── ThemedConfirmationPreference.kt │ │ │ ├── ThemedHeaderPreference.kt │ │ │ ├── ThemedPreference.kt │ │ │ ├── ThemedPreferenceCategory.kt │ │ │ ├── ThemedSimpleListPreference.kt │ │ │ ├── ThemedSwitchPreference.kt │ │ │ └── ThemedTimePickerPreference.kt │ │ │ └── timepickerpreference │ │ │ ├── TimePickerDialog.kt │ │ │ └── TimePickerPreference.kt │ └── res │ │ ├── anim │ │ ├── anim_immediate.xml │ │ ├── anim_in.xml │ │ ├── anim_in_left.xml │ │ ├── anim_in_note.xml │ │ ├── anim_in_right.xml │ │ ├── anim_out.xml │ │ ├── anim_out_left.xml │ │ ├── anim_out_note.xml │ │ └── anim_out_right.xml │ │ ├── drawable-v24 │ │ └── icon_launcher_background.xml │ │ ├── drawable │ │ ├── bg_circle.xml │ │ ├── bg_note.xml │ │ ├── bg_note_layer0.xml │ │ ├── bg_note_layer1.xml │ │ ├── bg_outline_rounded_rectangle.xml │ │ ├── bg_outline_rounded_rectangle_pressed.xml │ │ ├── cursor_edit_text.xml │ │ ├── icon_add.xml │ │ ├── icon_arrow_left.xml │ │ ├── icon_arrow_left_layer0.xml │ │ ├── icon_arrow_left_layer1.xml │ │ ├── icon_calendar_day.xml │ │ ├── icon_calendar_month.xml │ │ ├── icon_calendar_week.xml │ │ ├── icon_cancel.xml │ │ ├── icon_delete.xml │ │ ├── icon_edit.xml │ │ ├── icon_launcher_foreground.xml │ │ ├── icon_note.xml │ │ ├── icon_note_add.xml │ │ ├── icon_palette.xml │ │ ├── icon_settings.xml │ │ ├── icon_undo.xml │ │ ├── selector_week_day.xml │ │ ├── selector_week_day_selected.xml │ │ ├── text_select_handle_left.xml │ │ ├── text_select_handle_middle.xml │ │ └── text_select_handle_right.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── calendar_day.xml │ │ ├── calendar_week_day_bar.xml │ │ ├── fragment_day.xml │ │ ├── fragment_day_note.xml │ │ ├── fragment_day_note_add.xml │ │ ├── fragment_day_note_empty.xml │ │ ├── fragment_month.xml │ │ ├── fragment_simple_list_dialog.xml │ │ ├── fragment_simple_list_dialog_item.xml │ │ ├── fragment_timepicker.xml │ │ ├── fragment_week.xml │ │ ├── fragment_week_day.xml │ │ ├── preference_themed_switch_widget.xml │ │ └── preference_timepicker.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── icon_launcher.xml │ │ └── icon_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── icon_launcher.webp │ │ └── icon_launcher_round.webp │ │ ├── mipmap-mdpi │ │ ├── icon_launcher.webp │ │ └── icon_launcher_round.webp │ │ ├── mipmap-xhdpi │ │ ├── icon_launcher.webp │ │ └── icon_launcher_round.webp │ │ ├── mipmap-xxhdpi │ │ ├── icon_launcher.webp │ │ └── icon_launcher_round.webp │ │ ├── mipmap-xxxhdpi │ │ ├── icon_launcher.webp │ │ └── icon_launcher_round.webp │ │ ├── values-fr │ │ └── strings.xml │ │ ├── values-nb-rNO │ │ └── strings.xml │ │ ├── values-night │ │ └── themes.xml │ │ ├── values-pl │ │ └── strings.xml │ │ ├── values-pt │ │ └── strings.xml │ │ ├── values-ro │ │ └── strings.xml │ │ ├── values-vi │ │ └── strings.xml │ │ ├── values │ │ ├── arrays.xml │ │ ├── attrs.xml │ │ ├── colors.xml │ │ ├── simplelistpref_attrs.xml │ │ ├── strings.xml │ │ ├── styles.xml │ │ └── themes.xml │ │ └── xml │ │ ├── custom_theme_settings.xml │ │ ├── data_extraction_rules.xml │ │ └── root_settings.xml │ └── test │ └── java │ └── com │ └── sztorm │ └── notecalendar │ └── ExampleUnitTest.kt ├── assets ├── icon.png ├── pic-01.png ├── pic-02.png ├── pic-03.png ├── pic-04.png ├── pic-05.png ├── pic-06.png ├── pic-07.png ├── pic-08.png ├── pic-09.png └── pic-10.png ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # macOS files 2 | .DS_Store 3 | 4 | # Built application files 5 | *.apk 6 | *.aar 7 | *.ap_ 8 | *.aab 9 | 10 | # Files for the ART/Dalvik VM 11 | *.dex 12 | 13 | # Java class files 14 | *.class 15 | 16 | # Generated files 17 | bin/ 18 | gen/ 19 | out/ 20 | # Uncomment the following line in case you need and you don't have the release build type files in your app 21 | release/ 22 | 23 | # Gradle files 24 | .gradle/ 25 | build/ 26 | 27 | # Local configuration file (sdk path, etc) 28 | local.properties 29 | /local.properties 30 | 31 | # Proguard folder generated by Eclipse 32 | proguard/ 33 | 34 | # Log Files 35 | *.log 36 | 37 | # Android Studio Navigation editor temp files 38 | .navigation/ 39 | 40 | # Android Studio captures folder 41 | captures/ 42 | 43 | # IntelliJ 44 | *.iml 45 | .idea/workspace.xml 46 | .idea/tasks.xml 47 | .idea/gradle.xml 48 | .idea/assetWizardSettings.xml 49 | .idea/dictionaries 50 | .idea/libraries 51 | # Android Studio 3 in .gitignore file. 52 | .idea/caches 53 | .idea/modules.xml 54 | # Comment next line if keeping position of elements in Navigation Editor is relevant for you 55 | .idea/navEditor.xml 56 | # IDE junk 57 | .idea/deploymentTargetSelector.xml 58 | .idea/other.xml 59 | 60 | # Keystore files 61 | *.jks 62 | *.keystore 63 | 64 | # External native build folder generated in Android Studio 2.2 and later 65 | .externalNativeBuild 66 | .cxx/ 67 | 68 | # Google Services (e.g. APIs or Firebase) 69 | google-services.json 70 | 71 | # Freeline 72 | freeline.py 73 | freeline/ 74 | freeline_project_description.json 75 | 76 | # fastlane 77 | fastlane/report.xml 78 | fastlane/Preview.html 79 | fastlane/screenshots 80 | fastlane/test_output 81 | fastlane/readme.md 82 | 83 | # Version control 84 | vcs.xml 85 | 86 | # lint 87 | lint/intermediates/ 88 | lint/generated/ 89 | lint/outputs/ 90 | lint/tmp/ 91 | lint/reports/ 92 | 93 | # Android Profiling 94 | *.hprof 95 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | /deploymentTargetDropDown.xml -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 119 | 120 | 122 | 123 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 14 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | 29 | 30 | 34 | 35 | -------------------------------------------------------------------------------- /.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Sztorm 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /PRIVACY-POLICY.md: -------------------------------------------------------------------------------- 1 | ## Privacy Policy 2 | 3 | NoteCalendar does not collect, transmit, distribute or sell your data. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 |

NoteCalendar

3 | 4 | *Note Calendar* is a simple offline app which allows to manage notes for different days and offers a 5 | quick way to reach each of them. 6 | 7 | Minimum Android version: 5.0 (Lollipop, API level 21) 8 | 9 | [Get it on IzzyOnDroid](https://apt.izzysoft.de/fdroid/index/apk/com.sztorm.notecalendar) 10 | [Get it on Google Play](https://play.google.com/store/apps/details?id=com.sztorm.notecalendar) 11 | 12 | ## Screenshots 13 | 14 | note calendar picture 1 note calendar picture 2 note calendar picture 3 note calendar picture 4 15 | 16 | note calendar picture 5 note calendar picture 6 note calendar picture 7 note calendar picture 8 17 | 18 | note calendar picture 9 note calendar picture 10 19 | 20 | ## Features 21 | 22 | * Day view 23 | * This view is used to add, edit or delete note 24 | * Week view 25 | * Month view 26 | * Days that constains a note are marked with a ring 27 | * Day that is currently selected is marked with a solid circle 28 | * Today's day is marked with a different color of text 29 | * Long press on a day's number allow to quickly add or edit note for that day 30 | * Settings view 31 | * Theming 32 | * Setting custom theme which includes 10 modifiable colors 33 | * Setting light theme 34 | * Setting dark theme 35 | * Setting default theme based on system settings 36 | * Notes deletion 37 | * Notifications 38 | * When turned on, a notification will appear at notification time 39 | * Setting first day of week 40 | * Setting starting view 41 | 42 | ## Translations 43 | 44 | Translations state 45 | 46 | 47 | You can help with translations on https://hosted.weblate.org/projects/note-calendar/. 48 | 49 | Accessible translation platform is available thanks to Weblate and their support for libre projects. Hosting such platform costs money, if you want to help the Weblate project, you can donate [here](https://weblate.org/pl/donate/). 50 | 51 | ## License 52 | 53 | *NoteCalendar* is licensed under the MIT license. 54 | 55 | [More about license](LICENSE) 56 | 57 | [Privacy Policy](PRIVACY-POLICY.md) -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'kotlin-android' 4 | id "com.mikepenz.aboutlibraries.plugin" version "10.3.0" 5 | id 'org.jetbrains.kotlin.android' 6 | } 7 | 8 | android { 9 | defaultConfig { 10 | applicationId "com.sztorm.notecalendar" 11 | compileSdk 34 12 | minSdkVersion 21 13 | targetSdkVersion 34 14 | versionCode 11 15 | versionName "1.0.10" 16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 17 | } 18 | buildFeatures { 19 | viewBinding true 20 | buildConfig = true 21 | } 22 | buildTypes { 23 | release { 24 | minifyEnabled false 25 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 26 | } 27 | } 28 | compileOptions { 29 | coreLibraryDesugaringEnabled true 30 | sourceCompatibility JavaVersion.VERSION_17 31 | targetCompatibility JavaVersion.VERSION_17 32 | } 33 | dependencies { 34 | coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.2' 35 | } 36 | kotlin { 37 | jvmToolchain(17) 38 | } 39 | namespace 'com.sztorm.notecalendar' 40 | } 41 | 42 | aboutLibraries { 43 | excludeFields = ['generated'] 44 | } 45 | 46 | dependencies { 47 | implementation 'androidx.core:core-ktx:1.13.1' 48 | implementation 'androidx.appcompat:appcompat:1.7.0' 49 | implementation 'androidx.preference:preference-ktx:1.2.1' 50 | implementation 'androidx.constraintlayout:constraintlayout:2.1.4' 51 | implementation 'androidx.legacy:legacy-support-v4:1.0.0' 52 | implementation "androidx.cardview:cardview:1.0.0" 53 | implementation "androidx.recyclerview:recyclerview:1.3.2" 54 | implementation 'com.google.android.material:material:1.12.0' 55 | implementation 'com.mikepenz:aboutlibraries-core:10.3.0' 56 | implementation 'com.mikepenz:aboutlibraries:10.3.0' 57 | implementation 'com.github.satyan:sugar:1.5' 58 | implementation 'com.github.Sztorm:ColorPickerPreference:2.0.5-fork.2' 59 | implementation 'com.github.Sztorm:TimePicker:1.2.1' 60 | implementation 'com.github.kizitonwose:CalendarView:1.0.4' 61 | implementation 'com.jakewharton.timber:timber:5.0.1' 62 | testImplementation 'junit:junit:4.13.2' 63 | androidTestImplementation 'androidx.test.ext:junit:1.2.1' 64 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1' 65 | } 66 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/androidTest/java/com/sztorm/notecalendar/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.sztorm.notecalendar", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 9 | 10 | 11 | 22 | 23 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 37 | 38 | 41 | 44 | 47 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /app/src/main/icon_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sztorm/NoteCalendar/5b9466f9836be0cb5e21d195752bffacfbbaff9b/app/src/main/icon_launcher-playstore.png -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/AppPermissionCode.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar 2 | 3 | import android.Manifest 4 | import android.os.Build 5 | 6 | @JvmInline 7 | value class AppPermissionCode(val value: Int) { 8 | fun getPermissionArray(): Array = when (this) { 9 | NOTIFICATIONS -> { 10 | when { 11 | Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -> arrayOf( 12 | Manifest.permission.POST_NOTIFICATIONS 13 | ) 14 | 15 | Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> arrayOf( 16 | Manifest.permission.SCHEDULE_EXACT_ALARM 17 | ) 18 | 19 | else -> emptyArray() 20 | } 21 | } 22 | 23 | else -> emptyArray() 24 | } 25 | 26 | companion object { 27 | val NOTIFICATIONS 28 | get() = AppPermissionCode(0) 29 | } 30 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/AppSharedData.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar 2 | 3 | import java.time.LocalDate 4 | 5 | class AppSharedData(var viewedDate: LocalDate) -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/Arguments.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar 2 | 3 | interface Arguments 4 | 5 | object CreateOrEditNoteRequest : Arguments 6 | 7 | class UndoNoteDeleteOption(val note: NoteData) : Arguments -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/ConfirmationPreference.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import androidx.appcompat.app.AlertDialog 6 | import androidx.preference.Preference 7 | 8 | open class ConfirmationPreference: Preference { 9 | protected var mConfirmationDialog: AlertDialog = createConfirmationDialog() 10 | 11 | val confirmationDialog: AlertDialog 12 | get() = mConfirmationDialog 13 | 14 | constructor(context: Context) : super(context) 15 | 16 | constructor(context: Context, attributeSet: AttributeSet) : 17 | super(context, attributeSet) 18 | 19 | constructor(context: Context, attributeSet: AttributeSet, defStyle: Int) : 20 | super(context, attributeSet, defStyle) 21 | 22 | private fun createConfirmationDialog(): AlertDialog { 23 | val dialog = AlertDialog.Builder(context) 24 | dialog.setTitle(context.getString(R.string.Settings_DeleteAllNotes_Alert_Title)) 25 | dialog.setMessage(context.getString(R.string.Settings_DeleteAllNotes_Alert_Message)) 26 | dialog.setCancelable(true) 27 | dialog.setPositiveButton(context.getString(R.string.Confirm)) { _, _ -> } 28 | dialog.setNegativeButton(context.getString(R.string.Cancel)) { 29 | dlg, _ -> dlg.cancel() 30 | } 31 | return dialog.create() 32 | } 33 | 34 | override fun onClick() { 35 | mConfirmationDialog.show() 36 | } 37 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/DayFragment.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar 2 | 3 | import android.annotation.SuppressLint 4 | import android.content.Context 5 | import android.os.Bundle 6 | import android.view.LayoutInflater 7 | import android.view.View 8 | import android.view.ViewGroup 9 | import androidx.fragment.app.Fragment 10 | import com.sztorm.notecalendar.databinding.FragmentDayBinding 11 | import com.sztorm.notecalendar.helpers.DateHelper.Companion.toLocalizedString 12 | import com.sztorm.notecalendar.helpers.DateHelper.Companion.toLocalizedStringGenitiveCase 13 | import com.sztorm.notecalendar.repositories.NoteRepository 14 | import java.time.LocalDate 15 | 16 | class DayFragment : Fragment() { 17 | private lateinit var binding: FragmentDayBinding 18 | private lateinit var fragmentSetter: FragmentSetter 19 | private lateinit var mainActivity: MainActivity 20 | private lateinit var _swipeListener: SwipeListener 21 | private var postInitArgs: Arguments? = null 22 | val swipeListener: SwipeListener 23 | get() = _swipeListener 24 | 25 | fun postInit(args: Arguments?) { 26 | postInitArgs = args 27 | } 28 | 29 | override fun onAttach(context: Context) { 30 | super.onAttach(context) 31 | mainActivity = activity as MainActivity 32 | } 33 | 34 | override fun onCreateView( 35 | inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? 36 | ): View { 37 | fragmentSetter = FragmentSetter( 38 | childFragmentManager, 39 | R.id.dayNoteFragmentContainer, 40 | R.anim.anim_in_note, 41 | R.anim.anim_out_note 42 | ) 43 | binding = FragmentDayBinding.inflate(inflater, container, false) 44 | _swipeListener = SwipeListener(mainActivity, binding.root.context) 45 | val viewedDate: LocalDate = mainActivity.sharedData.viewedDate 46 | 47 | setTheme() 48 | setNoteFragmentOnCreate(viewedDate) 49 | setTouchListener() 50 | setLabelsText(viewedDate) 51 | 52 | return binding.root 53 | } 54 | 55 | fun setFragment(fragment: Fragment) = fragmentSetter.setFragment(fragment) 56 | 57 | private fun setNoteFragmentOnCreate(date: LocalDate) { 58 | val possibleNote: NoteData? = NoteRepository.getByDate(date) 59 | 60 | if (possibleNote == null) { 61 | fragmentSetter.setFragment( 62 | DayNoteEmptyFragment(this, postInitArgs), 63 | resAnimIn = R.anim.anim_immediate, 64 | resAnimOut = R.anim.anim_immediate 65 | ) 66 | } else { 67 | fragmentSetter.setFragment( 68 | DayNoteFragment(this, possibleNote, postInitArgs) 69 | ) 70 | } 71 | postInitArgs = null 72 | } 73 | 74 | private fun setLabelsText(date: LocalDate) { 75 | binding.lblDayOfMonth.text = date.dayOfMonth.toString() 76 | binding.lblDayOfWeek.text = date.dayOfWeek.toLocalizedString(mainActivity) 77 | binding.lblMonth.text = date.month.toLocalizedStringGenitiveCase(mainActivity) 78 | } 79 | 80 | @SuppressLint("ClickableViewAccessibility") 81 | private fun setTouchListener() = binding.root.setOnTouchListener(_swipeListener) 82 | 83 | private fun setTheme() { 84 | val themePainter: ThemePainter = mainActivity.themePainter 85 | val themeValues: ThemeValues = themePainter.values 86 | 87 | binding.lblDayOfMonth.setTextColor(themeValues.textColor) 88 | binding.lblDayOfWeek.setTextColor(themeValues.textColor) 89 | binding.lblMonth.setTextColor(themeValues.textColor) 90 | } 91 | 92 | class SwipeListener( 93 | private val mainActivity: MainActivity, context: Context 94 | ) : OnSwipeListener(context) { 95 | override fun onSwipeLeft() { 96 | super.onSwipeLeft() 97 | val sharedData = mainActivity.sharedData 98 | sharedData.viewedDate = sharedData.viewedDate.plusDays(1) 99 | mainActivity.setMainFragment( 100 | MainFragmentType.DAY, R.anim.anim_in_left, R.anim.anim_out_left 101 | ) 102 | } 103 | 104 | override fun onSwipeRight() { 105 | super.onSwipeRight() 106 | val sharedData = mainActivity.sharedData 107 | sharedData.viewedDate = sharedData.viewedDate.minusDays(1) 108 | mainActivity.setMainFragment( 109 | MainFragmentType.DAY, R.anim.anim_in_right, R.anim.anim_out_right 110 | ) 111 | } 112 | } 113 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/DayItem.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar 2 | 3 | import java.time.LocalDate 4 | 5 | data class DayItem(val date: LocalDate, val dayOfMonth: String, val dayOfWeek: String) -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/DayListAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar 2 | 3 | import android.view.LayoutInflater 4 | import android.view.View 5 | import android.view.ViewGroup 6 | import android.widget.LinearLayout 7 | import android.widget.TextView 8 | import androidx.recyclerview.widget.RecyclerView 9 | import com.sztorm.notecalendar.databinding.FragmentWeekDayBinding 10 | import java.time.LocalDate 11 | 12 | class DayListAdapter( 13 | private val dayItems: List, 14 | private val mainActivity: MainActivity, 15 | private val textColors: WeekDayTextColors 16 | ) : RecyclerView.Adapter() { 17 | var onItemClick: ((DayItem) -> Unit)? = null 18 | 19 | inner class ViewHolder(binding: FragmentWeekDayBinding) 20 | : RecyclerView.ViewHolder(binding.root), View.OnClickListener { 21 | val dayOfMonthLabel: TextView = binding.lblWeekDayOfMonth 22 | val dayOfWeekLabel: TextView = binding.lblWeekDayOfWeek 23 | val layout: LinearLayout = binding.layoutDayWeek 24 | var dataPosition: Int = -1 25 | 26 | override fun onClick(view: View) { 27 | onItemClick?.invoke(dayItems[dataPosition]) 28 | } 29 | } 30 | 31 | override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder { 32 | val inflater = LayoutInflater.from(viewGroup.context) 33 | val binding = FragmentWeekDayBinding.inflate(inflater, viewGroup, false) 34 | 35 | return ViewHolder(binding) 36 | } 37 | 38 | override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) { 39 | val dayItem: DayItem = dayItems[position] 40 | val dayOfMonthTextColor: Int = textColors.getTextColorOf(dayItem.date.dayOfWeek) 41 | val dayOfWeekTextColor: Int = mainActivity.themePainter.values.textColor 42 | val viewedDate: LocalDate = mainActivity.sharedData.viewedDate 43 | 44 | viewHolder.dataPosition = position 45 | viewHolder.dayOfMonthLabel.text = dayItem.dayOfMonth 46 | viewHolder.dayOfMonthLabel.setTextColor(dayOfMonthTextColor) 47 | viewHolder.dayOfWeekLabel.text = dayItem.dayOfWeek 48 | viewHolder.dayOfWeekLabel.setTextColor(dayOfWeekTextColor) 49 | viewHolder.layout.setOnClickListener(viewHolder) 50 | 51 | if (viewedDate == dayItem.date) { 52 | mainActivity.themePainter.paintSelectedWeekDayItem(viewHolder.layout) 53 | } 54 | else { 55 | mainActivity.themePainter.paintWeekDayItem(viewHolder.layout) 56 | } 57 | } 58 | 59 | override fun getItemCount() = dayItems.size 60 | } 61 | -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/DayNoteAddFragment.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar 2 | 3 | import android.content.Context 4 | import android.os.Bundle 5 | import android.view.LayoutInflater 6 | import android.view.View 7 | import android.view.ViewGroup 8 | import androidx.fragment.app.Fragment 9 | import com.sztorm.notecalendar.databinding.FragmentDayNoteAddBinding 10 | import com.sztorm.notecalendar.helpers.ViewHelper.Companion.hideKeyboard 11 | import com.sztorm.notecalendar.helpers.ViewHelper.Companion.showKeyboard 12 | import com.sztorm.notecalendar.repositories.NoteRepository 13 | import timber.log.Timber 14 | 15 | class DayNoteAddFragment() : Fragment() { 16 | private lateinit var binding: FragmentDayNoteAddBinding 17 | private lateinit var mainActivity: MainActivity 18 | private lateinit var dayFragment: DayFragment 19 | 20 | constructor(dayFragment: DayFragment) : this() { 21 | this.dayFragment = dayFragment 22 | } 23 | 24 | override fun onAttach(context: Context) { 25 | super.onAttach(context) 26 | mainActivity = context as MainActivity 27 | } 28 | 29 | override fun onCreateView( 30 | inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? 31 | ): View { 32 | binding = FragmentDayNoteAddBinding.inflate(inflater, container, false) 33 | setTheme() 34 | setBtnNoteCancelClickListener() 35 | setBtnNoteSaveClickListener() 36 | 37 | return binding.root 38 | } 39 | 40 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 41 | super.onViewCreated(view, savedInstanceState) 42 | 43 | binding.txtNoteAdd.requestFocus() 44 | binding.txtNoteAdd.showKeyboard() 45 | } 46 | 47 | private fun setBtnNoteCancelClickListener() = binding.btnNoteCancel.setOnClickListener { 48 | binding.txtNoteAdd.text.clear() 49 | binding.root.hideKeyboard() 50 | dayFragment.setFragment(DayNoteEmptyFragment(dayFragment)) 51 | } 52 | 53 | private fun setBtnNoteSaveClickListener() = binding.btnNoteSave.setOnClickListener { 54 | binding.root.hideKeyboard() 55 | val noteData = NoteData( 56 | date = mainActivity.sharedData.viewedDate.toString(), 57 | text = binding.txtNoteAdd.text.toString() 58 | ) 59 | NoteRepository.add(noteData) 60 | if (mainActivity.notificationManager.tryScheduleNotification( 61 | ScheduleNoteNotificationArguments(note = noteData) 62 | ) 63 | ) { 64 | Timber.i("${LogTags.NOTIFICATIONS} Scheduled notification after note save") 65 | } 66 | dayFragment.setFragment(DayNoteFragment(dayFragment, noteData)) 67 | } 68 | 69 | private fun setTheme() { 70 | val themePainter: ThemePainter = mainActivity.themePainter 71 | 72 | binding.layoutNoteBottom.setBackgroundColor(themePainter.values.noteColor) 73 | themePainter.paintNote(binding.layoutNoteUpper) 74 | themePainter.paintButton(binding.btnNoteSave) 75 | themePainter.paintButton(binding.btnNoteCancel) 76 | themePainter.paintEditText(binding.txtNoteAdd) 77 | } 78 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/DayNoteEmptyFragment.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar 2 | 3 | import android.content.Context 4 | import android.os.Bundle 5 | import android.view.LayoutInflater 6 | import android.view.View 7 | import android.view.ViewGroup 8 | import androidx.fragment.app.Fragment 9 | import com.sztorm.notecalendar.databinding.FragmentDayNoteEmptyBinding 10 | import com.sztorm.notecalendar.repositories.NoteRepository 11 | import timber.log.Timber 12 | 13 | class DayNoteEmptyFragment() : Fragment() { 14 | private lateinit var binding: FragmentDayNoteEmptyBinding 15 | private lateinit var mainActivity: MainActivity 16 | private lateinit var dayFragment: DayFragment 17 | private var args: Arguments? = null 18 | 19 | constructor(dayFragment: DayFragment, args: Arguments? = null) : this() { 20 | this.dayFragment = dayFragment 21 | this.args = args 22 | } 23 | 24 | override fun onAttach(context: Context) { 25 | super.onAttach(context) 26 | mainActivity = activity as MainActivity 27 | } 28 | 29 | override fun onCreateView( 30 | inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? 31 | ): View { 32 | binding = FragmentDayNoteEmptyBinding.inflate(inflater, container, false) 33 | setTheme() 34 | setBtnNoteAddClickListener() 35 | setBtnNoteUndoDeletionClickListener() 36 | handleArgs(args) 37 | 38 | return binding.root 39 | } 40 | 41 | private fun handleArgs(args: Arguments?) { 42 | when (args) { 43 | is CreateOrEditNoteRequest -> { 44 | dayFragment.setFragment(DayNoteAddFragment(dayFragment)) 45 | this.args = null 46 | } 47 | 48 | is UndoNoteDeleteOption -> { 49 | binding.btnUndoDeletion.visibility = View.VISIBLE 50 | } 51 | } 52 | } 53 | 54 | private fun setTheme() { 55 | val themePainter: ThemePainter = mainActivity.themePainter 56 | themePainter.paintButton(binding.btnNoteAdd) 57 | themePainter.paintButton(binding.btnUndoDeletion) 58 | } 59 | 60 | private fun setBtnNoteAddClickListener() = 61 | binding.btnNoteAdd.setOnClickListener { 62 | dayFragment.setFragment(DayNoteAddFragment(dayFragment)) 63 | } 64 | 65 | private fun setBtnNoteUndoDeletionClickListener() = 66 | binding.btnUndoDeletion.setOnClickListener { 67 | val args = this.args 68 | 69 | if (args is UndoNoteDeleteOption) { 70 | val note = args.note 71 | 72 | NoteRepository.add(note) 73 | 74 | if (mainActivity.notificationManager.tryScheduleNotification( 75 | ScheduleNoteNotificationArguments(note = note) 76 | ) 77 | ) { 78 | Timber.i("${LogTags.NOTIFICATIONS} Scheduled notification after note save") 79 | } 80 | dayFragment.setFragment(DayNoteFragment(dayFragment, note)) 81 | } 82 | } 83 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/DefaultActivityLifecycleCallbacks.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar 2 | 3 | import android.app.Activity 4 | import android.app.Application 5 | import android.os.Bundle 6 | 7 | interface DefaultActivityLifecycleCallbacks : Application.ActivityLifecycleCallbacks { 8 | override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) { } 9 | 10 | override fun onActivityStarted(activity: Activity) { } 11 | 12 | override fun onActivityResumed(activity: Activity) { } 13 | 14 | override fun onActivityPaused(activity: Activity) { } 15 | 16 | override fun onActivityStopped(activity: Activity) { } 17 | 18 | override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) { } 19 | 20 | override fun onActivityDestroyed(activity: Activity) { } 21 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/FragmentSetter.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("unused") 2 | 3 | package com.sztorm.notecalendar 4 | 5 | import androidx.fragment.app.Fragment 6 | import androidx.fragment.app.FragmentManager 7 | 8 | class FragmentSetter( 9 | private val fragmentManager: FragmentManager, 10 | private val fragmentContainerID: Int, 11 | private val defaultResAnimIn: Int = R.anim.anim_in, 12 | private val defaultResAnimOut: Int = R.anim.anim_out 13 | ) { 14 | fun setFragment( 15 | fragment: Fragment, 16 | resAnimIn: Int = defaultResAnimIn, 17 | resAnimOut: Int = defaultResAnimOut 18 | ) { 19 | fragmentManager.beginTransaction() 20 | .setCustomAnimations(resAnimIn, resAnimOut) 21 | .replace(fragmentContainerID, fragment) 22 | .commit() 23 | } 24 | 25 | fun setFragment( 26 | fragment: Fragment, 27 | backStackTag: String?, 28 | resAnimIn: Int = defaultResAnimIn, 29 | resAnimOut: Int = defaultResAnimOut 30 | ): Int = fragmentManager 31 | .beginTransaction() 32 | .setCustomAnimations(resAnimIn, resAnimOut) 33 | .replace(fragmentContainerID, fragment) 34 | .addToBackStack(backStackTag) 35 | .commit() 36 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/LogTags.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar 2 | class LogTags { 3 | companion object { 4 | const val NOTIFICATIONS = "Notifications:" 5 | } 6 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/MainFragmentType.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar 2 | 3 | import androidx.fragment.app.Fragment 4 | 5 | enum class MainFragmentType { 6 | DAY, 7 | WEEK, 8 | MONTH, 9 | ROOT_SETTINGS, 10 | CUSTOM_THEME_SETTINGS; 11 | 12 | fun createFragment(args: Arguments? = null): Fragment = CREATORS[ordinal](args) 13 | 14 | companion object { 15 | private val VALUES: Array = values() 16 | private val CREATORS: Array<(args: Arguments?) -> Fragment> = arrayOf( 17 | { args -> DayFragment().apply { postInit(args) } }, 18 | { args -> WeekFragment().apply { postInit(args) } }, 19 | { args -> MonthFragment().apply { postInit(args) } }, 20 | { args -> RootSettingsFragment().apply { postInit(args) } }, 21 | { args -> CustomThemeSettingsFragment().apply { postInit(args) } } 22 | ) 23 | 24 | fun from(ordinal: Int) = try { 25 | VALUES[ordinal] 26 | } catch (e: IndexOutOfBoundsException) { 27 | throw IllegalArgumentException( 28 | "Value is out of range of enum ordinals. The " + 29 | "value must be in [0, 4] range." 30 | ) 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/MonthFragment.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar 2 | 3 | import android.content.Context 4 | import android.os.Bundle 5 | import androidx.fragment.app.Fragment 6 | import android.view.LayoutInflater 7 | import android.view.View 8 | import android.view.ViewGroup 9 | import com.kizitonwose.calendarview.CalendarView 10 | import com.sztorm.notecalendar.calendarview.ThemedDayBinder 11 | import com.sztorm.notecalendar.databinding.CalendarWeekDayBarBinding 12 | import com.sztorm.notecalendar.databinding.FragmentMonthBinding 13 | import com.sztorm.notecalendar.helpers.DateHelper.Companion.toLocalizedAbbreviatedString 14 | import com.sztorm.notecalendar.helpers.DateHelper.Companion.toLocalizedString 15 | import java.time.DayOfWeek 16 | import java.time.LocalDate 17 | import java.time.YearMonth 18 | 19 | class MonthFragment : Fragment() { 20 | private lateinit var binding: FragmentMonthBinding 21 | private lateinit var mainActivity: MainActivity 22 | private lateinit var startMonth: YearMonth 23 | private lateinit var endMonth: YearMonth 24 | 25 | @Suppress("UNUSED_PARAMETER") 26 | fun postInit(args: Arguments?) {} 27 | 28 | override fun onAttach(context: Context) { 29 | super.onAttach(context) 30 | mainActivity = activity as MainActivity 31 | } 32 | 33 | private fun setDayOfWeekBarText(firstDayOfWeek: DayOfWeek) { 34 | val dayOfWeekBar: CalendarWeekDayBarBinding = binding.layoutDayOfWeekBar 35 | var dayOfWeekIterator: DayOfWeek = firstDayOfWeek 36 | 37 | dayOfWeekBar.lblFirstDay.text = dayOfWeekIterator.toLocalizedAbbreviatedString(mainActivity) 38 | dayOfWeekIterator += 1 39 | dayOfWeekBar.lblSecondDay.text = dayOfWeekIterator.toLocalizedAbbreviatedString(mainActivity) 40 | dayOfWeekIterator += 1 41 | dayOfWeekBar.lblThirdDay.text = dayOfWeekIterator.toLocalizedAbbreviatedString(mainActivity) 42 | dayOfWeekIterator += 1 43 | dayOfWeekBar.lblFourthDay.text = dayOfWeekIterator.toLocalizedAbbreviatedString(mainActivity) 44 | dayOfWeekIterator += 1 45 | dayOfWeekBar.lblFifthDay.text = dayOfWeekIterator.toLocalizedAbbreviatedString(mainActivity) 46 | dayOfWeekIterator += 1 47 | dayOfWeekBar.lblSixthDay.text = dayOfWeekIterator.toLocalizedAbbreviatedString(mainActivity) 48 | dayOfWeekIterator += 1 49 | dayOfWeekBar.lblSeventhDay.text = dayOfWeekIterator.toLocalizedAbbreviatedString(mainActivity) 50 | } 51 | 52 | private fun isNearEdgeOfMonthRange(currentMonth: YearMonth) 53 | = startMonth > currentMonth.minusMonths(2) || 54 | endMonth < currentMonth.plusMonths(2) 55 | 56 | override fun onCreateView( 57 | inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? 58 | ): View { 59 | binding = FragmentMonthBinding.inflate(inflater, container, false) 60 | val themePainter: ThemePainter = mainActivity.themePainter 61 | val calendarView: CalendarView = binding.calendarView 62 | val viewedDate: LocalDate = mainActivity.sharedData.viewedDate 63 | val currentSelectedMonth: YearMonth = YearMonth.of(viewedDate.year, viewedDate.month) 64 | startMonth = currentSelectedMonth.minusMonths(HALF_CACHED_MONTH_COUNT) 65 | endMonth = currentSelectedMonth.plusMonths(HALF_CACHED_MONTH_COUNT) 66 | val firstDayOfWeek: DayOfWeek = mainActivity.settings.firstDayOfWeek 67 | val dayBinder = ThemedDayBinder(mainActivity) 68 | val weekDayBinding = CalendarWeekDayBarBinding.bind(binding.layoutDayOfWeekBar.root) 69 | 70 | setDayOfWeekBarText(firstDayOfWeek) 71 | themePainter.paintCaledarDayOfWeekBar(weekDayBinding) 72 | binding.lblMonthAndYear.setTextColor(themePainter.values.textColor) 73 | 74 | calendarView.dayBinder = dayBinder 75 | calendarView.setup(startMonth, endMonth, firstDayOfWeek) 76 | calendarView.scrollToMonth(currentSelectedMonth) 77 | calendarView.monthScrollListener = { 78 | val currentMonth = it.yearMonth 79 | val text = "${currentMonth.month.toLocalizedString(mainActivity)} ${currentMonth.year}" 80 | binding.lblMonthAndYear.text = text 81 | 82 | if (isNearEdgeOfMonthRange(currentMonth)) { 83 | startMonth = currentMonth.minusMonths(HALF_CACHED_MONTH_COUNT) 84 | endMonth = currentMonth.plusMonths(HALF_CACHED_MONTH_COUNT) 85 | binding.calendarView.updateMonthRangeAsync(startMonth, endMonth) 86 | } 87 | } 88 | return binding.root 89 | } 90 | 91 | companion object { 92 | private const val HALF_CACHED_MONTH_COUNT: Long = 10 93 | } 94 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/NoteCalendarApplication.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar 2 | 3 | import android.app.Application 4 | import com.orm.SchemaGenerator 5 | import com.orm.SugarContext 6 | import com.orm.SugarDb 7 | import timber.log.Timber 8 | 9 | class NoteCalendarApplication : Application() { 10 | private fun initDebugLogger() { 11 | if (BuildConfig.DEBUG) { 12 | Timber.plant(Timber.DebugTree()) 13 | } 14 | } 15 | 16 | private fun initDatabase() { 17 | SugarContext.init(this) 18 | 19 | val schemaGenerator = SchemaGenerator(this) 20 | schemaGenerator.createDatabase(SugarDb(this).db) 21 | } 22 | 23 | override fun onCreate() { 24 | super.onCreate() 25 | initDebugLogger() 26 | initDatabase() 27 | } 28 | 29 | override fun onTerminate() { 30 | SugarContext.terminate() 31 | super.onTerminate() 32 | } 33 | 34 | companion object { 35 | const val BUNDLE_KEY_MAIN_FRAGMENT_TYPE = "MainFragmentType" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/NoteData.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar 2 | 3 | import com.orm.SugarRecord 4 | 5 | data class NoteData( 6 | var date: String = "", 7 | var text: String = "") : SugarRecord() 8 | -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/NoteNotificationData.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar 2 | 3 | import java.time.LocalDateTime 4 | 5 | data class NoteNotificationData(val note: NoteData, val dateTime: LocalDateTime) -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/NoteNotificationReceiver.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar 2 | 3 | import android.app.Notification 4 | import android.app.NotificationManager 5 | import android.content.BroadcastReceiver 6 | import android.content.Context 7 | import android.content.Intent 8 | import com.sztorm.notecalendar.helpers.IntentHelper.Companion.getParcelableExtraCompat 9 | 10 | class NoteNotificationReceiver : BroadcastReceiver() { 11 | override fun onReceive(context: Context, intent: Intent) { 12 | val manager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager 13 | val notification: Notification = intent.getParcelableExtraCompat( 14 | NOTIFICATION_EXTRA, Notification::class.java)!! 15 | manager.notify(AppNotificationManager.NOTIFICATION_ID, notification) 16 | } 17 | 18 | companion object { 19 | const val NOTIFICATION_EXTRA: String = "note-notification-extra" 20 | } 21 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/OnSwipeListener.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("unused") 2 | 3 | package com.sztorm.notecalendar 4 | 5 | import android.content.Context 6 | import android.view.GestureDetector 7 | import android.view.GestureDetector.SimpleOnGestureListener 8 | import android.view.MotionEvent 9 | import android.view.View 10 | import android.view.View.OnTouchListener 11 | import androidx.annotation.CallSuper 12 | import kotlin.math.abs 13 | 14 | open class OnSwipeListener( 15 | context: Context, 16 | val swipeThreshold: Int = 100, 17 | val swipeVelocityThreshold: Int = 100 18 | ) : OnTouchListener { 19 | private val gestureDetector = GestureDetector(context, GestureListener()) 20 | private var swipeListeners = ArrayList() 21 | 22 | private inner class GestureListener : SimpleOnGestureListener() { 23 | override fun onDown(e: MotionEvent): Boolean { 24 | return true 25 | } 26 | 27 | override fun onFling( 28 | me1: MotionEvent?, me2: MotionEvent, velocityX: Float, velocityY: Float 29 | ): Boolean { 30 | if (me1 == null) { 31 | return false 32 | } 33 | val diffY: Float = me2.y - me1.y 34 | val diffX: Float = me2.x - me1.x 35 | 36 | if (abs(diffX) > abs(diffY)) { 37 | if (abs(diffX) > swipeThreshold && abs(velocityX) > swipeVelocityThreshold) { 38 | if (diffX > 0) { 39 | onSwipeRight() 40 | } else { 41 | onSwipeLeft() 42 | } 43 | return true 44 | } 45 | } else if (abs(diffY) > swipeThreshold && abs(velocityY) > swipeVelocityThreshold) { 46 | if (diffY > 0) { 47 | onSwipeDown() 48 | } else { 49 | onSwipeUp() 50 | } 51 | return true 52 | } 53 | return false 54 | } 55 | } 56 | 57 | final override fun onTouch(v: View, event: MotionEvent): Boolean { 58 | v.performClick() 59 | return gestureDetector.onTouchEvent(event) 60 | } 61 | 62 | fun addOnSwipeListener(listener: OnDirectionalSwipeListener) { 63 | swipeListeners.add(listener) 64 | } 65 | 66 | fun removeOnSwipeListener(listener: OnDirectionalSwipeListener) { 67 | swipeListeners.remove(listener) 68 | } 69 | 70 | @CallSuper 71 | open fun onSwipeLeft() { 72 | val listeners = swipeListeners.toTypedArray() 73 | 74 | for (listener in listeners) { 75 | listener.onSwipe(SwipeDirection.Left, this) 76 | } 77 | } 78 | 79 | @CallSuper 80 | open fun onSwipeRight() { 81 | val listeners = swipeListeners.toTypedArray() 82 | 83 | for (listener in listeners) { 84 | listener.onSwipe(SwipeDirection.Right, this) 85 | } 86 | } 87 | 88 | @CallSuper 89 | open fun onSwipeUp() { 90 | val listeners = swipeListeners.toTypedArray() 91 | 92 | for (listener in listeners) { 93 | listener.onSwipe(SwipeDirection.Up, this) 94 | } 95 | } 96 | 97 | @CallSuper 98 | open fun onSwipeDown() { 99 | val listeners = swipeListeners.toTypedArray() 100 | 101 | for (listener in listeners) { 102 | listener.onSwipe(SwipeDirection.Down, this) 103 | } 104 | } 105 | } 106 | 107 | fun interface OnDirectionalSwipeListener { 108 | fun onSwipe(direction: SwipeDirection, swipeTouchListener: OnSwipeListener) 109 | } 110 | 111 | enum class SwipeDirection { 112 | Left, Right, Up, Down 113 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/ScheduleNoteNotificationArguments.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar 2 | 3 | import com.sztorm.notecalendar.timepickerpreference.TimePickerPreference 4 | 5 | data class ScheduleNoteNotificationArguments( 6 | val grantPermissions: Boolean = false, 7 | val enabledNotifications: Boolean? = null, 8 | val note: NoteData? = null, 9 | val notificationTime: TimePickerPreference.Time? = null 10 | ) 11 | -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/StartingViewType.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar 2 | 3 | import android.content.Context 4 | 5 | enum class StartingViewType { 6 | DAY_VIEW, 7 | WEEK_VIEW, 8 | MONTH_VIEW; 9 | 10 | fun toLocalizedString(context: Context): String = when (ordinal) { 11 | 0 -> context.getString(R.string.DayView) 12 | 1 -> context.getString(R.string.WeekView) 13 | 2 -> context.getString(R.string.MonthView) 14 | else -> "Error" 15 | } 16 | 17 | fun toMainFragmentType() = MainFragmentType.from(ordinal) 18 | 19 | companion object { 20 | private val VALUES: Array = values() 21 | 22 | fun from(ordinal: Int) = try { 23 | VALUES[ordinal] 24 | } catch (e: IndexOutOfBoundsException) { 25 | throw IllegalArgumentException( 26 | "Value is out of range of enum ordinals. The value must be in [0, 3] range." 27 | ) 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/ThemePaintable.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar 2 | 3 | interface ThemePaintable { 4 | var themePainter: ThemePainter 5 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/ThemeValues.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar 2 | 3 | import android.content.res.ColorStateList 4 | import android.graphics.Color 5 | import androidx.annotation.ColorInt 6 | import androidx.core.graphics.ColorUtils 7 | import com.sztorm.notecalendar.helpers.ColorStateListHelper 8 | 9 | class ThemeValues( 10 | @ColorInt val primaryColor: Int, 11 | @ColorInt val secondaryColor: Int, 12 | @ColorInt val inactiveItemColor: Int, 13 | @ColorInt val inactiveItemColorVariant: Int, 14 | @ColorInt val noteColor: Int, 15 | @ColorInt val noteColorVariant: Int, 16 | @ColorInt val textColor: Int, 17 | @ColorInt val buttonTextColor: Int, 18 | @ColorInt val noteTextColor: Int, 19 | @ColorInt val backgroundColor: Int) { 20 | val textHighlightColor: Int = ColorUtils.setAlphaComponent(secondaryColor, 255/3) 21 | val inactiveTextColor: Int = ColorUtils.setAlphaComponent(textColor, 255/3) 22 | val summaryTextColor: Int = ColorUtils.setAlphaComponent(textColor, 210) 23 | val buttonRippleColorStateList: ColorStateList = ColorStateListHelper 24 | .createRippleColorStateList( 25 | color = ColorUtils.setAlphaComponent(primaryColor, 255/10)) 26 | 27 | val alertButtonRippleColorStateList: ColorStateList = ColorStateListHelper 28 | .createRippleColorStateList( 29 | color = ColorUtils.setAlphaComponent(primaryColor, 255/7)) 30 | 31 | val buttonIconColorStateList: ColorStateList = ColorStateListHelper 32 | .createButtonColorStateList(enabledColor = buttonTextColor) 33 | 34 | val buttonBackgroundColorStateList: ColorStateList = ColorStateListHelper 35 | .createButtonColorStateList( 36 | enabledColor = primaryColor, 37 | disabledColor = inactiveItemColor) 38 | 39 | val navigationButtonStrokeColorStateList: ColorStateList = ColorStateListHelper 40 | .createToggleColorStateList( 41 | checkedColor = primaryColor, 42 | uncheckedColor = inactiveItemColorVariant) 43 | 44 | val navigationButtonIconColorStateList: ColorStateList = ColorStateListHelper 45 | .createToggleColorStateList( 46 | checkedColor = primaryColor, 47 | uncheckedColor = inactiveItemColor) 48 | 49 | val navigationButtonBackgroundColorStateList: ColorStateList = ColorStateListHelper 50 | .createToggleColorStateList( 51 | checkedColor = ColorUtils.setAlphaComponent(primaryColor, 255/10)) 52 | 53 | val outlinedButtonStrokeColorStateList: ColorStateList = ColorStateListHelper 54 | .createButtonColorStateList( 55 | enabledColor = primaryColor, 56 | disabledColor = inactiveItemColor) 57 | 58 | val outlinedButtonIconColorStateList: ColorStateList = ColorStateListHelper 59 | .createButtonColorStateList( 60 | enabledColor = primaryColor, 61 | disabledColor = inactiveItemColor) 62 | 63 | val switchThumbColorStateList: ColorStateList = ColorStateListHelper 64 | .createToggleColorStateList( 65 | checkedColor = secondaryColor, 66 | uncheckedColor = inactiveItemColor) 67 | 68 | val switchTrackColorStateList: ColorStateList = ColorStateListHelper 69 | .createToggleColorStateList( 70 | checkedColor = ColorUtils.setAlphaComponent(secondaryColor, 128), 71 | uncheckedColor = ColorUtils.setAlphaComponent(inactiveItemColor, 128)) 72 | 73 | val dayViewButtonColorStateList: ColorStateList = ColorStateListHelper 74 | .createClickableButtonColorStateList( 75 | pressedColor = inactiveItemColor, 76 | unpressedColor = Color.TRANSPARENT) 77 | 78 | val radioButtonTintList: ColorStateList = ColorStateListHelper 79 | .createToggleColorStateList( 80 | checkedColor = secondaryColor, 81 | uncheckedColor = inactiveItemColor) 82 | 83 | val dayViewSelectedButtonColorStateList: ColorStateList = ColorStateListHelper 84 | .createClickableButtonColorStateList( 85 | pressedColor = inactiveItemColor, 86 | unpressedColor = secondaryColor) 87 | 88 | val backArrowIconRippleColorStateList: ColorStateList = ColorStateListHelper 89 | .createClickableButtonColorStateList( 90 | pressedColor = ColorUtils.setAlphaComponent(textColor, 255 / 7), 91 | unpressedColor = Color.TRANSPARENT) 92 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/ThemedSimpleListDialog.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar 2 | 3 | import android.os.Bundle 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import android.widget.ListView 8 | import android.widget.TextView 9 | import com.google.android.material.button.MaterialButton 10 | import com.sztorm.notecalendar.simplelistpreference.SimpleListDialog 11 | import com.sztorm.notecalendar.simplelistpreference.SimpleListDialogAdapter 12 | 13 | class ThemedSimpleListDialog(override var themePainter: ThemePainter) : 14 | SimpleListDialog(), ThemePaintable { 15 | 16 | override fun onCreateView( 17 | inflater: LayoutInflater, 18 | container: ViewGroup?, 19 | savedInstanceState: Bundle? 20 | ): View { 21 | val root = super.onCreateView(inflater, container, savedInstanceState) 22 | val lblTitle: TextView = root.findViewById(R.id.lblTitle) 23 | val layoutList: ListView = root.findViewById(R.id.layoutList) 24 | val btnPositive: MaterialButton = root.findViewById(R.id.btnPositive) 25 | val btnNegative: MaterialButton = root.findViewById(R.id.btnNegative) 26 | val themeValues: ThemeValues = themePainter.values 27 | val adapter = (layoutList.adapter as SimpleListDialogAdapter) 28 | 29 | adapter.onCreateViewHolderListener = { 30 | it.itemLabel.setTextColor(themeValues.textColor) 31 | themePainter.paintRadio(it.itemRadio) 32 | } 33 | root.setBackgroundColor(themeValues.backgroundColor) 34 | lblTitle.setTextColor(themeValues.textColor) 35 | themePainter.paintDialogButton(btnPositive) 36 | themePainter.paintDialogButton(btnNegative) 37 | 38 | return root 39 | } 40 | 41 | class Builder(private val themePainter: ThemePainter): SimpleListDialog.Builder() { 42 | override fun build(): ThemedSimpleListDialog = createInstance(this) 43 | 44 | companion object { 45 | private fun createInstance(options: Builder): ThemedSimpleListDialog { 46 | val result = ThemedSimpleListDialog(options.themePainter) 47 | result.mEntries = options.entries 48 | result.mEntryValues = options.entryValues 49 | result.mSelectedValue = options.selectedValue 50 | result.mTitle = options.title 51 | result.mNegativeButtonText = options.negativeButtonText 52 | result.mPositiveButtonText = options.positiveButtonText 53 | 54 | return result 55 | } 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/WeekDayFragment.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar 2 | 3 | import androidx.fragment.app.Fragment 4 | 5 | class WeekDayFragment : Fragment() {} -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/WeekDayTextColors.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("MemberVisibilityCanBePrivate") 2 | 3 | package com.sztorm.notecalendar 4 | 5 | import java.time.DayOfWeek 6 | 7 | class WeekDayTextColors private constructor(val colors: IntArray) { 8 | val monday: Int 9 | get() = colors[0] 10 | val tuesday: Int 11 | get() = colors[1] 12 | val wednesday: Int 13 | get() = colors[2] 14 | val thursday: Int 15 | get() = colors[3] 16 | val friday: Int 17 | get() = colors[4] 18 | val saturday: Int 19 | get() = colors[5] 20 | val sunday: Int 21 | get() = colors[6] 22 | 23 | constructor(monday: Int, tuesday: Int, wednesday: Int, thursday: Int, friday: Int, 24 | saturday: Int, sunday: Int) : this(IntArray(7)) { 25 | colors[0] = monday 26 | colors[1] = tuesday 27 | colors[2] = wednesday 28 | colors[3] = thursday 29 | colors[4] = friday 30 | colors[5] = saturday 31 | colors[6] = sunday 32 | } 33 | 34 | fun getTextColorOf(dayOfWeek: DayOfWeek): Int = colors[dayOfWeek.value - 1] 35 | 36 | fun equals(other: WeekDayTextColors): Boolean = colors contentEquals other.colors 37 | 38 | override fun equals(other: Any?): Boolean { 39 | if (this === other) { 40 | return true 41 | } 42 | return if (other is WeekDayTextColors) equals(other) else false 43 | } 44 | 45 | override fun hashCode(): Int { 46 | var hash = 9 47 | 48 | for (color in colors) { 49 | hash = hash * 13 + color 50 | } 51 | return hash 52 | } 53 | 54 | companion object { 55 | /** 56 | * Creates new [WeekDayTextColors] instance out of 7 color values from monday to sunday. 57 | * 58 | * Throws [IllegalArgumentException] if [colors] array argument size is not 7. 59 | */ 60 | fun of(colors: IntArray): WeekDayTextColors { 61 | if (colors.size != 7) { 62 | throw IllegalArgumentException("Colors array size must be 7.") 63 | } 64 | return WeekDayTextColors(colors) 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/calendarview/DayViewContainer.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar.calendarview 2 | 3 | import android.widget.TextView 4 | import com.kizitonwose.calendarview.model.CalendarDay 5 | import com.kizitonwose.calendarview.ui.ViewContainer 6 | import com.sztorm.notecalendar.CreateOrEditNoteRequest 7 | import com.sztorm.notecalendar.MainActivity 8 | import com.sztorm.notecalendar.MainFragmentType 9 | import com.sztorm.notecalendar.databinding.CalendarDayBinding 10 | 11 | class DayViewContainer(binding: CalendarDayBinding, val mainActivity: MainActivity) : 12 | ViewContainer(binding.root) { 13 | private lateinit var day: CalendarDay 14 | val textView: TextView = binding.cvDayText 15 | 16 | init { 17 | binding.root.setOnClickListener { 18 | mainActivity.sharedData.viewedDate = day.date 19 | mainActivity.setMainFragment(MainFragmentType.DAY) 20 | } 21 | binding.root.setOnLongClickListener { 22 | mainActivity.sharedData.viewedDate = day.date 23 | mainActivity.setMainFragment(MainFragmentType.DAY, args = CreateOrEditNoteRequest) 24 | true 25 | } 26 | } 27 | 28 | fun reinit(day: CalendarDay) { 29 | this.day = day 30 | } 31 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/calendarview/ThemedDayBinder.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar.calendarview 2 | 3 | import android.view.View 4 | import com.kizitonwose.calendarview.model.CalendarDay 5 | import com.kizitonwose.calendarview.model.DayOwner 6 | import com.kizitonwose.calendarview.ui.DayBinder 7 | import com.sztorm.notecalendar.MainActivity 8 | import com.sztorm.notecalendar.NoteData 9 | import com.sztorm.notecalendar.ThemePainter 10 | import com.sztorm.notecalendar.databinding.CalendarDayBinding 11 | import com.sztorm.notecalendar.repositories.NoteRepository 12 | import java.time.LocalDate 13 | import java.time.Month 14 | 15 | class ThemedDayBinder(val mainActivity: MainActivity) : DayBinder { 16 | private var cachedMonthNotesList: List = NoteRepository.getByMonth( 17 | mainActivity.sharedData.viewedDate.month 18 | ) 19 | private var cachedNotesMonth: Month = mainActivity.sharedData.viewedDate.month 20 | private val today = LocalDate.now() 21 | 22 | override fun create(view: View) = DayViewContainer(CalendarDayBinding.bind(view), mainActivity) 23 | 24 | override fun bind(container: DayViewContainer, day: CalendarDay) { 25 | val themePainter: ThemePainter = mainActivity.themePainter 26 | val viewedDate: LocalDate = mainActivity.sharedData.viewedDate 27 | val textView = container.textView 28 | container.reinit(day) 29 | textView.text = day.date.dayOfMonth.toString() 30 | 31 | if (day.date.month != cachedNotesMonth) { 32 | cachedNotesMonth = day.date.month 33 | cachedMonthNotesList = NoteRepository.getByMonth(cachedNotesMonth) 34 | } 35 | 36 | themePainter.paintCalendarDayView( 37 | textView, 38 | isInMonth = day.owner == DayOwner.THIS_MONTH, 39 | isSelected = viewedDate == day.date, 40 | isToday = today == day.date, 41 | hasNote = cachedMonthNotesList.any { n -> n.date == day.date.toString() }) 42 | } 43 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/eventsubjects/DialogCancelSubject.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar.eventsubjects 2 | 3 | import android.content.DialogInterface 4 | 5 | interface DialogCancelSubject { 6 | fun addOnCancelListener(listener: DialogInterface.OnCancelListener): Boolean 7 | fun removeOnCancelListener(listener: DialogInterface.OnCancelListener): Boolean 8 | fun clearOnCancelListeners() 9 | } 10 | 11 | open class DialogCancelSubjectImpl : DialogCancelSubject { 12 | private val cancelListeners: MutableSet = LinkedHashSet() 13 | 14 | fun invokeCancelListeners(dialog: DialogInterface) { 15 | for (listener in cancelListeners) { 16 | listener.onCancel(dialog) 17 | } 18 | } 19 | 20 | override fun addOnCancelListener(listener: DialogInterface.OnCancelListener): Boolean 21 | = cancelListeners.add(listener) 22 | 23 | override fun removeOnCancelListener(listener: DialogInterface.OnCancelListener): Boolean 24 | = cancelListeners.remove(listener) 25 | 26 | override fun clearOnCancelListeners() = cancelListeners.clear() 27 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/eventsubjects/DialogClickSubject.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar.eventsubjects 2 | 3 | import android.content.DialogInterface 4 | 5 | interface DialogClickSubject { 6 | fun addOnClickListener(listener: DialogInterface.OnClickListener): Boolean 7 | fun removeOnClickListener(listener: DialogInterface.OnClickListener): Boolean 8 | fun clearOnClickListeners() 9 | } 10 | 11 | open class DialogClickSubjectImpl : DialogClickSubject { 12 | private val clickListeners: MutableSet = LinkedHashSet() 13 | 14 | fun invokeClickListeners(dialog: DialogInterface, which: Int) { 15 | for (listener in clickListeners) { 16 | listener.onClick(dialog, which) 17 | } 18 | } 19 | 20 | override fun addOnClickListener(listener: DialogInterface.OnClickListener): Boolean 21 | = clickListeners.add(listener) 22 | 23 | override fun removeOnClickListener(listener: DialogInterface.OnClickListener): Boolean 24 | = clickListeners.remove(listener) 25 | 26 | override fun clearOnClickListeners() = clickListeners.clear() 27 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/eventsubjects/DialogDismissSubject.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar.eventsubjects 2 | 3 | import android.content.DialogInterface 4 | 5 | interface DialogDismissSubject { 6 | fun addOnDismissListener(listener: DialogInterface.OnDismissListener): Boolean 7 | fun removeOnDismissListener(listener: DialogInterface.OnDismissListener): Boolean 8 | fun clearOnDismissListeners() 9 | } 10 | 11 | open class DialogDismissSubjectImpl : DialogDismissSubject { 12 | private val dismissListeners: MutableSet = LinkedHashSet() 13 | 14 | fun invokeDismissListeners(dialog: DialogInterface) { 15 | for (listener in dismissListeners) { 16 | listener.onDismiss(dialog) 17 | } 18 | } 19 | 20 | override fun addOnDismissListener(listener: DialogInterface.OnDismissListener): Boolean 21 | = dismissListeners.add(listener) 22 | 23 | override fun removeOnDismissListener(listener: DialogInterface.OnDismissListener): Boolean 24 | = dismissListeners.remove(listener) 25 | 26 | override fun clearOnDismissListeners() = dismissListeners.clear() 27 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/eventsubjects/DialogKeyListenerSubject.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar.eventsubjects 2 | 3 | import android.content.DialogInterface 4 | import android.view.KeyEvent 5 | 6 | interface DialogKeySubject { 7 | fun addOnKeyListener(listener: DialogInterface.OnKeyListener): Boolean 8 | fun removeOnKeyListener(listener: DialogInterface.OnKeyListener): Boolean 9 | fun clearOnKeyListeners() 10 | } 11 | 12 | open class DialogKeySubjectImpl : DialogKeySubject { 13 | private val keyListeners: MutableSet = LinkedHashSet() 14 | 15 | fun invokeKeyListeners(dialog: DialogInterface, keyCode: Int, event: KeyEvent) { 16 | for (listener in keyListeners) { 17 | listener.onKey(dialog, keyCode, event) 18 | } 19 | } 20 | 21 | override fun addOnKeyListener(listener: DialogInterface.OnKeyListener): Boolean 22 | = keyListeners.add(listener) 23 | 24 | override fun removeOnKeyListener(listener: DialogInterface.OnKeyListener): Boolean 25 | = keyListeners.remove(listener) 26 | 27 | override fun clearOnKeyListeners() = keyListeners.clear() 28 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/eventsubjects/DialogMultiChoiceClickSubject.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar.eventsubjects 2 | 3 | import android.content.DialogInterface 4 | 5 | interface DialogMultiChoiceClickSubject { 6 | fun addOnMultiChoiceClickListener(listener: DialogInterface.OnMultiChoiceClickListener): Boolean 7 | fun removeOnMultiChoiceClickListener( 8 | listener: DialogInterface.OnMultiChoiceClickListener): Boolean 9 | fun clearOnMultiChoiceClickListeners() 10 | } 11 | 12 | open class DialogMultiChoiceClickSubjectImpl : DialogMultiChoiceClickSubject { 13 | private val multiChoiceClickListeners: MutableSet 14 | = LinkedHashSet() 15 | 16 | fun invokeMultiChoiceClickListeners(dialog: DialogInterface, which: Int, isChecked: Boolean) { 17 | for (listener in multiChoiceClickListeners) { 18 | listener.onClick(dialog, which, isChecked) 19 | } 20 | } 21 | 22 | override fun addOnMultiChoiceClickListener( 23 | listener: DialogInterface.OnMultiChoiceClickListener): Boolean 24 | = multiChoiceClickListeners.add(listener) 25 | 26 | override fun removeOnMultiChoiceClickListener( 27 | listener: DialogInterface.OnMultiChoiceClickListener): Boolean 28 | = multiChoiceClickListeners.remove(listener) 29 | 30 | override fun clearOnMultiChoiceClickListeners() = multiChoiceClickListeners.clear() 31 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/eventsubjects/DialogShowSubject.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar.eventsubjects 2 | 3 | import android.content.DialogInterface 4 | 5 | interface DialogShowSubject { 6 | fun addOnShowListener(listener: DialogInterface.OnShowListener): Boolean 7 | fun removeOnShowListener(listener: DialogInterface.OnShowListener): Boolean 8 | fun clearOnShowListeners() 9 | } 10 | 11 | open class DialogShowSubjectImpl : DialogShowSubject { 12 | private val showListeners: MutableSet = LinkedHashSet() 13 | 14 | fun invokeShowListeners(dialog: DialogInterface) { 15 | for (listener in showListeners) { 16 | listener.onShow(dialog) 17 | } 18 | } 19 | 20 | override fun addOnShowListener(listener: DialogInterface.OnShowListener): Boolean 21 | = showListeners.add(listener) 22 | 23 | override fun removeOnShowListener(listener: DialogInterface.OnShowListener): Boolean 24 | = showListeners.remove(listener) 25 | 26 | override fun clearOnShowListeners() = showListeners.clear() 27 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/eventsubjects/NegativeButtonClickSubject.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar.eventsubjects 2 | 3 | import android.view.View 4 | 5 | interface NegativeButtonClickSubject { 6 | fun addOnNegativeButtonClickListener(listener: View.OnClickListener): Boolean 7 | fun removeOnNegativeButtonClickListener(listener: View.OnClickListener): Boolean 8 | fun clearOnNegativeButtonClickListeners() 9 | } 10 | 11 | open class NegativeButtonClickSubjectImpl : NegativeButtonClickSubject { 12 | private val clickListeners: MutableSet = LinkedHashSet() 13 | 14 | fun invokeNegativeButtonClickListeners(view: View) { 15 | for (listener in clickListeners) { 16 | listener.onClick(view) 17 | } 18 | } 19 | 20 | override fun addOnNegativeButtonClickListener(listener: View.OnClickListener): Boolean 21 | = clickListeners.add(listener) 22 | 23 | override fun removeOnNegativeButtonClickListener(listener: View.OnClickListener): Boolean 24 | = clickListeners.remove(listener) 25 | 26 | override fun clearOnNegativeButtonClickListeners() = clickListeners.clear() 27 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/eventsubjects/NeutralButtonClickSubject.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar.eventsubjects 2 | 3 | import android.view.View 4 | 5 | interface NeutralButtonClickSubject { 6 | fun addOnNeutralButtonClickListener(listener: View.OnClickListener): Boolean 7 | fun removeOnNeutralButtonClickListener(listener: View.OnClickListener): Boolean 8 | fun clearOnNeutralButtonClickListeners() 9 | } 10 | 11 | open class NeutralButtonClickSubjectImpl : NeutralButtonClickSubject { 12 | private val clickListeners: MutableSet = LinkedHashSet() 13 | 14 | fun invokeNeutralButtonClickListeners(view: View) { 15 | for (listener in clickListeners) { 16 | listener.onClick(view) 17 | } 18 | } 19 | 20 | override fun addOnNeutralButtonClickListener(listener: View.OnClickListener): Boolean 21 | = clickListeners.add(listener) 22 | 23 | override fun removeOnNeutralButtonClickListener(listener: View.OnClickListener): Boolean 24 | = clickListeners.remove(listener) 25 | 26 | override fun clearOnNeutralButtonClickListeners() = clickListeners.clear() 27 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/eventsubjects/PositiveButtonClickSubject.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar.eventsubjects 2 | 3 | import android.view.View 4 | 5 | interface PositiveButtonClickSubject { 6 | fun addOnPositiveButtonClickListener(listener: View.OnClickListener): Boolean 7 | fun removeOnPositiveButtonClickListener(listener: View.OnClickListener): Boolean 8 | fun clearOnPositiveButtonClickListeners() 9 | } 10 | 11 | open class PositiveButtonClickSubjectImpl : PositiveButtonClickSubject { 12 | private val clickListeners: MutableSet = LinkedHashSet() 13 | 14 | fun invokePositiveButtonClickListeners(view: View) { 15 | for (listener in clickListeners) { 16 | listener.onClick(view) 17 | } 18 | } 19 | 20 | override fun addOnPositiveButtonClickListener(listener: View.OnClickListener): Boolean 21 | = clickListeners.add(listener) 22 | 23 | override fun removeOnPositiveButtonClickListener(listener: View.OnClickListener): Boolean 24 | = clickListeners.remove(listener) 25 | 26 | override fun clearOnPositiveButtonClickListeners() = clickListeners.clear() 27 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/eventsubjects/ValueChangeSubject.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar.eventsubjects 2 | 3 | interface ValueChangeSubject { 4 | fun addOnValueChangeListener(listener: (T) -> Unit): Boolean 5 | fun removeOnValueChangeListener(listener: (T) -> Unit): Boolean 6 | fun clearOnValueChangeListeners() 7 | } 8 | 9 | open class ValueChangeSubjectImpl : ValueChangeSubject { 10 | private val valueChangeListeners: MutableSet<(T) -> Unit> = LinkedHashSet() 11 | 12 | fun invokeValueChangeListeners(value: T) { 13 | for (listener in valueChangeListeners) { 14 | listener(value) 15 | } 16 | } 17 | 18 | override fun addOnValueChangeListener(listener: (T) -> Unit): Boolean 19 | = valueChangeListeners.add(listener) 20 | 21 | override fun removeOnValueChangeListener(listener: (T) -> Unit): Boolean 22 | = valueChangeListeners.remove(listener) 23 | 24 | override fun clearOnValueChangeListeners() = valueChangeListeners.clear() 25 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/eventsubjects/ViewCreatedSubject.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar.eventsubjects 2 | 3 | import android.view.View 4 | 5 | interface ViewCreatedSubject { 6 | fun addOnViewCreatedListener(listener: (View) -> Unit): Boolean 7 | fun removeOnViewCreatedListener(listener: (View) -> Unit): Boolean 8 | fun clearOnViewCreatedListeners() 9 | } 10 | 11 | open class ViewCreatedSubjectImpl : ViewCreatedSubject { 12 | private val viewCreatedListeners: MutableSet<(View) -> Unit> = LinkedHashSet() 13 | 14 | fun invokeViewCreatedListeners(view: View) { 15 | for (listener in viewCreatedListeners) { 16 | listener(view) 17 | } 18 | } 19 | 20 | override fun addOnViewCreatedListener(listener: (View) -> Unit): Boolean 21 | = viewCreatedListeners.add(listener) 22 | 23 | override fun removeOnViewCreatedListener(listener: (View) -> Unit): Boolean 24 | = viewCreatedListeners.remove(listener) 25 | 26 | override fun clearOnViewCreatedListeners() = viewCreatedListeners.clear() 27 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/helpers/AlarmManagerHelper.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar.helpers 2 | 3 | import android.app.AlarmManager 4 | import android.app.PendingIntent 5 | import android.os.Build 6 | 7 | class AlarmManagerHelper private constructor() { 8 | companion object { 9 | /** 10 | * API level 31 (S) and greater: Checks if [AlarmManager.canScheduleExactAlarms] then calls 11 | * [AlarmManager.setExactAndAllowWhileIdle]. 12 | * 13 | * API level 23 (M) and greater: Calls [AlarmManager.setExactAndAllowWhileIdle]. 14 | * 15 | * Lower API levels: Calls [AlarmManager.setExact]. 16 | **/ 17 | fun AlarmManager.setExactAndAllowWhileIdleCompat( 18 | type: Int, triggerAtMillis: Long, operation: PendingIntent 19 | ) { 20 | when { 21 | Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> 22 | if (canScheduleExactAlarms()) { 23 | setExactAndAllowWhileIdle(type, triggerAtMillis, operation) 24 | } 25 | Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> 26 | setExactAndAllowWhileIdle(type, triggerAtMillis, operation) 27 | else -> setExact(type, triggerAtMillis, operation) 28 | } 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/helpers/ColorStateListHelper.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar.helpers 2 | 3 | import android.content.res.ColorStateList 4 | 5 | class ColorStateListHelper { 6 | companion object { 7 | val EMPTY = ColorStateList( 8 | Array(size = 0) { IntArray(size = 0) }, IntArray(size = 0)) 9 | 10 | fun createToggleColorStateList(checkedColor: Int, uncheckedColor: Int): ColorStateList { 11 | val states: Array = arrayOf( 12 | intArrayOf(android.R.attr.state_checked), 13 | intArrayOf(-android.R.attr.state_checked)) 14 | val colors = intArrayOf( 15 | checkedColor, 16 | uncheckedColor) 17 | 18 | return ColorStateList(states, colors) 19 | } 20 | 21 | fun createToggleColorStateList(checkedColor: Int): ColorStateList { 22 | val states: Array = arrayOf(intArrayOf(android.R.attr.state_checked)) 23 | val colors = intArrayOf(checkedColor) 24 | 25 | return ColorStateList(states, colors) 26 | } 27 | 28 | fun createButtonColorStateList(enabledColor: Int, disabledColor: Int): ColorStateList { 29 | val states: Array = arrayOf( 30 | intArrayOf(android.R.attr.state_enabled), 31 | intArrayOf(-android.R.attr.state_enabled)) 32 | val colors = intArrayOf(enabledColor, disabledColor) 33 | 34 | return ColorStateList(states, colors) 35 | } 36 | 37 | fun createButtonColorStateList(enabledColor: Int): ColorStateList { 38 | val states: Array = arrayOf(intArrayOf(android.R.attr.state_enabled)) 39 | val colors = intArrayOf(enabledColor) 40 | 41 | return ColorStateList(states, colors) 42 | } 43 | 44 | fun createClickableButtonColorStateList( 45 | pressedColor: Int, unpressedColor: Int): ColorStateList { 46 | val states: Array = arrayOf( 47 | intArrayOf(android.R.attr.state_pressed), 48 | intArrayOf(-android.R.attr.state_pressed)) 49 | val colors = intArrayOf(pressedColor, unpressedColor) 50 | 51 | return ColorStateList(states, colors) 52 | } 53 | 54 | fun createRippleColorStateList(color: Int): ColorStateList { 55 | val states: Array = arrayOf( 56 | intArrayOf(android.R.attr.state_pressed), 57 | intArrayOf(-android.R.attr.state_pressed)) 58 | val colors = intArrayOf(color, color) 59 | 60 | return ColorStateList(states, colors) 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/helpers/ContextHelper.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar.helpers 2 | 3 | import android.content.Context 4 | import android.content.res.Configuration 5 | import android.graphics.drawable.Drawable 6 | import android.util.TypedValue 7 | import androidx.annotation.AttrRes 8 | import androidx.annotation.ColorInt 9 | import androidx.annotation.ColorRes 10 | import androidx.annotation.DrawableRes 11 | import androidx.core.content.ContextCompat 12 | 13 | class ContextHelper { 14 | companion object { 15 | @ColorInt 16 | fun Context.getColorFromAttr(@AttrRes attrColor: Int): Int { 17 | val typedValue = TypedValue() 18 | this.theme.resolveAttribute(attrColor, typedValue, true) 19 | return typedValue.data 20 | } 21 | 22 | @ColorInt 23 | fun Context.getColorCompat(@ColorRes id: Int): Int = ContextCompat.getColor(this, id) 24 | 25 | fun Context.getPixelsFromDip(dp: Float): Float = TypedValue.applyDimension( 26 | TypedValue.COMPLEX_UNIT_DIP, dp, resources.displayMetrics) 27 | 28 | fun Context.getDrawableCompat(@DrawableRes id: Int): Drawable? 29 | = ContextCompat.getDrawable(this, id) 30 | 31 | val Context.isDarkThemeEnabled: Boolean 32 | get() = (this.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) == 33 | Configuration.UI_MODE_NIGHT_YES 34 | } 35 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/helpers/DateHelper.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar.helpers 2 | 3 | import android.content.Context 4 | import com.sztorm.notecalendar.R 5 | import java.time.DayOfWeek 6 | import java.time.Month 7 | 8 | class DateHelper { 9 | companion object { 10 | fun DayOfWeek.toLocalizedString(context: Context): String = when (this.value) { 11 | 1 -> context.getString(R.string.lblDayOfWeek_Monday) 12 | 2 -> context.getString(R.string.lblDayOfWeek_Tuesday) 13 | 3 -> context.getString(R.string.lblDayOfWeek_Wednesday) 14 | 4 -> context.getString(R.string.lblDayOfWeek_Thursday) 15 | 5 -> context.getString(R.string.lblDayOfWeek_Friday) 16 | 6 -> context.getString(R.string.lblDayOfWeek_Saturday) 17 | 7 -> context.getString(R.string.lblDayOfWeek_Sunday) 18 | else -> "Error" 19 | } 20 | 21 | fun DayOfWeek.toLocalizedAbbreviatedString(context: Context): String = when (this.value) { 22 | 1 -> context.getString(R.string.abbreviation_Monday) 23 | 2 -> context.getString(R.string.abbreviation_Tuesday) 24 | 3 -> context.getString(R.string.abbreviation_Wednesday) 25 | 4 -> context.getString(R.string.abbreviation_Thursday) 26 | 5 -> context.getString(R.string.abbreviation_Friday) 27 | 6 -> context.getString(R.string.abbreviation_Saturday) 28 | 7 -> context.getString(R.string.abbreviation_Sunday) 29 | else -> "Error" 30 | } 31 | 32 | fun Month.toLocalizedStringGenitiveCase(context: Context): String = when (this.value) { 33 | 1 -> context.getString(R.string.lblMonth_January) 34 | 2 -> context.getString(R.string.lblMonth_February) 35 | 3 -> context.getString(R.string.lblMonth_March) 36 | 4 -> context.getString(R.string.lblMonth_April) 37 | 5 -> context.getString(R.string.lblMonth_May) 38 | 6 -> context.getString(R.string.lblMonth_June) 39 | 7 -> context.getString(R.string.lblMonth_July) 40 | 8 -> context.getString(R.string.lblMonth_August) 41 | 9 -> context.getString(R.string.lblMonth_September) 42 | 10 -> context.getString(R.string.lblMonth_October) 43 | 11 -> context.getString(R.string.lblMonth_November) 44 | 12 -> context.getString(R.string.lblMonth_December) 45 | else -> "Error" 46 | } 47 | 48 | fun Month.toLocalizedString(context: Context): String = when (this.value) { 49 | 1 -> context.getString(R.string.January) 50 | 2 -> context.getString(R.string.February) 51 | 3 -> context.getString(R.string.March) 52 | 4 -> context.getString(R.string.April) 53 | 5 -> context.getString(R.string.May) 54 | 6 -> context.getString(R.string.June) 55 | 7 -> context.getString(R.string.July) 56 | 8 -> context.getString(R.string.August) 57 | 9 -> context.getString(R.string.September) 58 | 10 -> context.getString(R.string.October) 59 | 11 -> context.getString(R.string.November) 60 | 12 -> context.getString(R.string.December) 61 | else -> "Error" 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/helpers/DialogFragmentHelper.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar.helpers 2 | 3 | import android.os.Build 4 | import android.view.View 5 | import android.view.ViewGroup 6 | import androidx.core.graphics.Insets 7 | import androidx.core.view.WindowInsetsCompat 8 | import androidx.fragment.app.DialogFragment 9 | 10 | class DialogFragmentHelper private constructor() { 11 | companion object { 12 | fun DialogFragment.setMaximumWidth() { 13 | val view: View = requireActivity().window.decorView 14 | val width: Int = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { 15 | ((resources.displayMetrics.widthPixels)) 16 | } else { 17 | val insets: Insets = WindowInsetsCompat 18 | .toWindowInsetsCompat(view.rootWindowInsets, view) 19 | .getInsets(WindowInsetsCompat.Type.systemBars()) 20 | ((resources.displayMetrics.widthPixels - insets.right - insets.left)) 21 | } 22 | val height = ViewGroup.LayoutParams.WRAP_CONTENT 23 | 24 | requireDialog().window!!.setLayout(width, height) 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/helpers/DrawableHelper.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar.helpers 2 | 3 | import android.graphics.drawable.Drawable 4 | import androidx.core.graphics.drawable.DrawableCompat 5 | 6 | class DrawableHelper { 7 | companion object { 8 | fun Drawable.wrapCompat(): Drawable 9 | = DrawableCompat.wrap(this) 10 | } 11 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/helpers/IntentHelper.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar.helpers 2 | 3 | import android.content.Intent 4 | import android.os.Build 5 | import android.os.Parcelable 6 | 7 | class IntentHelper { 8 | companion object { 9 | @Suppress("DEPRECATION") 10 | fun Intent.getParcelableExtraCompat( 11 | name: String, clazz: Class 12 | ): T? { 13 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { 14 | return getParcelableExtra(name, clazz) 15 | } 16 | return getParcelableExtra(name) 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/helpers/PreferenceFragmentCompatHelper.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar.helpers 2 | 3 | import androidx.annotation.StringRes 4 | import androidx.preference.Preference 5 | import androidx.preference.PreferenceFragmentCompat 6 | import com.sztorm.notecalendar.ThemePaintable 7 | import com.sztorm.notecalendar.ThemePainter 8 | 9 | class PreferenceFragmentCompatHelper private constructor() { 10 | companion object { 11 | fun PreferenceFragmentCompat.setPreferenceThemePainter( 12 | themePainter: ThemePainter, @StringRes prefKeyStringResId: Int) 13 | where T : ThemePaintable, T: Preference { 14 | val key: String = requireContext().getString(prefKeyStringResId) 15 | val preference: T = (findPreference(key) as T?)!! 16 | preference.themePainter = themePainter 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/helpers/ViewHelper.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar.helpers 2 | 3 | import android.app.Activity 4 | import android.view.View 5 | import android.view.inputmethod.InputMethodManager 6 | 7 | class ViewHelper { 8 | companion object { 9 | fun View.hideKeyboard() { 10 | val imm = context.getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager 11 | imm.hideSoftInputFromWindow(this.windowToken, 0) 12 | } 13 | 14 | fun View.showKeyboard() { 15 | val imm = context.getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager 16 | imm.showSoftInput(this, 0) 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/repositories/NoteRepository.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("unused") 2 | 3 | package com.sztorm.notecalendar.repositories 4 | 5 | import com.orm.SugarRecord 6 | import com.orm.SugarContext 7 | import com.sztorm.notecalendar.NoteData 8 | import java.time.LocalDate 9 | import java.time.Month 10 | 11 | /** 12 | * [NoteRepository] is ready to use when [SugarContext] is initialized. 13 | **/ 14 | object NoteRepository { 15 | fun add(note: NoteData) { 16 | note.save() 17 | } 18 | 19 | fun delete(note: NoteData) { 20 | note.delete() 21 | } 22 | 23 | fun deleteAll() { 24 | SugarRecord.deleteAll(NoteData::class.java) 25 | } 26 | 27 | fun getByDate(date: LocalDate): NoteData? { 28 | val records: List = SugarRecord.find( 29 | NoteData::class.java, "date = ?", date.toString() 30 | ) 31 | 32 | return if (records.isNotEmpty()) records[0] else null 33 | } 34 | 35 | fun getByMonth(month: Month): List { 36 | val capacity = 6 37 | val argBuilder = StringBuilder(capacity).append("%-") 38 | val monthValueRaw = month.value.toString() 39 | 40 | if (monthValueRaw.length == 1) { 41 | argBuilder.append('0') 42 | 43 | } 44 | argBuilder 45 | .append(monthValueRaw) 46 | .append("-%") 47 | 48 | return SugarRecord.find( 49 | NoteData::class.java, "date LIKE ?", argBuilder.toString() 50 | ) 51 | } 52 | 53 | fun getAll(): List = SugarRecord.listAll(NoteData::class.java) 54 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/simplelistpreference/SimpleListDialogAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar.simplelistpreference 2 | 3 | import android.content.Context 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import android.widget.* 8 | import com.sztorm.notecalendar.R 9 | import com.sztorm.notecalendar.databinding.FragmentSimpleListDialogItemBinding 10 | 11 | class SimpleListDialogAdapter(context: Context, entries: Array, checkedPosition: Int = -1) : 12 | ArrayAdapter(context, R.layout.fragment_simple_list_dialog_item, entries) { 13 | private var checkedItemPosition: Int = checkedPosition 14 | private var checkedRadio: RadioButton? = null 15 | var onItemClickListener: ((item: CharSequence?, position: Int) -> Unit)? = null 16 | var onCreateViewHolderListener: ((holder: ViewHolder) -> Unit)? = null 17 | 18 | inner class ViewHolder(binding: FragmentSimpleListDialogItemBinding) { 19 | val itemRadio: RadioButton = binding.radioItem 20 | val itemLabel: TextView = binding.lblItem 21 | val itemLayout: LinearLayout = binding.root 22 | } 23 | 24 | override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { 25 | val binding: FragmentSimpleListDialogItemBinding 26 | val holder: ViewHolder 27 | 28 | if (convertView === null) { 29 | val inflater = LayoutInflater.from(parent.context) 30 | binding = FragmentSimpleListDialogItemBinding.inflate(inflater, parent, false) 31 | holder = ViewHolder(binding) 32 | 33 | val clickListener = View.OnClickListener { 34 | checkedRadio?.isChecked = false 35 | checkedItemPosition = position 36 | checkedRadio = holder.itemRadio 37 | 38 | holder.itemRadio.isChecked = true 39 | onItemClickListener?.invoke(getItem(position), position) 40 | } 41 | holder.itemLayout.setOnClickListener(clickListener) 42 | holder.itemRadio.setOnClickListener(clickListener) 43 | 44 | if (checkedItemPosition == position) { 45 | checkedRadio = holder.itemRadio 46 | holder.itemRadio.isChecked = true 47 | } 48 | else { 49 | holder.itemRadio.isChecked = false 50 | } 51 | onCreateViewHolderListener?.invoke(holder) 52 | 53 | binding.root.tag = holder 54 | } 55 | else { 56 | binding = FragmentSimpleListDialogItemBinding.bind(convertView) 57 | holder = binding.root.tag as ViewHolder 58 | } 59 | holder.itemLabel.text = getItem(position) 60 | 61 | return binding.root 62 | } 63 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/simplelistpreference/SimpleListPreference.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("MemberVisibilityCanBePrivate") 2 | 3 | package com.sztorm.notecalendar.simplelistpreference 4 | 5 | import android.content.Context 6 | import android.content.SharedPreferences 7 | import android.content.res.TypedArray 8 | import android.util.AttributeSet 9 | import android.view.View 10 | import androidx.core.content.edit 11 | import androidx.fragment.app.FragmentActivity 12 | import androidx.preference.Preference 13 | import androidx.preference.PreferenceManager 14 | import androidx.preference.PreferenceViewHolder 15 | import com.sztorm.notecalendar.R 16 | 17 | open class SimpleListPreference: Preference, View.OnClickListener { 18 | private lateinit var sharedPrefs: SharedPreferences 19 | protected lateinit var entries: Array 20 | protected lateinit var entryValues: Array 21 | protected var mDefaultValue: CharSequence? = null 22 | protected var mValue: CharSequence? = null 23 | 24 | var value: CharSequence? 25 | get() = mValue 26 | set(value) { 27 | mValue = value 28 | } 29 | 30 | constructor(context: Context) : super(context) 31 | 32 | constructor(context: Context, attributeSet: AttributeSet) : super(context, attributeSet) { 33 | setup(context, attributeSet) 34 | } 35 | 36 | constructor(context: Context, attributeSet: AttributeSet, defStyle: Int) : 37 | super(context, attributeSet, defStyle) { 38 | setup(context, attributeSet) 39 | } 40 | 41 | private fun setup(context: Context, attributeSet: AttributeSet) { 42 | val typedArray: TypedArray = context.obtainStyledAttributes( 43 | attributeSet, R.styleable.CustomizableListPreference) 44 | try { 45 | entries = typedArray.getTextArray( 46 | R.styleable.CustomizableListPreference_android_entries) 47 | entryValues = typedArray.getTextArray( 48 | R.styleable.CustomizableListPreference_android_entryValues) 49 | mDefaultValue = typedArray.getText( 50 | R.styleable.CustomizableListPreference_android_defaultValue) 51 | } 52 | finally { 53 | typedArray.recycle() 54 | } 55 | } 56 | 57 | override fun setDefaultValue(defaultValue: Any?) { 58 | mDefaultValue = defaultValue as CharSequence? 59 | super.setDefaultValue(mDefaultValue) 60 | } 61 | 62 | override fun onBindViewHolder(holder: PreferenceViewHolder) { 63 | super.onBindViewHolder(holder) 64 | sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context) 65 | mValue = sharedPrefs.getString(key, mDefaultValue?.toString()) 66 | 67 | val settingView: View = holder.itemView 68 | 69 | settingView.setOnClickListener(this) 70 | } 71 | 72 | protected open fun setupDialogBuilder() = SimpleListDialog.Builder() 73 | .setTitle(title) 74 | .setPositiveButton(context.getString(android.R.string.ok)) 75 | .setNegativeButton(context.getString(android.R.string.cancel)) 76 | .setEntries(entries) 77 | .setEntryValues(entryValues) 78 | .setSelectedValue(mValue) 79 | 80 | override fun onClick(v: View) { 81 | val activity = context as FragmentActivity 82 | val dialog: SimpleListDialog = setupDialogBuilder().build() 83 | 84 | dialog.addOnPositiveButtonClickListener{ 85 | mValue = dialog.selectedValue 86 | 87 | sharedPrefs.edit { 88 | putString(key, mValue?.toString()) 89 | } 90 | onPreferenceChangeListener?.onPreferenceChange(this, mValue) 91 | } 92 | dialog.show(activity.supportFragmentManager, null) 93 | } 94 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/themedpreferences/ThemedColorPickerPreference.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar.themedpreferences 2 | 3 | import android.content.Context 4 | import android.graphics.drawable.GradientDrawable 5 | import android.util.AttributeSet 6 | import android.widget.TextView 7 | import androidx.annotation.ColorInt 8 | import androidx.core.content.edit 9 | import androidx.preference.PreferenceViewHolder 10 | import com.skydoves.colorpickerpreference.ColorPickerPreference 11 | import com.skydoves.colorpickerview.ColorEnvelope 12 | import com.sztorm.notecalendar.ThemePaintable 13 | import com.sztorm.notecalendar.ThemePainter 14 | import com.sztorm.notecalendar.ThemeValues 15 | 16 | class ThemedColorPickerPreference: ColorPickerPreference, ThemePaintable { 17 | private lateinit var mThemePainter: ThemePainter 18 | private var colorBoxColor: Int? = null 19 | 20 | /** 21 | * [themePainter] must be set before can be get. 22 | */ 23 | override var themePainter: ThemePainter 24 | get() = mThemePainter 25 | set(value) { 26 | mThemePainter = value 27 | outlineColor = themePainter.values.textColor 28 | } 29 | 30 | constructor(context: Context) : super(context) 31 | 32 | constructor(context: Context, attributeSet: AttributeSet) : 33 | super(context, attributeSet) 34 | 35 | constructor(context: Context, attributeSet: AttributeSet, defStyle: Int) : 36 | super(context, attributeSet, defStyle) 37 | 38 | override fun onBindViewHolder(holder: PreferenceViewHolder) { 39 | super.onBindViewHolder(holder) 40 | val themeValues: ThemeValues = mThemePainter.values 41 | val title: TextView = holder.findViewById(android.R.id.title) as TextView 42 | val colorBoxGradient = colorBox.background as GradientDrawable 43 | val colorBoxColor: Int? = colorBoxColor 44 | 45 | title.setTextColor(themeValues.textColor) 46 | 47 | if (colorBoxColor !== null) { 48 | colorBoxGradient.setColor(colorBoxColor) 49 | } 50 | colorBoxGradient.setStroke(outlineSize, outlineColor) 51 | } 52 | 53 | fun setColorBoxColor( 54 | @ColorInt color: Int, @ColorInt outlineColor: Int = themePainter.values.textColor) { 55 | colorBoxColor = color 56 | this.outlineColor = outlineColor 57 | } 58 | 59 | fun saveColor(@ColorInt color: Int) { 60 | notifyColorChanged(ColorEnvelope(color)) 61 | preferenceManager.sharedPreferences?.edit { 62 | putInt(key, color) 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/themedpreferences/ThemedConfirmationPreference.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar.themedpreferences 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import android.widget.TextView 6 | import androidx.preference.PreferenceViewHolder 7 | import com.sztorm.notecalendar.ConfirmationPreference 8 | import com.sztorm.notecalendar.ThemePaintable 9 | import com.sztorm.notecalendar.ThemePainter 10 | 11 | class ThemedConfirmationPreference: ConfirmationPreference, ThemePaintable { 12 | /** 13 | * [themePainter] must be set before can be get. 14 | */ 15 | override lateinit var themePainter: ThemePainter 16 | 17 | constructor(context: Context) : super(context) 18 | 19 | constructor(context: Context, attributeSet: AttributeSet) : 20 | super(context, attributeSet) 21 | 22 | constructor(context: Context, attributeSet: AttributeSet, defStyle: Int) : 23 | super(context, attributeSet, defStyle) 24 | 25 | override fun onBindViewHolder(holder: PreferenceViewHolder) { 26 | super.onBindViewHolder(holder) 27 | 28 | val title: TextView = holder.findViewById(android.R.id.title) as TextView 29 | title.setTextColor(themePainter.values.textColor) 30 | } 31 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/themedpreferences/ThemedHeaderPreference.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar.themedpreferences 2 | 3 | import android.content.Context 4 | import android.graphics.Color 5 | import android.util.AttributeSet 6 | import android.util.TypedValue 7 | import android.widget.ImageView 8 | import android.widget.TextView 9 | import androidx.preference.Preference 10 | import androidx.preference.PreferenceViewHolder 11 | import com.sztorm.notecalendar.ThemePaintable 12 | import com.sztorm.notecalendar.ThemePainter 13 | import com.sztorm.notecalendar.helpers.ContextHelper.Companion.getPixelsFromDip 14 | 15 | class ThemedHeaderPreference: Preference, ThemePaintable { 16 | /** 17 | * [themePainter] must be set before can be get. 18 | */ 19 | override lateinit var themePainter: ThemePainter 20 | 21 | constructor(context: Context) : super(context) 22 | 23 | constructor(context: Context, attributeSet: AttributeSet) : 24 | super(context, attributeSet) 25 | 26 | constructor(context: Context, attributeSet: AttributeSet, defStyle: Int) : 27 | super(context, attributeSet, defStyle) 28 | 29 | override fun onBindViewHolder(holder: PreferenceViewHolder) { 30 | super.onBindViewHolder(holder) 31 | val iconView: ImageView = holder.findViewById(android.R.id.icon) as ImageView 32 | val title: TextView = holder.findViewById(android.R.id.title) as TextView 33 | val iconSizeDip = 40F 34 | val titleTextSizeSp = 20F 35 | 36 | holder.itemView.setOnClickListener { } 37 | holder.itemView.setBackgroundColor(Color.TRANSPARENT) 38 | 39 | iconView.minimumHeight = context.getPixelsFromDip(iconSizeDip).toInt() 40 | iconView.minimumWidth = context.getPixelsFromDip(iconSizeDip).toInt() 41 | iconView.setOnClickListener { 42 | onPreferenceClickListener?.onPreferenceClick(this) 43 | } 44 | 45 | themePainter.paintBackArrowIcon(iconView) 46 | title.setTextSize(TypedValue.COMPLEX_UNIT_SP, titleTextSizeSp) 47 | title.setTextColor(themePainter.values.secondaryColor) 48 | } 49 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/themedpreferences/ThemedPreference.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar.themedpreferences 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import android.widget.TextView 6 | import androidx.preference.Preference 7 | import androidx.preference.PreferenceViewHolder 8 | import com.sztorm.notecalendar.ThemePaintable 9 | import com.sztorm.notecalendar.ThemePainter 10 | import com.sztorm.notecalendar.ThemeValues 11 | 12 | class ThemedPreference: Preference, ThemePaintable { 13 | /** 14 | * [themePainter] must be set before can be get. 15 | */ 16 | override lateinit var themePainter: ThemePainter 17 | 18 | constructor(context: Context) : super(context) 19 | 20 | constructor(context: Context, attributeSet: AttributeSet) : 21 | super(context, attributeSet) 22 | 23 | constructor(context: Context, attributeSet: AttributeSet, defStyle: Int) : 24 | super(context, attributeSet, defStyle) 25 | 26 | override fun onBindViewHolder(holder: PreferenceViewHolder) { 27 | super.onBindViewHolder(holder) 28 | val themeValues: ThemeValues = themePainter.values 29 | val title: TextView = holder.findViewById(android.R.id.title) as TextView 30 | val summary: TextView = holder.findViewById(android.R.id.summary) as TextView 31 | 32 | title.setTextColor(themeValues.textColor) 33 | summary.setTextColor(themeValues.summaryTextColor) 34 | icon?.setTint(themeValues.secondaryColor) 35 | } 36 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/themedpreferences/ThemedPreferenceCategory.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar.themedpreferences 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import android.widget.TextView 6 | import androidx.preference.PreferenceCategory 7 | import androidx.preference.PreferenceViewHolder 8 | import com.sztorm.notecalendar.ThemePaintable 9 | import com.sztorm.notecalendar.ThemePainter 10 | 11 | class ThemedPreferenceCategory: PreferenceCategory, ThemePaintable { 12 | /** 13 | * [themePainter] must be set before can be get. 14 | */ 15 | override lateinit var themePainter: ThemePainter 16 | 17 | constructor(context: Context) : super(context) 18 | 19 | constructor(context: Context, attributeSet: AttributeSet) : 20 | super(context, attributeSet) 21 | 22 | constructor(context: Context, attributeSet: AttributeSet, defStyle: Int) : 23 | super(context, attributeSet, defStyle) 24 | 25 | override fun onBindViewHolder(holder: PreferenceViewHolder) { 26 | super.onBindViewHolder(holder) 27 | 28 | val title: TextView = holder.findViewById(android.R.id.title) as TextView 29 | title.setTextColor(themePainter.values.secondaryColor) 30 | } 31 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/themedpreferences/ThemedSimpleListPreference.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar.themedpreferences 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import android.widget.TextView 6 | import androidx.preference.PreferenceViewHolder 7 | import com.sztorm.notecalendar.* 8 | import com.sztorm.notecalendar.simplelistpreference.SimpleListPreference 9 | 10 | @Suppress("unused") 11 | class ThemedSimpleListPreference : SimpleListPreference, ThemePaintable { 12 | /** 13 | * [themePainter] must be set before can be get. 14 | */ 15 | override lateinit var themePainter: ThemePainter 16 | 17 | constructor(context: Context) : super(context) 18 | 19 | constructor(context: Context, attributeSet: AttributeSet) : 20 | super(context, attributeSet) 21 | 22 | constructor(context: Context, attributeSet: AttributeSet, defStyle: Int) : 23 | super(context, attributeSet, defStyle) 24 | 25 | override fun onBindViewHolder(holder: PreferenceViewHolder) { 26 | super.onBindViewHolder(holder) 27 | val themeValues: ThemeValues = themePainter.values 28 | val title: TextView = holder.findViewById(android.R.id.title) as TextView 29 | val summary: TextView = holder.findViewById(android.R.id.summary) as TextView 30 | 31 | title.setTextColor(themeValues.textColor) 32 | summary.setTextColor(themeValues.summaryTextColor) 33 | } 34 | 35 | override fun setupDialogBuilder() = ThemedSimpleListDialog.Builder(themePainter) 36 | .setTitle(title) 37 | .setPositiveButton(context.getString(R.string.Confirm)) 38 | .setNegativeButton(context.getString(R.string.Cancel)) 39 | .setEntries(entries) 40 | .setEntryValues(entryValues) 41 | .setSelectedValue(mValue) 42 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/themedpreferences/ThemedSwitchPreference.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar.themedpreferences 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import android.widget.TextView 6 | import androidx.appcompat.widget.SwitchCompat 7 | import androidx.preference.PreferenceViewHolder 8 | import androidx.preference.SwitchPreference 9 | import com.sztorm.notecalendar.R 10 | import com.sztorm.notecalendar.ThemePaintable 11 | import com.sztorm.notecalendar.ThemePainter 12 | 13 | @Suppress("unused") 14 | class ThemedSwitchPreference: SwitchPreference, ThemePaintable { 15 | 16 | /** 17 | * [themePainter] must be set before can be get. 18 | */ 19 | override lateinit var themePainter: ThemePainter 20 | 21 | constructor(context: Context) : super(context) 22 | 23 | constructor(context: Context, attributeSet: AttributeSet) : 24 | super(context, attributeSet) 25 | 26 | constructor(context: Context, attributeSet: AttributeSet, defStyle: Int) : 27 | super(context, attributeSet, defStyle) 28 | 29 | init { 30 | widgetLayoutResource = R.layout.preference_themed_switch_widget 31 | } 32 | 33 | override fun onBindViewHolder(holder: PreferenceViewHolder) { 34 | super.onBindViewHolder(holder) 35 | val title = holder.findViewById(android.R.id.title) as TextView 36 | val switch: SwitchCompat = holder.itemView.findViewById(R.id.switchWidget) 37 | val settingIsChecked = getPersistedBoolean(false) 38 | 39 | switch.setOnCheckedChangeListener { _, isChecked -> 40 | this.isChecked = isChecked 41 | } 42 | title.setTextColor(themePainter.values.textColor) 43 | themePainter.paintSwitch(switch) 44 | isChecked = settingIsChecked 45 | switch.isChecked = settingIsChecked 46 | } 47 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/themedpreferences/ThemedTimePickerPreference.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar.themedpreferences 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import android.widget.TextView 6 | import androidx.preference.PreferenceViewHolder 7 | import com.google.android.material.button.MaterialButton 8 | import com.sztorm.notecalendar.* 9 | import com.sztorm.notecalendar.timepickerpreference.TimePickerDialog 10 | import com.sztorm.notecalendar.timepickerpreference.TimePickerPreference 11 | 12 | class ThemedTimePickerPreference : TimePickerPreference, ThemePaintable { 13 | private lateinit var mThemePainter: ThemePainter 14 | 15 | /** 16 | * [themePainter] must be set before can be get. 17 | */ 18 | override var themePainter: ThemePainter 19 | get() = mThemePainter 20 | set(value) { 21 | mThemePainter = value 22 | } 23 | 24 | constructor(context: Context) : super(context) 25 | 26 | constructor(context: Context, attributeSet: AttributeSet) : 27 | super(context, attributeSet) 28 | 29 | constructor(context: Context, attributeSet: AttributeSet, defStyle: Int) : 30 | super(context, attributeSet, defStyle) 31 | 32 | override fun createTimePickerDialog(): TimePickerDialog { 33 | val dialog: TimePickerDialog = super.createTimePickerDialog() 34 | 35 | dialog.addOnViewCreatedListener { view -> 36 | val themeValues: ThemeValues = themePainter.values 37 | val titleHeader: TextView = view.findViewById(R.id.lblTitle) 38 | val positiveButton: MaterialButton = view.findViewById(R.id.btnPositive) 39 | val negativeButton: MaterialButton = view.findViewById(R.id.btnNegative) 40 | 41 | view.setBackgroundColor(themeValues.backgroundColor) 42 | titleHeader.setTextColor(themeValues.textColor) 43 | themePainter.paintTimePicker(dialog.picker) 44 | themePainter.paintDialogButton(positiveButton) 45 | themePainter.paintDialogButton(negativeButton) 46 | } 47 | return dialog 48 | } 49 | 50 | override fun onBindViewHolder(holder: PreferenceViewHolder) { 51 | super.onBindViewHolder(holder) 52 | val themeValues: ThemeValues = mThemePainter.values 53 | val title: TextView = holder.findViewById(android.R.id.title) as TextView 54 | val timeValue = holder.findViewById(R.id.timeValue) as TextView 55 | 56 | if (isEnabled) { 57 | title.setTextColor(themeValues.textColor) 58 | timeValue.setTextColor(themeValues.textColor) 59 | } 60 | else { 61 | title.setTextColor(themeValues.inactiveTextColor) 62 | timeValue.setTextColor(themeValues.inactiveTextColor) 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sztorm/notecalendar/timepickerpreference/TimePickerPreference.kt: -------------------------------------------------------------------------------- 1 | package com.sztorm.notecalendar.timepickerpreference 2 | 3 | import android.content.Context 4 | import android.content.SharedPreferences 5 | import android.text.format.DateFormat.is24HourFormat 6 | import android.util.AttributeSet 7 | import android.view.View 8 | import android.widget.TextView 9 | import androidx.core.content.edit 10 | import androidx.preference.Preference 11 | import androidx.preference.PreferenceManager 12 | import androidx.preference.PreferenceViewHolder 13 | import com.google.android.material.timepicker.TimeFormat 14 | import com.sztorm.notecalendar.MainActivity 15 | import com.sztorm.notecalendar.R 16 | import com.sztorm.notecalendar.timepickerpreference.TimePickerPreference.Time.Companion.asTime 17 | import com.sztorm.timepicker.PickedTime 18 | import java.time.LocalTime 19 | import java.time.format.DateTimeFormatter 20 | import java.time.format.FormatStyle 21 | 22 | open class TimePickerPreference : Preference, View.OnClickListener { 23 | class Time { 24 | private val value: Int 25 | 26 | val hour: Int 27 | get() = value and HOUR_BITS 28 | val minute: Int 29 | get() = (value and MINUTE_BITS) shr HOUR_BITS_SIZE 30 | 31 | private constructor(value: Int) { 32 | this.value = value 33 | } 34 | 35 | constructor(hour: Int, minute: Int) { 36 | this.value = asInt(hour, minute) 37 | } 38 | 39 | fun asInt(): Int = value 40 | 41 | fun toLocalTime(): LocalTime = LocalTime.of(hour, minute) 42 | 43 | override fun toString(): String = 44 | "${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}" 45 | 46 | companion object { 47 | // 5 bits = [0, 31] range of values, while [0, 23] is needed 48 | private const val HOUR_BITS: Int = 0b00000000_00000000_00000000_00011111 49 | private const val HOUR_BITS_SIZE: Int = 5 50 | // 6 bits = [0, 63] range of values, while [0, 59] is needed 51 | private const val MINUTE_BITS: Int = 0b00000000_00000000_00000111_11100000 52 | private const val MINUTE_BITS_SIZE: Int = 6 53 | 54 | fun asInt(hour: Int, minute: Int): Int = hour or (minute shl HOUR_BITS_SIZE) 55 | 56 | fun Int.asTime() = Time(this) 57 | } 58 | } 59 | 60 | companion object { 61 | val DEFAULT_TIME_RAW: Int = Time.asInt(hour = 8, minute = 0) 62 | val DEFAULT_TIME: Time = DEFAULT_TIME_RAW.asTime() 63 | } 64 | 65 | private val formatter: DateTimeFormatter = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT) 66 | private lateinit var sharedPrefs: SharedPreferences 67 | 68 | init { 69 | widgetLayoutResource = R.layout.preference_timepicker 70 | } 71 | 72 | constructor(context: Context) : super(context) 73 | 74 | constructor(context: Context, attributeSet: AttributeSet) : super(context, attributeSet) 75 | 76 | constructor(context: Context, attributeSet: AttributeSet, defStyle: Int) : 77 | super(context, attributeSet, defStyle) 78 | 79 | protected open fun createTimePickerDialog(): TimePickerDialog { 80 | val time: Time = sharedPrefs.getInt(key, DEFAULT_TIME_RAW).asTime() 81 | val timeFormat = if (is24HourFormat(context)) TimeFormat.CLOCK_24H else TimeFormat.CLOCK_12H 82 | 83 | return TimePickerDialog.Builder() 84 | .setTimeFormat(timeFormat) 85 | .setHour(time.hour) 86 | .setMinute(time.minute) 87 | .setTitleText(title) 88 | .build() 89 | } 90 | 91 | override fun onBindViewHolder(holder: PreferenceViewHolder) { 92 | super.onBindViewHolder(holder) 93 | sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context) 94 | val time: Time = sharedPrefs.getInt(key, DEFAULT_TIME_RAW).asTime() 95 | val settingView: View = holder.itemView 96 | val timeValueLabel = holder.findViewById(R.id.timeValue) as TextView 97 | 98 | timeValueLabel.text = formatter.format(LocalTime.of(time.hour, time.minute)) 99 | settingView.setOnClickListener(this) 100 | } 101 | 102 | override fun onClick(v: View) { 103 | val mainActivity = context as MainActivity 104 | val dialog = createTimePickerDialog() 105 | 106 | dialog.addOnPositiveButtonClickListener { 107 | val time: PickedTime = dialog.picker.time 108 | val preferenceValue = Time(time.hour, time.minute) 109 | 110 | sharedPrefs.edit { 111 | putInt(key, preferenceValue.asInt()) 112 | } 113 | onPreferenceChangeListener?.onPreferenceChange(this, preferenceValue) 114 | 115 | val timeValue = v.findViewById(R.id.timeValue) as TextView 116 | timeValue.text = formatter.format(preferenceValue.toLocalTime()) 117 | } 118 | dialog.show(mainActivity.supportFragmentManager, null) 119 | } 120 | } -------------------------------------------------------------------------------- /app/src/main/res/anim/anim_immediate.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/anim/anim_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/anim/anim_in_left.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/anim/anim_in_note.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/anim/anim_in_right.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/anim/anim_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/anim/anim_out_left.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/anim/anim_out_note.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/anim/anim_out_right.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/icon_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | 18 | 19 | 20 | 21 | 22 | 23 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_circle.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_note.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_note_layer0.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_note_layer1.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_outline_rounded_rectangle.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 10 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_outline_rounded_rectangle_pressed.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 10 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/cursor_edit_text.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/icon_add.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/icon_arrow_left.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/icon_arrow_left_layer0.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/icon_arrow_left_layer1.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/icon_calendar_day.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/icon_calendar_month.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/icon_calendar_week.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/icon_cancel.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/icon_delete.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/icon_edit.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/icon_note.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/icon_note_add.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/icon_palette.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/icon_settings.xml: -------------------------------------------------------------------------------- 1 | 5 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/icon_undo.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/selector_week_day.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/selector_week_day_selected.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/text_select_handle_left.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/text_select_handle_middle.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/text_select_handle_right.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 19 | 20 | 27 | 28 | 34 | 35 | 41 | 42 |