├── .gitignore ├── .idea ├── .gitignore ├── .name ├── compiler.xml ├── deploymentTargetDropDown.xml ├── discord.xml ├── git_toolbox_prj.xml ├── gradle.xml ├── highlightedFiles.xml ├── kotlinc.xml ├── misc.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── google-services.json ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── example │ │ └── notes_mvvm │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── notes_mvvm │ │ │ ├── MainActivity.kt │ │ │ ├── database │ │ │ ├── DatabaseRepository.kt │ │ │ ├── firebase │ │ │ │ ├── AllNotesLiveData.kt │ │ │ │ └── AppFirebaseRepository.kt │ │ │ └── room │ │ │ │ ├── AppRoomDao.kt │ │ │ │ ├── AppRoomDatabase.kt │ │ │ │ └── AppRoomRepository.kt │ │ │ ├── models │ │ │ └── AppNote.kt │ │ │ ├── screens │ │ │ ├── add_new_note │ │ │ │ ├── AddNewNoteFragment.kt │ │ │ │ └── AddNewNoteViewModel.kt │ │ │ ├── main │ │ │ │ ├── MainAdapter.kt │ │ │ │ ├── MainFragment.kt │ │ │ │ └── MainFragmentViewModel.kt │ │ │ ├── note │ │ │ │ ├── NoteFragment.kt │ │ │ │ └── NoteFragmentViewModel.kt │ │ │ └── start │ │ │ │ ├── StartFragment.kt │ │ │ │ └── StartFragmentViewModel.kt │ │ │ └── utilits │ │ │ ├── AppPreference.kt │ │ │ ├── constants.kt │ │ │ └── funs.kt │ └── res │ │ ├── anim │ │ ├── slide_right_to_left_enter.xml │ │ └── slide_right_to_left_out.xml │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ ├── ic_add.xml │ │ ├── ic_delete.xml │ │ ├── ic_exit.xml │ │ ├── ic_launcher_background.xml │ │ ├── screen_enter_server.webp │ │ ├── screen_main.webp │ │ ├── screen_new_note.webp │ │ ├── screen_note_view.webp │ │ └── screen_notes_list.webp │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── fragment_add_new_note.xml │ │ ├── fragment_main.xml │ │ ├── fragment_note.xml │ │ ├── fragment_start.xml │ │ └── note_item.xml │ │ ├── menu │ │ ├── main_action_menu.xml │ │ └── note_action_menu.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.webp │ │ ├── ic_launcher_round.webp │ │ └── ic_letter.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── navigation │ │ └── navigation_graph.xml │ │ ├── values-night │ │ └── themes.xml │ │ ├── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ ├── styles.xml │ │ └── themes.xml │ │ └── xml │ │ ├── backup_rules.xml │ │ └── data_extraction_rules.xml │ └── test │ └── java │ └── com │ └── example │ └── notes_mvvm │ └── ExampleUnitTest.kt ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | Notes mvvm -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/deploymentTargetDropDown.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /.idea/discord.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | -------------------------------------------------------------------------------- /.idea/git_toolbox_prj.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 19 | 20 | -------------------------------------------------------------------------------- /.idea/highlightedFiles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 17 | -------------------------------------------------------------------------------- /.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | 17 | 18 | 19 | 20 | 21 | 23 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Notes 2 | Testing pattern MVVM by simple app "notes". 3 | 4 | In this app I used to: 5 | 6 | 1. Kotlin 7 | 2. MVVM pattern 8 | 3. Room 9 | 4. Firebase 10 | 5. Coroutines 11 | 6. Navigation component 12 | 7. App Preference 13 | etc. 14 | 15 | Это приложение я сделал по курсу изучения архитектуры, написав это приложение я научился создавать приложения по паттерну проектирвоание MVVM. 16 | Этот проект позволяет вам создавать и удалять заметки. Вы можете их хранить локально на устройстве, либо на сервере, в таком случае вы вводите почту и пароль, все заметки сохраняются на сервере и если вы выйдете из акканута, и войдете на другом устройстве, то все данные сохранятся. 17 | Дизайн простенький, или если быть честным - его нет. Приложение сделано для отработки паттерна MVVM и работы с Firebase. 18 | 19 | 20 | Landing screen Game screen 21 | Landing screen Game screen 22 | Game screen 23 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'org.jetbrains.kotlin.android' 4 | id 'com.google.gms.google-services' 5 | } 6 | 7 | apply plugin: 'kotlin-kapt' 8 | apply plugin: 'com.google.gms.google-services' 9 | 10 | android { 11 | compileSdk 33 12 | 13 | defaultConfig { 14 | applicationId "com.example.notes_mvvm" 15 | minSdk 25 16 | targetSdk 33 17 | versionCode 1 18 | versionName "1.0" 19 | viewBinding.enabled = true 20 | 21 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 22 | } 23 | 24 | buildTypes { 25 | release { 26 | minifyEnabled false 27 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 28 | } 29 | } 30 | compileOptions { 31 | sourceCompatibility JavaVersion.VERSION_1_8 32 | targetCompatibility JavaVersion.VERSION_1_8 33 | } 34 | kotlinOptions { 35 | jvmTarget = '1.8' 36 | } 37 | namespace 'com.example.notes_mvvm' 38 | } 39 | 40 | dependencies { 41 | 42 | implementation 'androidx.core:core-ktx:1.10.1' 43 | implementation 'androidx.appcompat:appcompat:1.6.1' 44 | implementation 'com.google.android.material:material:1.9.0' 45 | implementation 'androidx.constraintlayout:constraintlayout:2.1.4' 46 | 47 | // Firebase 48 | implementation 'com.google.firebase:firebase-auth:22.1.0' 49 | implementation platform('com.google.firebase:firebase-bom:30.4.1') 50 | implementation 'com.google.firebase:firebase-analytics-ktx' 51 | implementation 'com.google.firebase:firebase-database:20.2.2' 52 | implementation 'com.google.firebase:firebase-messaging' 53 | 54 | // Room 55 | implementation("androidx.room:room-runtime:2.5.2") 56 | kapt("androidx.room:room-compiler:2.5.2") 57 | 58 | // optional - Kotlin Extensions and Coroutines support for Room 59 | implementation 'androidx.room:room-ktx:2.5.2' 60 | 61 | // Navigation component 62 | implementation "androidx.navigation:navigation-fragment:2.6.0" 63 | implementation "androidx.navigation:navigation-ui:2.6.0" 64 | 65 | // for Kotlin 66 | implementation "androidx.navigation:navigation-fragment-ktx:2.6.0" 67 | implementation "androidx.navigation:navigation-ui-ktx:2.6.0" 68 | } -------------------------------------------------------------------------------- /app/google-services.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_info": { 3 | "project_number": "200509252823", 4 | "firebase_url": "https://notes-a68f0-default-rtdb.asia-southeast1.firebasedatabase.app", 5 | "project_id": "notes-a68f0", 6 | "storage_bucket": "notes-a68f0.appspot.com" 7 | }, 8 | "client": [ 9 | { 10 | "client_info": { 11 | "mobilesdk_app_id": "1:200509252823:android:f55e5147495004f7523c92", 12 | "android_client_info": { 13 | "package_name": "com.example.notes_mvvm" 14 | } 15 | }, 16 | "oauth_client": [ 17 | { 18 | "client_id": "200509252823-c59jkii0q4paqbbivhcgadao848m4d5p.apps.googleusercontent.com", 19 | "client_type": 3 20 | } 21 | ], 22 | "api_key": [ 23 | { 24 | "current_key": "AIzaSyD_yjALE-ToDu4UJ4FcO3qbl0O-WSeytV8" 25 | } 26 | ], 27 | "services": { 28 | "appinvite_service": { 29 | "other_platform_oauth_client": [ 30 | { 31 | "client_id": "200509252823-c59jkii0q4paqbbivhcgadao848m4d5p.apps.googleusercontent.com", 32 | "client_type": 3 33 | } 34 | ] 35 | } 36 | } 37 | } 38 | ], 39 | "configuration_version": "1" 40 | } -------------------------------------------------------------------------------- /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/example/notes_mvvm/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.example.notes_mvvm 2 | 3 | 4 | /** 5 | * Instrumented test, which will execute on an Android device. 6 | * 7 | * See [testing documentation](http://d.android.com/tools/testing). 8 | */ 9 | class ExampleInstrumentedTest { 10 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/notes_mvvm/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.notes_mvvm 2 | 3 | import androidx.appcompat.app.AppCompatActivity 4 | import android.os.Bundle 5 | import androidx.appcompat.widget.Toolbar 6 | import androidx.navigation.NavController 7 | import androidx.navigation.Navigation 8 | import com.example.notes_mvvm.databinding.ActivityMainBinding 9 | import com.example.notes_mvvm.utilits.APP_ACTIVITY 10 | import com.example.notes_mvvm.utilits.AppPreference 11 | 12 | class MainActivity : AppCompatActivity() { 13 | 14 | lateinit var mToolbar: Toolbar 15 | lateinit var navController: NavController 16 | private var _binding: ActivityMainBinding? = null 17 | private val mBinding get() = _binding!! 18 | 19 | override fun onCreate(savedInstanceState: Bundle?) { 20 | super.onCreate(savedInstanceState) 21 | _binding = ActivityMainBinding.inflate(layoutInflater).also { setContentView(it.root) } 22 | 23 | APP_ACTIVITY = this 24 | mToolbar = mBinding.toolbar 25 | navController = Navigation.findNavController(this, R.id.nav_host_fragment) 26 | setSupportActionBar(mToolbar) 27 | title = getString(R.string.title) 28 | AppPreference.getPreference(this) 29 | } 30 | 31 | override fun onDestroy() { 32 | super.onDestroy() 33 | _binding = null 34 | } 35 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/notes_mvvm/database/DatabaseRepository.kt: -------------------------------------------------------------------------------- 1 | package com.example.notes_mvvm.database 2 | 3 | import androidx.lifecycle.LiveData 4 | import com.example.notes_mvvm.models.AppNote 5 | 6 | interface DatabaseRepository { 7 | val allNotes: LiveData> 8 | suspend fun insert(note: AppNote, onSuccess: () -> Unit) 9 | suspend fun delete(note: AppNote, onSuccess: () -> Unit) 10 | 11 | fun connectToDatabase(onSuccess: () -> Unit, onFail: (String) -> Unit) {} 12 | 13 | fun signOut() {} 14 | // добавляем пустую реализацию чтобы 15 | // в реализующих этот интерфейс классах необязательно нужно 16 | // было эти функции реализовывать, ведь при подключении бд room реализация 17 | // этих ф-й не нужна 18 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/notes_mvvm/database/firebase/AllNotesLiveData.kt: -------------------------------------------------------------------------------- 1 | package com.example.notes_mvvm.database.firebase 2 | 3 | import androidx.lifecycle.LiveData 4 | import com.example.notes_mvvm.models.AppNote 5 | import com.example.notes_mvvm.utilits.REF_DATABASE 6 | import com.google.firebase.database.DataSnapshot 7 | import com.google.firebase.database.DatabaseError 8 | import com.google.firebase.database.ValueEventListener 9 | 10 | class AllNotesLiveData : LiveData>() { 11 | 12 | private val listener = object : ValueEventListener { 13 | override fun onDataChange(snapshot: DataSnapshot) { 14 | value = snapshot.children.map { 15 | it.getValue(AppNote::class.java) ?: AppNote() 16 | } 17 | } 18 | 19 | override fun onCancelled(p0: DatabaseError) {} 20 | } 21 | 22 | override fun onActive() { 23 | REF_DATABASE.addValueEventListener(listener) 24 | super.onActive() 25 | } 26 | 27 | override fun onInactive() { 28 | REF_DATABASE.removeEventListener(listener) 29 | super.onInactive() 30 | } 31 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/notes_mvvm/database/firebase/AppFirebaseRepository.kt: -------------------------------------------------------------------------------- 1 | package com.example.notes_mvvm.database.firebase 2 | 3 | import androidx.lifecycle.LiveData 4 | import com.example.notes_mvvm.database.DatabaseRepository 5 | import com.example.notes_mvvm.models.AppNote 6 | import com.example.notes_mvvm.utilits.* 7 | import com.google.firebase.auth.FirebaseAuth 8 | import com.google.firebase.database.FirebaseDatabase 9 | 10 | class AppFirebaseRepository : DatabaseRepository { 11 | 12 | init { 13 | AUTH = FirebaseAuth.getInstance() 14 | } 15 | 16 | override val allNotes: LiveData> = AllNotesLiveData() 17 | 18 | override fun connectToDatabase(onSuccess: () -> Unit, onFail: (String) -> Unit) { 19 | if (AppPreference.getInitUser()) { 20 | initRefs() 21 | onSuccess() 22 | } else { 23 | AUTH.signInWithEmailAndPassword(EMAIL, PASSWORD) 24 | .addOnSuccessListener { 25 | initRefs() 26 | onSuccess() 27 | } 28 | .addOnFailureListener() { 29 | AUTH.createUserWithEmailAndPassword(EMAIL, PASSWORD) 30 | .addOnSuccessListener { 31 | initRefs() 32 | onSuccess() 33 | } 34 | .addOnFailureListener { onFail(it.message.toString()) } 35 | } 36 | } 37 | } 38 | 39 | override suspend fun insert(note: AppNote, onSuccess: () -> Unit) { 40 | val idNote = REF_DATABASE.push().key.toString() 41 | val mapNote = hashMapOf() 42 | mapNote[ID_FIREBASE] = idNote 43 | mapNote[NAME] = note.name 44 | mapNote[TEXT] = note.text 45 | 46 | REF_DATABASE.child(idNote) 47 | .updateChildren(mapNote) 48 | .addOnSuccessListener { onSuccess() } 49 | .addOnFailureListener { showToast(it.message.toString()) } 50 | } 51 | 52 | override suspend fun delete(note: AppNote, onSuccess: () -> Unit) { 53 | REF_DATABASE.child(note.idFirebase).removeValue() 54 | .addOnSuccessListener { onSuccess() } 55 | .addOnFailureListener { showToast(it.message.toString()) } 56 | } 57 | 58 | private fun initRefs() { 59 | CURRENT_ID = AUTH.currentUser?.uid.toString() 60 | REF_DATABASE = FirebaseDatabase.getInstance().reference 61 | .child(CURRENT_ID) 62 | } 63 | 64 | override fun signOut() { 65 | AUTH.signOut() 66 | } 67 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/notes_mvvm/database/room/AppRoomDao.kt: -------------------------------------------------------------------------------- 1 | package com.example.notes_mvvm.database.room 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.room.* 5 | import com.example.notes_mvvm.models.AppNote 6 | 7 | @Dao // data access option 8 | interface AppRoomDao { 9 | @Query("SELECT * from notes_tables") 10 | fun getAllNotes(): LiveData> 11 | 12 | @Insert(onConflict = OnConflictStrategy.IGNORE) 13 | suspend fun insert(note: AppNote) 14 | 15 | @Delete 16 | suspend fun delete(note: AppNote) 17 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/notes_mvvm/database/room/AppRoomDatabase.kt: -------------------------------------------------------------------------------- 1 | package com.example.notes_mvvm.database.room 2 | 3 | import android.content.Context 4 | import androidx.room.Database 5 | import androidx.room.Room 6 | import androidx.room.RoomDatabase 7 | import com.example.notes_mvvm.models.AppNote 8 | 9 | @Database(entities = [AppNote::class], version = 1) 10 | abstract class AppRoomDatabase : RoomDatabase() { 11 | abstract fun getAppRoomDao(): AppRoomDao 12 | 13 | companion object { 14 | // делаем сингл тон - чтобы не получилось так, что у нас 2 экземпляра 15 | // базы данных 16 | 17 | @Volatile 18 | private var database: AppRoomDatabase? = null 19 | 20 | @Synchronized 21 | fun getInstance(context: Context): AppRoomDatabase { 22 | return if (database == null) { 23 | database = Room.databaseBuilder(context, AppRoomDatabase::class.java, "database") 24 | .build() 25 | database as AppRoomDatabase 26 | } else database as AppRoomDatabase 27 | } 28 | 29 | } 30 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/notes_mvvm/database/room/AppRoomRepository.kt: -------------------------------------------------------------------------------- 1 | package com.example.notes_mvvm.database.room 2 | 3 | import androidx.lifecycle.LiveData 4 | import com.example.notes_mvvm.database.DatabaseRepository 5 | import com.example.notes_mvvm.models.AppNote 6 | 7 | class AppRoomRepository(private val appRoomDao: AppRoomDao) : DatabaseRepository { 8 | 9 | override val allNotes: LiveData> 10 | get() = appRoomDao.getAllNotes() 11 | 12 | override suspend fun insert(note: AppNote, onSuccess: () -> Unit) { 13 | appRoomDao.insert(note) 14 | onSuccess() 15 | } 16 | 17 | override suspend fun delete(note: AppNote, onSuccess: () -> Unit) { 18 | appRoomDao.delete(note) 19 | onSuccess() 20 | } 21 | 22 | override fun signOut() { 23 | super.signOut() 24 | } 25 | 26 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/notes_mvvm/models/AppNote.kt: -------------------------------------------------------------------------------- 1 | package com.example.notes_mvvm.models 2 | 3 | import androidx.room.ColumnInfo 4 | import androidx.room.Entity 5 | import androidx.room.PrimaryKey 6 | import java.io.Serializable 7 | 8 | @Entity(tableName = "notes_tables") 9 | data class AppNote( 10 | @PrimaryKey(autoGenerate = true) val id: Int = 0, 11 | @ColumnInfo val name: String = "", 12 | @ColumnInfo val text: String = "", 13 | val idFirebase: String = "" 14 | ) : Serializable -------------------------------------------------------------------------------- /app/src/main/java/com/example/notes_mvvm/screens/add_new_note/AddNewNoteFragment.kt: -------------------------------------------------------------------------------- 1 | package com.example.notes_mvvm.screens.add_new_note 2 | 3 | import android.os.Bundle 4 | import androidx.fragment.app.Fragment 5 | import android.view.LayoutInflater 6 | import android.view.View 7 | import android.view.ViewGroup 8 | import androidx.lifecycle.ViewModelProvider 9 | import com.example.notes_mvvm.R 10 | import com.example.notes_mvvm.databinding.FragmentAddNewNoteBinding 11 | import com.example.notes_mvvm.models.AppNote 12 | import com.example.notes_mvvm.utilits.APP_ACTIVITY 13 | import com.example.notes_mvvm.utilits.showToast 14 | 15 | class AddNewNoteFragment : Fragment() { 16 | 17 | private var _binding: FragmentAddNewNoteBinding? = null 18 | private val mBinding get() = _binding!! 19 | private lateinit var mViewModel: AddNewNoteViewModel 20 | 21 | override fun onCreateView( 22 | inflater: LayoutInflater, container: ViewGroup?, 23 | savedInstanceState: Bundle?, 24 | ): View { 25 | _binding = FragmentAddNewNoteBinding.inflate(layoutInflater, container, false) 26 | return mBinding.root 27 | } 28 | 29 | override fun onStart() { 30 | super.onStart() 31 | initialization() 32 | } 33 | 34 | private fun initialization() { 35 | mViewModel = ViewModelProvider(this).get(AddNewNoteViewModel::class.java) 36 | mBinding.btnAddNote.setOnClickListener { 37 | val name = mBinding.inputNameNote.text.toString() 38 | val text = mBinding.inputTextNote.text.toString() 39 | if (name.isEmpty()) { 40 | showToast(getString(R.string.toast_enter_name)) 41 | } else { 42 | mViewModel.insert(AppNote(name = name, text = text)) { 43 | APP_ACTIVITY.navController.navigate(R.id.action_addNewNoteFragment_to_mainFragment) 44 | } 45 | } 46 | } 47 | } 48 | 49 | override fun onDestroyView() { 50 | super.onDestroyView() 51 | _binding = null 52 | } 53 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/notes_mvvm/screens/add_new_note/AddNewNoteViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.example.notes_mvvm.screens.add_new_note 2 | 3 | import android.app.Application 4 | import androidx.lifecycle.AndroidViewModel 5 | import androidx.lifecycle.viewModelScope 6 | import com.example.notes_mvvm.models.AppNote 7 | import com.example.notes_mvvm.utilits.REPOSITORY 8 | import kotlinx.coroutines.Dispatchers 9 | import kotlinx.coroutines.launch 10 | 11 | class AddNewNoteViewModel(application: Application) : AndroidViewModel(application) { 12 | 13 | fun insert(note: AppNote, onSuccess: () -> Unit) = 14 | viewModelScope.launch(Dispatchers.Main) { 15 | REPOSITORY.insert(note) { 16 | onSuccess() 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/notes_mvvm/screens/main/MainAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.example.notes_mvvm.screens.main 2 | 3 | import android.annotation.SuppressLint 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import android.widget.TextView 8 | import androidx.recyclerview.widget.RecyclerView 9 | import com.example.notes_mvvm.R 10 | import com.example.notes_mvvm.databinding.NoteItemBinding 11 | import com.example.notes_mvvm.models.AppNote 12 | 13 | class MainAdapter : RecyclerView.Adapter() { 14 | 15 | private var mListNotes = emptyList() 16 | 17 | class MainHolder(view: View) : RecyclerView.ViewHolder(view) { 18 | private val binding = NoteItemBinding.bind(view) 19 | val nameNote: TextView = binding.itemNoteName 20 | val textNote: TextView = binding.itemNoteText 21 | } 22 | 23 | override fun onViewAttachedToWindow(holder: MainHolder) { 24 | holder.itemView.setOnClickListener { 25 | MainFragment.click(mListNotes[holder.adapterPosition]) 26 | } 27 | } 28 | 29 | override fun onViewDetachedFromWindow(holder: MainHolder) { 30 | holder.itemView.setOnClickListener(null) 31 | super.onViewDetachedFromWindow(holder) 32 | } 33 | 34 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MainHolder { 35 | val view = LayoutInflater.from(parent.context).inflate(R.layout.note_item, parent, false) 36 | return MainHolder(view) 37 | } 38 | 39 | override fun onBindViewHolder(holder: MainHolder, position: Int) { 40 | holder.textNote.text = mListNotes[position].text 41 | holder.nameNote.text = mListNotes[position].name 42 | } 43 | 44 | override fun getItemCount(): Int = mListNotes.size 45 | 46 | @SuppressLint("NotifyDataSetChanged") 47 | fun setList(list: List) { 48 | mListNotes = list 49 | notifyDataSetChanged() 50 | } 51 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/notes_mvvm/screens/main/MainFragment.kt: -------------------------------------------------------------------------------- 1 | package com.example.notes_mvvm.screens.main 2 | 3 | import android.os.Bundle 4 | import android.view.* 5 | import androidx.fragment.app.Fragment 6 | import androidx.lifecycle.Observer 7 | import androidx.lifecycle.ViewModelProvider 8 | import androidx.recyclerview.widget.RecyclerView 9 | import com.example.notes_mvvm.R 10 | import com.example.notes_mvvm.databinding.FragmentMainBinding 11 | import com.example.notes_mvvm.models.AppNote 12 | import com.example.notes_mvvm.utilits.APP_ACTIVITY 13 | import com.example.notes_mvvm.utilits.AppPreference 14 | 15 | class MainFragment : Fragment() { 16 | 17 | private var _binding: FragmentMainBinding? = null 18 | private val mBinding get() = _binding!! 19 | private lateinit var mViewModel: MainFragmentViewModel 20 | private lateinit var mRecyclerView: RecyclerView 21 | private lateinit var mAdapter: MainAdapter 22 | private lateinit var mObserverList: Observer> 23 | 24 | override fun onCreateView( 25 | inflater: LayoutInflater, container: ViewGroup?, 26 | savedInstanceState: Bundle?, 27 | ): View { 28 | _binding = FragmentMainBinding.inflate(layoutInflater, container, false) 29 | return mBinding.root 30 | } 31 | 32 | override fun onStart() { 33 | super.onStart() 34 | initialization() 35 | } 36 | 37 | private fun initialization() { 38 | setHasOptionsMenu(true) 39 | mAdapter = MainAdapter() 40 | mRecyclerView = mBinding.recyclerView 41 | mRecyclerView.adapter = mAdapter 42 | mObserverList = Observer { 43 | val list = it.asReversed() 44 | mAdapter.setList(list) 45 | } 46 | mViewModel = ViewModelProvider(this).get(MainFragmentViewModel::class.java) 47 | mViewModel.allNotes.observe(this, mObserverList) 48 | mBinding.btnAddNote.setOnClickListener { 49 | APP_ACTIVITY.navController.navigate(R.id.action_mainFragment_to_addNewNoteFragment) 50 | } 51 | } 52 | 53 | override fun onDestroyView() { 54 | super.onDestroyView() 55 | _binding = null 56 | mViewModel.allNotes.removeObserver(mObserverList) 57 | mRecyclerView.adapter = null 58 | } 59 | 60 | companion object { 61 | fun click(note: AppNote) { 62 | val bundle = Bundle() 63 | bundle.putSerializable("note", note) 64 | APP_ACTIVITY.navController.navigate(R.id.action_mainFragment_to_noteFragment, bundle) 65 | } 66 | } 67 | 68 | override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { 69 | inflater.inflate(R.menu.main_action_menu, menu) 70 | } 71 | 72 | override fun onOptionsItemSelected(item: MenuItem): Boolean { 73 | when (item.itemId) { 74 | R.id.btn_exit -> { 75 | mViewModel.signOut() 76 | AppPreference.setInitUser(false) 77 | APP_ACTIVITY.navController.navigate(R.id.action_mainFragment_to_startFragment) 78 | } 79 | } 80 | return super.onOptionsItemSelected(item) 81 | } 82 | 83 | } 84 | 85 | 86 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/notes_mvvm/screens/main/MainFragmentViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.example.notes_mvvm.screens.main 2 | 3 | import android.app.Application 4 | import androidx.lifecycle.AndroidViewModel 5 | import com.example.notes_mvvm.utilits.REPOSITORY 6 | 7 | class MainFragmentViewModel(application: Application) : AndroidViewModel(application) { 8 | val allNotes = REPOSITORY.allNotes 9 | 10 | fun signOut() { 11 | REPOSITORY.signOut() 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/notes_mvvm/screens/note/NoteFragment.kt: -------------------------------------------------------------------------------- 1 | package com.example.notes_mvvm.screens.note 2 | 3 | import android.os.Bundle 4 | import android.view.* 5 | import androidx.fragment.app.Fragment 6 | import androidx.lifecycle.ViewModelProvider 7 | import com.example.notes_mvvm.R 8 | import com.example.notes_mvvm.databinding.FragmentNoteBinding 9 | import com.example.notes_mvvm.models.AppNote 10 | import com.example.notes_mvvm.utilits.APP_ACTIVITY 11 | 12 | class NoteFragment : Fragment() { 13 | 14 | private var _binding: FragmentNoteBinding? = null 15 | private val mBinding get() = _binding!! 16 | private lateinit var mViewModel: NoteFragmentViewModel 17 | private lateinit var mCurrentNote: AppNote 18 | 19 | override fun onCreateView( 20 | inflater: LayoutInflater, container: ViewGroup?, 21 | savedInstanceState: Bundle?, 22 | ): View { 23 | _binding = FragmentNoteBinding.inflate(layoutInflater, container, false) 24 | mCurrentNote = arguments?.getSerializable("note") as AppNote 25 | return mBinding.root 26 | } 27 | 28 | override fun onStart() { 29 | super.onStart() 30 | initialization() 31 | } 32 | 33 | private fun initialization() { 34 | setHasOptionsMenu(true) 35 | mBinding.noteName.text = mCurrentNote.name 36 | mBinding.noteText.text = mCurrentNote.text 37 | mViewModel = ViewModelProvider(this).get(NoteFragmentViewModel::class.java) 38 | } 39 | 40 | override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { 41 | inflater.inflate(R.menu.note_action_menu, menu) 42 | } 43 | 44 | override fun onOptionsItemSelected(item: MenuItem): Boolean { 45 | when (item.itemId) { 46 | R.id.btn_delete -> { 47 | mViewModel.delete(mCurrentNote) { 48 | APP_ACTIVITY.navController.navigate(R.id.action_noteFragment_to_mainFragment) 49 | } 50 | } 51 | } 52 | return super.onOptionsItemSelected(item) 53 | } 54 | 55 | override fun onDestroyView() { 56 | super.onDestroyView() 57 | _binding = null 58 | } 59 | 60 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/notes_mvvm/screens/note/NoteFragmentViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.example.notes_mvvm.screens.note 2 | 3 | import android.app.Application 4 | import androidx.lifecycle.AndroidViewModel 5 | import androidx.lifecycle.viewModelScope 6 | import com.example.notes_mvvm.models.AppNote 7 | import com.example.notes_mvvm.utilits.REPOSITORY 8 | import kotlinx.coroutines.Dispatchers 9 | import kotlinx.coroutines.launch 10 | 11 | class NoteFragmentViewModel(application: Application) : AndroidViewModel(application) { 12 | fun delete(note: AppNote, onSuccess: () -> Unit) = 13 | viewModelScope.launch(Dispatchers.Main) { 14 | REPOSITORY.delete(note) { 15 | onSuccess() 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/notes_mvvm/screens/start/StartFragment.kt: -------------------------------------------------------------------------------- 1 | package com.example.notes_mvvm.screens.start 2 | 3 | import android.os.Bundle 4 | import androidx.fragment.app.Fragment 5 | import android.view.LayoutInflater 6 | import android.view.View 7 | import android.view.View.VISIBLE 8 | import android.view.ViewGroup 9 | import androidx.lifecycle.ViewModelProvider 10 | import com.example.notes_mvvm.R 11 | import com.example.notes_mvvm.databinding.FragmentStartBinding 12 | import com.example.notes_mvvm.utilits.* 13 | 14 | class StartFragment : Fragment() { 15 | 16 | private var _binding: FragmentStartBinding? = null 17 | private val mBinding get() = _binding!! 18 | private lateinit var mViewModel: StartFragmentViewModel 19 | 20 | override fun onCreateView( 21 | inflater: LayoutInflater, container: ViewGroup?, 22 | savedInstanceState: Bundle?, 23 | ): View { 24 | _binding = FragmentStartBinding.inflate(layoutInflater, container, false) 25 | return mBinding.root 26 | } 27 | 28 | override fun onStart() { 29 | super.onStart() 30 | 31 | mViewModel = ViewModelProvider(this).get(StartFragmentViewModel::class.java) 32 | 33 | if (AppPreference.getInitUser()) { 34 | mViewModel.initDatabase(AppPreference.getTypeDB()) { 35 | APP_ACTIVITY.navController.navigate(R.id.action_startFragment_to_mainFragment) 36 | } 37 | } else initialization() 38 | } 39 | 40 | private fun initialization() { 41 | mBinding.btnRoom.setOnClickListener { 42 | mViewModel.initDatabase(TYPE_ROOM) { 43 | AppPreference.setInitUser(true) 44 | AppPreference.setTypeDB(TYPE_ROOM) 45 | APP_ACTIVITY.navController.navigate(R.id.action_startFragment_to_mainFragment) 46 | } 47 | } 48 | 49 | mBinding.btnFirebase.setOnClickListener { 50 | mBinding.inputEmail.visibility = VISIBLE 51 | mBinding.inputPassword.visibility = VISIBLE 52 | mBinding.btnLogin.visibility = VISIBLE 53 | mBinding.btnLogin.setOnClickListener { 54 | val inputEmail = mBinding.inputEmail.text.toString() 55 | val inputPassword = mBinding.inputPassword.text.toString() 56 | if (inputEmail.isNotEmpty() && inputPassword.isNotEmpty()) { 57 | EMAIL = inputEmail 58 | PASSWORD = inputPassword 59 | mViewModel.initDatabase(TYPE_FIREBASE) { 60 | 61 | AppPreference.setInitUser(true) 62 | AppPreference.setTypeDB(TYPE_FIREBASE) 63 | 64 | APP_ACTIVITY.navController.navigate(R.id.action_startFragment_to_mainFragment) 65 | } 66 | } else showToast(getString(R.string.toast_wrong_enter)) 67 | } 68 | } 69 | } 70 | 71 | override fun onDestroyView() { 72 | super.onDestroyView() 73 | _binding = null 74 | } 75 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/notes_mvvm/screens/start/StartFragmentViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.example.notes_mvvm.screens.start 2 | 3 | import android.app.Application 4 | import androidx.lifecycle.AndroidViewModel 5 | import com.example.notes_mvvm.database.firebase.AppFirebaseRepository 6 | import com.example.notes_mvvm.database.room.AppRoomDatabase 7 | import com.example.notes_mvvm.database.room.AppRoomRepository 8 | import com.example.notes_mvvm.utilits.* 9 | 10 | class StartFragmentViewModel(application: Application) : AndroidViewModel(application) { 11 | private val mContext = application 12 | 13 | fun initDatabase(type: String, onSuccess: () -> Unit) { 14 | when (type) { 15 | TYPE_ROOM -> { 16 | val dao = AppRoomDatabase.getInstance(mContext).getAppRoomDao() 17 | REPOSITORY = AppRoomRepository(dao) 18 | onSuccess() 19 | } 20 | 21 | TYPE_FIREBASE -> { 22 | REPOSITORY = AppFirebaseRepository() 23 | REPOSITORY.connectToDatabase({ onSuccess() }, { showToast(it) }) 24 | } 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/notes_mvvm/utilits/AppPreference.kt: -------------------------------------------------------------------------------- 1 | package com.example.notes_mvvm.utilits 2 | 3 | import android.content.Context 4 | import android.content.SharedPreferences 5 | 6 | object AppPreference { 7 | 8 | private const val INIT_USER = "init_user" 9 | private const val TYPE_DB = "typeDB" 10 | private const val NAME_PREF = "preference" 11 | 12 | private lateinit var mPreference: SharedPreferences 13 | 14 | fun getPreference(context: Context): SharedPreferences { 15 | mPreference = context.getSharedPreferences(NAME_PREF, Context.MODE_PRIVATE) 16 | return mPreference 17 | } 18 | 19 | fun setInitUser(init: Boolean) { 20 | mPreference.edit() 21 | .putBoolean(INIT_USER, init) 22 | .apply() 23 | } 24 | 25 | fun setTypeDB(type: String) { 26 | mPreference.edit() 27 | .putString(TYPE_DB, type) 28 | .apply() 29 | } 30 | 31 | fun getInitUser(): Boolean { 32 | return mPreference.getBoolean(INIT_USER, false) 33 | } 34 | 35 | fun getTypeDB(): String { 36 | return mPreference.getString(TYPE_DB, TYPE_ROOM).toString() 37 | } 38 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/notes_mvvm/utilits/constants.kt: -------------------------------------------------------------------------------- 1 | package com.example.notes_mvvm.utilits 2 | 3 | import com.example.notes_mvvm.MainActivity 4 | import com.example.notes_mvvm.database.DatabaseRepository 5 | import com.google.firebase.auth.FirebaseAuth 6 | import com.google.firebase.database.DatabaseReference 7 | 8 | lateinit var AUTH: FirebaseAuth 9 | lateinit var CURRENT_ID: String 10 | lateinit var REF_DATABASE: DatabaseReference 11 | lateinit var APP_ACTIVITY: MainActivity 12 | lateinit var REPOSITORY: DatabaseRepository 13 | 14 | const val TYPE_ROOM = "type_room" 15 | const val TYPE_FIREBASE = "type_firebase" 16 | lateinit var EMAIL: String 17 | lateinit var PASSWORD: String 18 | const val ID_FIREBASE = "idFirebase" 19 | const val NAME = "name" 20 | const val TEXT = "text" -------------------------------------------------------------------------------- /app/src/main/java/com/example/notes_mvvm/utilits/funs.kt: -------------------------------------------------------------------------------- 1 | package com.example.notes_mvvm.utilits 2 | 3 | import android.widget.Toast 4 | 5 | fun showToast(message: String) { 6 | Toast.makeText(APP_ACTIVITY, message, Toast.LENGTH_SHORT).show() 7 | } -------------------------------------------------------------------------------- /app/src/main/res/anim/slide_right_to_left_enter.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/anim/slide_right_to_left_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_add.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_delete.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_exit.xml: -------------------------------------------------------------------------------- 1 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/screen_enter_server.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Picalfer/Notes_mvvm/cdeba09a1ac3b4c627234f4902e98fe07767e445/app/src/main/res/drawable/screen_enter_server.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable/screen_main.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Picalfer/Notes_mvvm/cdeba09a1ac3b4c627234f4902e98fe07767e445/app/src/main/res/drawable/screen_main.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable/screen_new_note.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Picalfer/Notes_mvvm/cdeba09a1ac3b4c627234f4902e98fe07767e445/app/src/main/res/drawable/screen_new_note.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable/screen_note_view.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Picalfer/Notes_mvvm/cdeba09a1ac3b4c627234f4902e98fe07767e445/app/src/main/res/drawable/screen_note_view.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable/screen_notes_list.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Picalfer/Notes_mvvm/cdeba09a1ac3b4c627234f4902e98fe07767e445/app/src/main/res/drawable/screen_notes_list.webp -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 18 | 19 | 30 | 31 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_add_new_note.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 18 | 19 | 28 | 29 |