├── .gitignore ├── .idea ├── .name ├── codeStyles │ ├── Project.xml │ └── codeStyleConfig.xml ├── gradle.xml ├── jarRepositories.xml ├── markdown-navigator-enh.xml ├── markdown-navigator.xml ├── misc.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro ├── schemas │ └── com.javadsh98.mjpersiondictionary.data.db.WordDB │ │ ├── 1.json │ │ ├── 2.json │ │ └── 3.json └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── javadsh98 │ │ └── mjpersiondictionary │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── assets │ │ └── database │ │ │ ├── data.json │ │ │ └── my_database │ ├── java │ │ └── com │ │ │ └── javadsh98 │ │ │ └── mjpersiondictionary │ │ │ ├── data │ │ │ ├── Repository.kt │ │ │ ├── db │ │ │ │ ├── WordDB.kt │ │ │ │ ├── convertor │ │ │ │ │ └── Converter.kt │ │ │ │ ├── dao │ │ │ │ │ └── WordDao.kt │ │ │ │ └── entity │ │ │ │ │ └── Word.kt │ │ │ └── gson │ │ │ │ └── Model.kt │ │ │ ├── ui │ │ │ └── main │ │ │ │ ├── activity │ │ │ │ ├── MainActivity.kt │ │ │ │ ├── MainViewModel.kt │ │ │ │ └── NavigationExtensions.kt │ │ │ │ └── fragment │ │ │ │ ├── FragmentFactoryImpl.kt │ │ │ │ ├── History │ │ │ │ ├── HistoryAdapter.kt │ │ │ │ ├── HistoryFragment.kt │ │ │ │ └── HistoryViewModel.kt │ │ │ │ ├── detail │ │ │ │ ├── DetailFragment.kt │ │ │ │ └── DetailViewModel.kt │ │ │ │ ├── favorite │ │ │ │ ├── FavoriteAdapter.kt │ │ │ │ ├── FavoriteFragment.kt │ │ │ │ ├── FavoriteHolder.kt │ │ │ │ └── FavoriteViewModel.kt │ │ │ │ ├── home │ │ │ │ ├── HomeAdapter.kt │ │ │ │ ├── HomeFragment.kt │ │ │ │ ├── HomeViewModel.kt │ │ │ │ └── NormalHolder.kt │ │ │ │ └── more │ │ │ │ └── MoreFragment.kt │ │ │ └── utils │ │ │ ├── BottomNavigationBehavior.java │ │ │ └── Utils.kt │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ ├── back_all_corner.xml │ │ ├── ic_all_dislike_24.xml │ │ ├── ic_all_like_24.xml │ │ ├── ic_launcher_background.xml │ │ ├── ic_menu_favorite_24.xml │ │ ├── ic_menu_history_24.xml │ │ ├── ic_menu_home_24.xml │ │ └── ic_menu_more_24.xml │ │ ├── font │ │ └── regular.ttf │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── dialog_detail.xml │ │ ├── fragment_detail.xml │ │ ├── fragment_favorite.xml │ │ ├── fragment_history.xml │ │ ├── fragment_home.xml │ │ ├── fragment_more.xml │ │ └── item_normal.xml │ │ ├── menu │ │ └── navigation_menu.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── navigation │ │ ├── favorite.xml │ │ ├── history.xml │ │ ├── home.xml │ │ └── more.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── javadsh98 │ └── mjpersiondictionary │ └── ExampleUnitTest.kt ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── pic ├── pic_1.jpg ├── pic_2.jpg └── pic_3.jpg └── 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 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | MJ persion Dictionary -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 20 | 22 | 23 | 24 | 26 | 27 | 28 |
29 | 30 | 31 | 32 | xmlns:android 33 | 34 | ^$ 35 | 36 | 37 | 38 |
39 |
40 | 41 | 42 | 43 | xmlns:.* 44 | 45 | ^$ 46 | 47 | 48 | BY_NAME 49 | 50 |
51 |
52 | 53 | 54 | 55 | .*:id 56 | 57 | http://schemas.android.com/apk/res/android 58 | 59 | 60 | 61 |
62 |
63 | 64 | 65 | 66 | .*:name 67 | 68 | http://schemas.android.com/apk/res/android 69 | 70 | 71 | 72 |
73 |
74 | 75 | 76 | 77 | name 78 | 79 | ^$ 80 | 81 | 82 | 83 |
84 |
85 | 86 | 87 | 88 | style 89 | 90 | ^$ 91 | 92 | 93 | 94 |
95 |
96 | 97 | 98 | 99 | .* 100 | 101 | ^$ 102 | 103 | 104 | BY_NAME 105 | 106 |
107 |
108 | 109 | 110 | 111 | .* 112 | 113 | http://schemas.android.com/apk/res/android 114 | 115 | 116 | ANDROID_ATTRIBUTE_ORDER 117 | 118 |
119 |
120 | 121 | 122 | 123 | .* 124 | 125 | .* 126 | 127 | 128 | BY_NAME 129 | 130 |
131 |
132 |
133 |
134 | 135 | 137 |
138 |
-------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 19 | 20 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | -------------------------------------------------------------------------------- /.idea/markdown-navigator-enh.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /.idea/markdown-navigator.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 29 | 30 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | 10 | 11 | 12 | 13 | 1.8 14 | 15 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MVVM-persian-Dictionary 2 | This is a english-persian dictionary that use MVVM design pattern and room library for sqlite data base . 3 | ### Room 4 | As i mentioned earlier i use Room library for interact with my prepopulate database. 5 |
Room support prepopulate database in version 2.2.0 6 | ```kotlin 7 | Room.databaseBuilder(application, WordDB::class.java, "w_database") 8 | .createFromAsset("database/word_db") 9 | .build() 10 | ``` 11 | ### Screenshot 12 | 13 | ![img_1](/pic/pic_1.jpg) 14 | ![img_2](/pic/pic_2.jpg) 15 | ![img_3](/pic/pic_3.jpg) 16 | 17 | ### Resources 18 | [ViewBinding](https://developer.android.com/topic/libraries/view-binding) 19 | 20 | [pre-populate database](https://developer.android.com/jetpack/androidx/releases/room) 21 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'kotlin-android-extensions' 4 | apply plugin: "kotlin-kapt" 5 | apply plugin: "androidx.navigation.safeargs.kotlin" 6 | 7 | android { 8 | signingConfigs { 9 | release { 10 | storeFile file('C:\\Users\\Muhammad Javad\\Desktop\\dictionary key\\dic_key.jks') 11 | storePassword '123456' 12 | keyAlias 'dic_key' 13 | keyPassword '123456' 14 | } 15 | } 16 | compileSdkVersion 29 17 | buildToolsVersion "29.0.3" 18 | 19 | defaultConfig { 20 | applicationId "com.javadsh98.mjpersiondictionary" 21 | minSdkVersion 21 22 | targetSdkVersion 29 23 | versionCode 1 24 | versionName "1.0" 25 | 26 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 27 | multiDexEnabled true 28 | 29 | javaCompileOptions { 30 | annotationProcessorOptions { 31 | arguments = ["room.schemaLocation": 32 | "$projectDir/schemas".toString()] 33 | } 34 | } 35 | } 36 | 37 | buildTypes { 38 | release { 39 | minifyEnabled false 40 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 41 | signingConfig signingConfigs.release 42 | } 43 | } 44 | compileOptions { 45 | sourceCompatibility = 1.8 46 | targetCompatibility = 1.8 47 | } 48 | kotlinOptions { 49 | jvmTarget = JavaVersion.VERSION_1_8.toString() 50 | } 51 | 52 | viewBinding { 53 | enabled = true 54 | } 55 | 56 | } 57 | 58 | dependencies { 59 | 60 | implementation 'androidx.legacy:legacy-support-v4:1.0.0' 61 | testImplementation test 62 | implementation Libraries 63 | androidTestImplementation androidTest 64 | kapt kotlinApt 65 | api androidPi 66 | 67 | } 68 | -------------------------------------------------------------------------------- /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 22 | -------------------------------------------------------------------------------- /app/schemas/com.javadsh98.mjpersiondictionary.data.db.WordDB/1.json: -------------------------------------------------------------------------------- 1 | { 2 | "formatVersion": 1, 3 | "database": { 4 | "version": 1, 5 | "identityHash": "c4daab953ac035f41a9777885856d754", 6 | "entities": [ 7 | { 8 | "tableName": "word_tb", 9 | "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `persian_word` TEXT NOT NULL, `english_word` TEXT NOT NULL, `favorite` INTEGER NOT NULL, `view_count` INTEGER NOT NULL, `fav_date` INTEGER)", 10 | "fields": [ 11 | { 12 | "fieldPath": "id", 13 | "columnName": "id", 14 | "affinity": "INTEGER", 15 | "notNull": true 16 | }, 17 | { 18 | "fieldPath": "persianWord", 19 | "columnName": "persian_word", 20 | "affinity": "TEXT", 21 | "notNull": true 22 | }, 23 | { 24 | "fieldPath": "englishWord", 25 | "columnName": "english_word", 26 | "affinity": "TEXT", 27 | "notNull": true 28 | }, 29 | { 30 | "fieldPath": "favorite", 31 | "columnName": "favorite", 32 | "affinity": "INTEGER", 33 | "notNull": true 34 | }, 35 | { 36 | "fieldPath": "viewCount", 37 | "columnName": "view_count", 38 | "affinity": "INTEGER", 39 | "notNull": true 40 | }, 41 | { 42 | "fieldPath": "date", 43 | "columnName": "fav_date", 44 | "affinity": "INTEGER", 45 | "notNull": false 46 | } 47 | ], 48 | "primaryKey": { 49 | "columnNames": [ 50 | "id" 51 | ], 52 | "autoGenerate": true 53 | }, 54 | "indices": [], 55 | "foreignKeys": [] 56 | } 57 | ], 58 | "views": [], 59 | "setupQueries": [ 60 | "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", 61 | "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'c4daab953ac035f41a9777885856d754')" 62 | ] 63 | } 64 | } -------------------------------------------------------------------------------- /app/schemas/com.javadsh98.mjpersiondictionary.data.db.WordDB/2.json: -------------------------------------------------------------------------------- 1 | { 2 | "formatVersion": 1, 3 | "database": { 4 | "version": 2, 5 | "identityHash": "5b3834e4fe3e767ddefc6d67605c7649", 6 | "entities": [ 7 | { 8 | "tableName": "word_tb", 9 | "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `persian_word` TEXT NOT NULL, `english_word` TEXT NOT NULL, `italian_word` TEXT NOT NULL, `favorite` INTEGER NOT NULL, `view_count` INTEGER NOT NULL, `fav_date` INTEGER)", 10 | "fields": [ 11 | { 12 | "fieldPath": "id", 13 | "columnName": "id", 14 | "affinity": "INTEGER", 15 | "notNull": true 16 | }, 17 | { 18 | "fieldPath": "persianWord", 19 | "columnName": "persian_word", 20 | "affinity": "TEXT", 21 | "notNull": true 22 | }, 23 | { 24 | "fieldPath": "englishWord", 25 | "columnName": "english_word", 26 | "affinity": "TEXT", 27 | "notNull": true 28 | }, 29 | { 30 | "fieldPath": "italianWord", 31 | "columnName": "italian_word", 32 | "affinity": "TEXT", 33 | "notNull": true 34 | }, 35 | { 36 | "fieldPath": "favorite", 37 | "columnName": "favorite", 38 | "affinity": "INTEGER", 39 | "notNull": true 40 | }, 41 | { 42 | "fieldPath": "viewCount", 43 | "columnName": "view_count", 44 | "affinity": "INTEGER", 45 | "notNull": true 46 | }, 47 | { 48 | "fieldPath": "date", 49 | "columnName": "fav_date", 50 | "affinity": "INTEGER", 51 | "notNull": false 52 | } 53 | ], 54 | "primaryKey": { 55 | "columnNames": [ 56 | "id" 57 | ], 58 | "autoGenerate": true 59 | }, 60 | "indices": [], 61 | "foreignKeys": [] 62 | } 63 | ], 64 | "views": [], 65 | "setupQueries": [ 66 | "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", 67 | "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '5b3834e4fe3e767ddefc6d67605c7649')" 68 | ] 69 | } 70 | } -------------------------------------------------------------------------------- /app/schemas/com.javadsh98.mjpersiondictionary.data.db.WordDB/3.json: -------------------------------------------------------------------------------- 1 | { 2 | "formatVersion": 1, 3 | "database": { 4 | "version": 3, 5 | "identityHash": "5b3834e4fe3e767ddefc6d67605c7649", 6 | "entities": [ 7 | { 8 | "tableName": "word_tb", 9 | "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `persian_word` TEXT NOT NULL, `english_word` TEXT NOT NULL, `italian_word` TEXT NOT NULL, `favorite` INTEGER NOT NULL, `view_count` INTEGER NOT NULL, `fav_date` INTEGER)", 10 | "fields": [ 11 | { 12 | "fieldPath": "id", 13 | "columnName": "id", 14 | "affinity": "INTEGER", 15 | "notNull": true 16 | }, 17 | { 18 | "fieldPath": "persianWord", 19 | "columnName": "persian_word", 20 | "affinity": "TEXT", 21 | "notNull": true 22 | }, 23 | { 24 | "fieldPath": "englishWord", 25 | "columnName": "english_word", 26 | "affinity": "TEXT", 27 | "notNull": true 28 | }, 29 | { 30 | "fieldPath": "italianWord", 31 | "columnName": "italian_word", 32 | "affinity": "TEXT", 33 | "notNull": true 34 | }, 35 | { 36 | "fieldPath": "favorite", 37 | "columnName": "favorite", 38 | "affinity": "INTEGER", 39 | "notNull": true 40 | }, 41 | { 42 | "fieldPath": "viewCount", 43 | "columnName": "view_count", 44 | "affinity": "INTEGER", 45 | "notNull": true 46 | }, 47 | { 48 | "fieldPath": "date", 49 | "columnName": "fav_date", 50 | "affinity": "INTEGER", 51 | "notNull": false 52 | } 53 | ], 54 | "primaryKey": { 55 | "columnNames": [ 56 | "id" 57 | ], 58 | "autoGenerate": true 59 | }, 60 | "indices": [], 61 | "foreignKeys": [] 62 | } 63 | ], 64 | "views": [], 65 | "setupQueries": [ 66 | "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", 67 | "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '5b3834e4fe3e767ddefc6d67605c7649')" 68 | ] 69 | } 70 | } -------------------------------------------------------------------------------- /app/src/androidTest/java/com/javadsh98/mjpersiondictionary/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.javadsh98.mjpersiondictionary 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.javadsh98.mjpersiondictionary", appContext.packageName) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/assets/database/my_database: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Muhammad-Javad/MVVM-persian-Dictionary/235432a74227d02a78390b6763368e82d188cf29/app/src/main/assets/database/my_database -------------------------------------------------------------------------------- /app/src/main/java/com/javadsh98/mjpersiondictionary/data/Repository.kt: -------------------------------------------------------------------------------- 1 | package com.javadsh98.mjpersiondictionary.data 2 | 3 | import android.app.Application 4 | import androidx.lifecycle.LiveData 5 | import com.javadsh98.mjpersiondictionary.data.db.WordDB 6 | import com.javadsh98.mjpersiondictionary.data.db.entity.Word 7 | 8 | import kotlinx.coroutines.CoroutineScope 9 | 10 | class Repository private constructor(var application: Application, var scope: CoroutineScope){ 11 | 12 | lateinit var database: WordDB 13 | 14 | init { 15 | database = WordDB.getInstance(application, scope) 16 | } 17 | 18 | companion object{ 19 | 20 | var INSTANCE : Repository? = null 21 | 22 | fun getInstance(application: Application, scope: CoroutineScope): Repository{ 23 | if (INSTANCE == null) 24 | INSTANCE = Repository(application, scope) 25 | return INSTANCE as Repository 26 | } 27 | 28 | } 29 | 30 | // suspend fun getAllWords(): LiveData>{ 31 | // return database.getWordDao().readAllEnglishWord() 32 | // } 33 | 34 | suspend fun searchEnglish(english: String) : LiveData>{ 35 | return database.getWordDao().readEnglishWord(english) 36 | } 37 | 38 | suspend fun searchPersian(persian: String) : LiveData>{ 39 | return database.getWordDao().readPersianWord(persian) 40 | } 41 | 42 | suspend fun update(word: Word){ 43 | database.getWordDao().update(word) 44 | } 45 | 46 | suspend fun searchFavorites(): LiveData>{ 47 | return database.getWordDao().readAllFavorite() 48 | } 49 | 50 | suspend fun getHistories(): LiveData>{ 51 | return database.getWordDao().readHistories() 52 | } 53 | 54 | suspend fun clearHistories(){ 55 | database.getWordDao().clearHistories() 56 | } 57 | 58 | fun getDefaultWord(number: Int) : LiveData>{ 59 | return database.getWordDao().getDefaultWord(number) 60 | } 61 | 62 | } -------------------------------------------------------------------------------- /app/src/main/java/com/javadsh98/mjpersiondictionary/data/db/WordDB.kt: -------------------------------------------------------------------------------- 1 | package com.javadsh98.mjpersiondictionary.data.db 2 | 3 | import android.app.Application 4 | import android.util.Log 5 | import androidx.room.Database 6 | import androidx.room.Room 7 | import androidx.room.RoomDatabase 8 | import androidx.room.TypeConverters 9 | import androidx.room.migration.Migration 10 | import androidx.sqlite.db.SupportSQLiteDatabase 11 | import com.google.gson.Gson 12 | import com.javadsh98.mjpersiondictionary.data.db.convertor.Converter 13 | import com.javadsh98.mjpersiondictionary.data.db.dao.WordDao 14 | import com.javadsh98.mjpersiondictionary.data.db.entity.Word 15 | import com.javadsh98.mjpersiondictionary.data.gson.Dictionary 16 | import kotlinx.coroutines.CoroutineScope 17 | import kotlinx.coroutines.launch 18 | import java.io.IOException 19 | import java.io.InputStream 20 | 21 | @Database(entities = arrayOf(Word::class) , version = 3, exportSchema = true ) 22 | @TypeConverters(value = arrayOf(Converter::class)) 23 | abstract class WordDB : RoomDatabase() { 24 | 25 | abstract fun getWordDao(): WordDao 26 | 27 | companion object{ 28 | val TAG = WordDB::class.java.simpleName 29 | 30 | var migration1_2: Migration = object : Migration(1, 2) { 31 | override fun migrate(database: SupportSQLiteDatabase) { 32 | } 33 | } 34 | 35 | var migration1_3: Migration = object : Migration(1, 3) { 36 | override fun migrate(database: SupportSQLiteDatabase) { 37 | } 38 | } 39 | 40 | var migration2_3: Migration = object : Migration(2, 3) { 41 | override fun migrate(database: SupportSQLiteDatabase) { 42 | } 43 | } 44 | 45 | var INSTANCE : WordDB? = null 46 | 47 | fun getInstance(application: Application, scope: CoroutineScope): WordDB{ 48 | if(INSTANCE == null) 49 | INSTANCE = Room.databaseBuilder(application, WordDB::class.java, "w_database") 50 | // .addCallback(WordCallBack(scope, application)) 51 | // .addMigrations(migration1_2) 52 | // .addMigrations(migration1_3) 53 | // .addMigrations(migration2_3) 54 | .fallbackToDestructiveMigration() 55 | .createFromAsset("database/my_database") 56 | .build() 57 | return INSTANCE as WordDB 58 | } 59 | 60 | } 61 | 62 | private class WordCallBack(val scope: CoroutineScope, val application: Application) : RoomDatabase.Callback() { 63 | 64 | override fun onCreate(db: SupportSQLiteDatabase) { 65 | super.onCreate(db) 66 | } 67 | 68 | override fun onOpen(db: SupportSQLiteDatabase) { 69 | super.onOpen(db) 70 | 71 | INSTANCE.let { 72 | 73 | scope.launch { 74 | it?.getWordDao()?.deleteAllWords() 75 | 76 | var json: String? = null 77 | try { 78 | val inputStream: InputStream = application.assets.open("database/data.json") 79 | val size: Int = inputStream.available() 80 | val buffer = ByteArray(size) 81 | inputStream.read(buffer) 82 | inputStream.close() 83 | json = String(buffer, Charsets.UTF_8) 84 | } catch (e: IOException) { 85 | e.printStackTrace() 86 | } 87 | 88 | var gson = Gson() 89 | var word = gson.fromJson(json, Dictionary::class.java) 90 | 91 | for (i in 0 until word.list.size){ 92 | var w = word.list[i] 93 | var word = Word(persianWord = w.persian, englishWord = w.englisgh) 94 | Log.d(TAG, "english word = ${word.englishWord}") 95 | it?.getWordDao()?.insert(word) 96 | } 97 | 98 | } 99 | 100 | 101 | } 102 | 103 | } 104 | } 105 | 106 | } -------------------------------------------------------------------------------- /app/src/main/java/com/javadsh98/mjpersiondictionary/data/db/convertor/Converter.kt: -------------------------------------------------------------------------------- 1 | package com.javadsh98.mjpersiondictionary.data.db.convertor 2 | 3 | import androidx.room.TypeConverter 4 | import java.util.* 5 | 6 | class Converter { 7 | 8 | 9 | @TypeConverter 10 | fun toDate(time: Long) : Date 11 | = Date(time) 12 | 13 | @TypeConverter 14 | fun toLong(date: Date) : Long 15 | = date.time 16 | 17 | } -------------------------------------------------------------------------------- /app/src/main/java/com/javadsh98/mjpersiondictionary/data/db/dao/WordDao.kt: -------------------------------------------------------------------------------- 1 | package com.javadsh98.mjpersiondictionary.data.db.dao 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MutableLiveData 5 | import androidx.room.* 6 | import com.javadsh98.mjpersiondictionary.data.db.entity.Word 7 | 8 | @Dao 9 | interface WordDao{ 10 | 11 | @Insert 12 | suspend fun insert(word: Word) 13 | 14 | @Update(onConflict = OnConflictStrategy.REPLACE) 15 | suspend fun update(word: Word) 16 | 17 | @Delete 18 | suspend fun delete(word: Word) 19 | 20 | @Query("delete from word_tb") 21 | suspend fun deleteAllWords() 22 | 23 | @Query("SELECT * FROM word_tb WHERE :persian like english_word") 24 | fun readEnglishWord(persian: String): LiveData> 25 | 26 | @Query("SELECT * FROM word_tb WHERE :english like persian_word") 27 | fun readPersianWord(english: String): LiveData> 28 | 29 | @Query("SELECT * FROM word_tb WHERE favorite = 1 ORDER BY fav_date DESC") 30 | fun readAllFavorite(): LiveData> 31 | 32 | @Query("SELECT * FROM word_tb WHERE view_count > 0 ORDER BY view_count ASC") 33 | fun readHistories(): LiveData> 34 | 35 | @Query("update word_tb set view_count = 0 where view_count > 0") 36 | suspend fun clearHistories() 37 | 38 | @Query("select * from word_tb limit :number") 39 | fun getDefaultWord(number: Int): LiveData> 40 | 41 | } -------------------------------------------------------------------------------- /app/src/main/java/com/javadsh98/mjpersiondictionary/data/db/entity/Word.kt: -------------------------------------------------------------------------------- 1 | package com.javadsh98.mjpersiondictionary.data.db.entity 2 | 3 | import android.os.Parcelable 4 | import androidx.room.ColumnInfo 5 | import androidx.room.Entity 6 | import androidx.room.PrimaryKey 7 | import kotlinx.android.parcel.Parcelize 8 | import java.util.* 9 | 10 | @Parcelize 11 | @Entity(tableName = "word_tb") 12 | data class Word constructor(@PrimaryKey(autoGenerate = true) var id: Int = 0 13 | , @ColumnInfo(name = "persian_word") var persianWord: String = "" 14 | , @ColumnInfo(name = "english_word") var englishWord: String = "" 15 | , @ColumnInfo(name = "italian_word") var italianWord: String = "" 16 | , @ColumnInfo(name = "favorite") var favorite: Boolean = false 17 | , @ColumnInfo(name = "view_count") var viewCount: Int = 0 18 | , @ColumnInfo(name = "fav_date") var date: Date? = Date(0)): Parcelable 19 | -------------------------------------------------------------------------------- /app/src/main/java/com/javadsh98/mjpersiondictionary/data/gson/Model.kt: -------------------------------------------------------------------------------- 1 | package com.javadsh98.mjpersiondictionary.data.gson 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class Word(@SerializedName("EnglishWord") var englisgh: String 6 | , @SerializedName("PersianWord") var persian: String) 7 | 8 | data class Dictionary(@SerializedName("dirctionary")var list: List) 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/java/com/javadsh98/mjpersiondictionary/ui/main/activity/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.javadsh98.mjpersiondictionary.ui.main.activity 2 | 3 | import android.os.Bundle 4 | import android.view.View 5 | import androidx.appcompat.app.AppCompatActivity 6 | import androidx.lifecycle.LiveData 7 | import androidx.lifecycle.Observer 8 | import androidx.navigation.NavController 9 | import androidx.navigation.findNavController 10 | import androidx.navigation.ui.AppBarConfiguration 11 | import androidx.navigation.ui.setupActionBarWithNavController 12 | import androidx.navigation.ui.setupWithNavController 13 | import com.google.android.material.bottomnavigation.BottomNavigationView 14 | import com.javadsh98.mjpersiondictionary.R 15 | import com.javadsh98.mjpersiondictionary.databinding.ActivityMainBinding 16 | import com.javadsh98.mjpersiondictionary.ui.main.fragment.FragmentFactoryImpl 17 | import kotlinx.android.synthetic.main.activity_main.* 18 | 19 | class MainActivity : AppCompatActivity(){ 20 | 21 | lateinit var binding: ActivityMainBinding 22 | lateinit var root: View 23 | 24 | private var currentNavController: LiveData? = null 25 | 26 | override fun onCreate(savedInstanceState: Bundle?) { 27 | 28 | super.onCreate(savedInstanceState) 29 | binding = ActivityMainBinding.inflate(layoutInflater) 30 | root = binding.root 31 | setContentView(root) 32 | 33 | if (savedInstanceState == null) { 34 | setupActionbar() 35 | setupBottomNavigationBar() 36 | } // Else, need to wait for onRestoreInstanceState 37 | 38 | } 39 | 40 | override fun onRestoreInstanceState(savedInstanceState: Bundle?) { 41 | super.onRestoreInstanceState(savedInstanceState) 42 | // Now that BottomNavigationBar has restored its instance state 43 | // and its selectedItemId, we can proceed with setting up the 44 | // BottomNavigationBar with Navigation 45 | setupActionbar() 46 | setupBottomNavigationBar() 47 | } 48 | 49 | /** 50 | * Called on first creation and when restoring state. 51 | */ 52 | private fun setupBottomNavigationBar() { 53 | val bottomNavigationView = findViewById(R.id.bottomnavigation_main_menu) 54 | 55 | val navGraphIds = listOf(R.navigation.home, R.navigation.favorite, R.navigation.history, R.navigation.more) 56 | 57 | // Setup the bottom navigation view with a list of navigation graphs 58 | val controller = bottomNavigationView.setupWithNavController( 59 | navGraphIds = navGraphIds, 60 | fragmentManager = supportFragmentManager, 61 | containerId = R.id.container_main_fragment, 62 | intent = intent 63 | ) 64 | 65 | // Whenever the selected controller changes, setup the action bar. 66 | controller.observe(this, Observer { navController -> 67 | setupActionBarWithNavController(navController) 68 | }) 69 | currentNavController = controller 70 | } 71 | 72 | override fun onSupportNavigateUp(): Boolean { 73 | return currentNavController?.value?.navigateUp() ?: false 74 | } 75 | 76 | private fun setupActionbar(){ 77 | setSupportActionBar(toolbar_main) 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /app/src/main/java/com/javadsh98/mjpersiondictionary/ui/main/activity/MainViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.javadsh98.mjpersiondictionary.ui.main.activity 2 | 3 | import androidx.lifecycle.ViewModel 4 | 5 | class MainViewModel : ViewModel(){ 6 | 7 | private var bottomNavigationPosition: BottomPosition = BottomPosition.HOME 8 | 9 | fun setPosition(position: BottomPosition) { 10 | this.bottomNavigationPosition = position 11 | } 12 | 13 | fun getPosition(): BottomPosition = bottomNavigationPosition 14 | 15 | } 16 | 17 | enum class BottomPosition { 18 | HOME, FAVORITE, HISTORY, MORE 19 | } -------------------------------------------------------------------------------- /app/src/main/java/com/javadsh98/mjpersiondictionary/ui/main/activity/NavigationExtensions.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019, The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.javadsh98.mjpersiondictionary.ui.main.activity 18 | 19 | import android.content.Intent 20 | import android.util.SparseArray 21 | import androidx.core.util.forEach 22 | import androidx.core.util.set 23 | import androidx.fragment.app.FragmentManager 24 | import androidx.lifecycle.LiveData 25 | import androidx.lifecycle.MutableLiveData 26 | import androidx.navigation.NavController 27 | import androidx.navigation.fragment.NavHostFragment 28 | import com.google.android.material.bottomnavigation.BottomNavigationView 29 | import com.javadsh98.mjpersiondictionary.R 30 | 31 | /** 32 | * Manages the various graphs needed for a [BottomNavigationView]. 33 | * 34 | * This sample is a workaround until the Navigation Component supports multiple back stacks. 35 | */ 36 | fun BottomNavigationView.setupWithNavController( 37 | navGraphIds: List, 38 | fragmentManager: FragmentManager, 39 | containerId: Int, 40 | intent: Intent 41 | ): LiveData { 42 | 43 | // Map of tags 44 | val graphIdToTagMap = SparseArray() 45 | // Result. Mutable live data with the selected controlled 46 | val selectedNavController = MutableLiveData() 47 | 48 | var firstFragmentGraphId = 0 49 | 50 | // First create a NavHostFragment for each NavGraph ID 51 | navGraphIds.forEachIndexed { index, navGraphId -> 52 | val fragmentTag = 53 | getFragmentTag( 54 | index 55 | ) 56 | 57 | // Find or create the Navigation host fragment 58 | val navHostFragment = 59 | obtainNavHostFragment( 60 | fragmentManager, 61 | fragmentTag, 62 | navGraphId, 63 | containerId 64 | ) 65 | 66 | // Obtain its id 67 | val graphId = navHostFragment.navController.graph.id 68 | 69 | if (index == 0) { 70 | firstFragmentGraphId = graphId 71 | } 72 | 73 | // Save to the map 74 | graphIdToTagMap[graphId] = fragmentTag 75 | 76 | // Attach or detach nav host fragment depending on whether it's the selected item. 77 | if (this.selectedItemId == graphId) { 78 | // Update livedata with the selected graph 79 | selectedNavController.value = navHostFragment.navController 80 | attachNavHostFragment( 81 | fragmentManager, 82 | navHostFragment, 83 | index == 0 84 | ) 85 | } else { 86 | detachNavHostFragment( 87 | fragmentManager, 88 | navHostFragment 89 | ) 90 | } 91 | } 92 | 93 | // Now connect selecting an item with swapping Fragments 94 | var selectedItemTag = graphIdToTagMap[this.selectedItemId] 95 | val firstFragmentTag = graphIdToTagMap[firstFragmentGraphId] 96 | var isOnFirstFragment = selectedItemTag == firstFragmentTag 97 | 98 | // When a navigation item is selected 99 | setOnNavigationItemSelectedListener { item -> 100 | // Don't do anything if the state is state has already been saved. 101 | if (fragmentManager.isStateSaved) { 102 | false 103 | } else { 104 | val newlySelectedItemTag = graphIdToTagMap[item.itemId] 105 | if (selectedItemTag != newlySelectedItemTag) { 106 | // Pop everything above the first fragment (the "fixed start destination") 107 | fragmentManager.popBackStack(firstFragmentTag, 108 | FragmentManager.POP_BACK_STACK_INCLUSIVE) 109 | val selectedFragment = fragmentManager.findFragmentByTag(newlySelectedItemTag) 110 | as NavHostFragment 111 | 112 | // Exclude the first fragment tag because it's always in the back stack. 113 | if (firstFragmentTag != newlySelectedItemTag) { 114 | // Commit a transaction that cleans the back stack and adds the first fragment 115 | // to it, creating the fixed started destination. 116 | fragmentManager.beginTransaction() 117 | .setCustomAnimations( 118 | R.anim.nav_default_enter_anim, 119 | R.anim.nav_default_exit_anim, 120 | R.anim.nav_default_pop_enter_anim, 121 | R.anim.nav_default_pop_exit_anim 122 | ) 123 | .attach(selectedFragment) 124 | .setPrimaryNavigationFragment(selectedFragment) 125 | .apply { 126 | // Detach all other Fragments 127 | graphIdToTagMap.forEach { _, fragmentTagIter -> 128 | if (fragmentTagIter != newlySelectedItemTag) { 129 | detach(fragmentManager.findFragmentByTag(firstFragmentTag)!!) 130 | } 131 | } 132 | } 133 | .addToBackStack(firstFragmentTag) 134 | .setReorderingAllowed(true) 135 | .commit() 136 | } 137 | selectedItemTag = newlySelectedItemTag 138 | isOnFirstFragment = selectedItemTag == firstFragmentTag 139 | selectedNavController.value = selectedFragment.navController 140 | true 141 | } else { 142 | false 143 | } 144 | } 145 | } 146 | 147 | // Optional: on item reselected, pop back stack to the destination of the graph 148 | setupItemReselected(graphIdToTagMap, fragmentManager) 149 | 150 | // Handle deep link 151 | setupDeepLinks(navGraphIds, fragmentManager, containerId, intent) 152 | 153 | // Finally, ensure that we update our BottomNavigationView when the back stack changes 154 | fragmentManager.addOnBackStackChangedListener { 155 | if (!isOnFirstFragment && !fragmentManager.isOnBackStack(firstFragmentTag)) { 156 | this.selectedItemId = firstFragmentGraphId 157 | } 158 | 159 | // Reset the graph if the currentDestination is not valid (happens when the back 160 | // stack is popped after using the back button). 161 | selectedNavController.value?.let { controller -> 162 | if (controller.currentDestination == null) { 163 | controller.navigate(controller.graph.id) 164 | } 165 | } 166 | } 167 | return selectedNavController 168 | } 169 | 170 | private fun BottomNavigationView.setupDeepLinks( 171 | navGraphIds: List, 172 | fragmentManager: FragmentManager, 173 | containerId: Int, 174 | intent: Intent 175 | ) { 176 | navGraphIds.forEachIndexed { index, navGraphId -> 177 | val fragmentTag = 178 | getFragmentTag( 179 | index 180 | ) 181 | 182 | // Find or create the Navigation host fragment 183 | val navHostFragment = 184 | obtainNavHostFragment( 185 | fragmentManager, 186 | fragmentTag, 187 | navGraphId, 188 | containerId 189 | ) 190 | // Handle Intent 191 | if (navHostFragment.navController.handleDeepLink(intent) 192 | && selectedItemId != navHostFragment.navController.graph.id) { 193 | this.selectedItemId = navHostFragment.navController.graph.id 194 | } 195 | } 196 | } 197 | 198 | private fun BottomNavigationView.setupItemReselected( 199 | graphIdToTagMap: SparseArray, 200 | fragmentManager: FragmentManager 201 | ) { 202 | setOnNavigationItemReselectedListener { item -> 203 | val newlySelectedItemTag = graphIdToTagMap[item.itemId] 204 | val selectedFragment = fragmentManager.findFragmentByTag(newlySelectedItemTag) 205 | as NavHostFragment 206 | val navController = selectedFragment.navController 207 | // Pop the back stack to the start destination of the current navController graph 208 | navController.popBackStack( 209 | navController.graph.startDestination, false 210 | ) 211 | } 212 | } 213 | 214 | private fun detachNavHostFragment( 215 | fragmentManager: FragmentManager, 216 | navHostFragment: NavHostFragment 217 | ) { 218 | fragmentManager.beginTransaction() 219 | .detach(navHostFragment) 220 | .commitNow() 221 | } 222 | 223 | private fun attachNavHostFragment( 224 | fragmentManager: FragmentManager, 225 | navHostFragment: NavHostFragment, 226 | isPrimaryNavFragment: Boolean 227 | ) { 228 | fragmentManager.beginTransaction() 229 | .attach(navHostFragment) 230 | .apply { 231 | if (isPrimaryNavFragment) { 232 | setPrimaryNavigationFragment(navHostFragment) 233 | } 234 | } 235 | .commitNow() 236 | 237 | } 238 | 239 | private fun obtainNavHostFragment( 240 | fragmentManager: FragmentManager, 241 | fragmentTag: String, 242 | navGraphId: Int, 243 | containerId: Int 244 | ): NavHostFragment { 245 | // If the Nav Host fragment exists, return it 246 | val existingFragment = fragmentManager.findFragmentByTag(fragmentTag) as NavHostFragment? 247 | existingFragment?.let { return it } 248 | 249 | // Otherwise, create it and return it. 250 | val navHostFragment = NavHostFragment.create(navGraphId) 251 | fragmentManager.beginTransaction() 252 | .add(containerId, navHostFragment, fragmentTag) 253 | .commitNow() 254 | return navHostFragment 255 | } 256 | 257 | private fun FragmentManager.isOnBackStack(backStackName: String): Boolean { 258 | val backStackCount = backStackEntryCount 259 | for (index in 0 until backStackCount) { 260 | if (getBackStackEntryAt(index).name == backStackName) { 261 | return true 262 | } 263 | } 264 | return false 265 | } 266 | 267 | private fun getFragmentTag(index: Int) = "bottomNavigation#$index" 268 | -------------------------------------------------------------------------------- /app/src/main/java/com/javadsh98/mjpersiondictionary/ui/main/fragment/FragmentFactoryImpl.kt: -------------------------------------------------------------------------------- 1 | package com.javadsh98.mjpersiondictionary.ui.main.fragment 2 | 3 | import androidx.fragment.app.Fragment 4 | import androidx.fragment.app.FragmentFactory 5 | import com.javadsh98.mjpersiondictionary.ui.main.fragment.History.HistoryFragment 6 | import com.javadsh98.mjpersiondictionary.ui.main.fragment.favorite.FavoriteFragment 7 | import com.javadsh98.mjpersiondictionary.ui.main.fragment.home.HomeFragment 8 | import com.javadsh98.mjpersiondictionary.ui.main.fragment.more.MoreFragment 9 | 10 | class FragmentFactoryImpl : FragmentFactory() { 11 | 12 | override fun instantiate(classLoader: ClassLoader, className: String): Fragment { 13 | return when(className) { 14 | 15 | HomeFragment::class.java.name -> HomeFragment() 16 | FavoriteFragment::class.java.name -> FavoriteFragment() 17 | HistoryFragment::class.java.name -> HistoryFragment() 18 | MoreFragment::class.java.name -> MoreFragment() 19 | 20 | else -> super.instantiate(classLoader, className) 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /app/src/main/java/com/javadsh98/mjpersiondictionary/ui/main/fragment/History/HistoryAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.javadsh98.mjpersiondictionary.ui.main.fragment.History 2 | 3 | import android.view.LayoutInflater 4 | import android.view.View 5 | import android.view.ViewGroup 6 | import androidx.recyclerview.widget.ListAdapter 7 | import androidx.recyclerview.widget.RecyclerView 8 | import com.javadsh98.mjpersiondictionary.R 9 | import com.javadsh98.mjpersiondictionary.data.db.entity.Word 10 | import com.javadsh98.mjpersiondictionary.ui.main.fragment.home.HomeAdapter 11 | import com.javadsh98.mjpersiondictionary.ui.main.fragment.home.onItemClick 12 | import kotlinx.android.synthetic.main.item_normal.view.* 13 | import java.util.* 14 | 15 | class HistoryAdapter(): ListAdapter(HomeAdapter.diff){ 16 | 17 | lateinit var likeListener: onItemClick 18 | 19 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { 20 | return HistoryHolder.create(parent) 21 | } 22 | 23 | override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { 24 | if (holder is HistoryHolder){ 25 | holder.bind(getItem(position)) 26 | } 27 | } 28 | 29 | class HistoryHolder(itemview: View): RecyclerView.ViewHolder(itemview){ 30 | 31 | companion object{ 32 | 33 | fun create(parent: ViewGroup): HistoryHolder{ 34 | val view = LayoutInflater.from(parent.context).inflate(R.layout.item_normal, parent, false) 35 | return HistoryHolder(view) 36 | } 37 | 38 | } 39 | 40 | fun bind(word: Word) { 41 | itemView.textview_allrecycler_persian.text = word.persianWord 42 | itemView.textview_allrecycler_english.text = word.englishWord 43 | itemView.textview_allrecycler_italian.text = word.italianWord 44 | 45 | 46 | //like : 47 | // likeState(word) 48 | // likeOnClickListener(word, likeListener) 49 | 50 | } 51 | // 52 | // private fun likeOnClickListener(word: Word, likeListener : onItemClick) { 53 | // itemView.imageview_detail_like.setOnClickListener { 54 | // word.favorite = !word.favorite 55 | // word.date = Date(System.currentTimeMillis()) 56 | // likeState(word) 57 | // likeListener.invoke(word) 58 | // } 59 | // } 60 | // 61 | // private fun likeState(word: Word) { 62 | // if (word.favorite) 63 | // itemView.imageview_detail_like.setImageResource(R.drawable.ic_all_like_24) 64 | // else 65 | // itemView.imageview_detail_like.setImageResource(R.drawable.ic_all_dislike_24) 66 | // } 67 | 68 | } 69 | 70 | 71 | } 72 | -------------------------------------------------------------------------------- /app/src/main/java/com/javadsh98/mjpersiondictionary/ui/main/fragment/History/HistoryFragment.kt: -------------------------------------------------------------------------------- 1 | package com.javadsh98.mjpersiondictionary.ui.main.fragment.History 2 | 3 | import android.os.Bundle 4 | import android.view.View 5 | import androidx.fragment.app.Fragment 6 | import androidx.lifecycle.Observer 7 | import androidx.lifecycle.ViewModelProvider 8 | import com.javadsh98.mjpersiondictionary.R 9 | import com.javadsh98.mjpersiondictionary.data.db.entity.Word 10 | import kotlinx.android.synthetic.main.fragment_history.* 11 | 12 | /** 13 | * A simple [Fragment] subclass. 14 | */ 15 | class HistoryFragment(): Fragment(R.layout.fragment_history) { 16 | 17 | //ui 18 | lateinit var adapter: HistoryAdapter 19 | 20 | //viewmodel 21 | lateinit var viewModel: HistoryViewModel 22 | 23 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 24 | super.onViewCreated(view, savedInstanceState) 25 | 26 | viewModel = ViewModelProvider(requireActivity()).get(HistoryViewModel::class.java) 27 | 28 | setupViewmodel() 29 | setupRecyclerview() 30 | setupClearHistory() 31 | } 32 | 33 | private fun setupClearHistory() { 34 | button_history_clear_all.setOnClickListener { 35 | viewModel.clearHistories() 36 | } 37 | } 38 | 39 | private fun setupViewmodel() { 40 | 41 | viewModel.getHistory().observe(viewLifecycleOwner, Observer { 42 | adapter.submitList(it) 43 | if (it.isEmpty()){ 44 | showMessage() 45 | }else{ 46 | hideMessage() 47 | } 48 | }) 49 | } 50 | 51 | private fun setupRecyclerview() { 52 | var recyclerview = recyclerview_history_words 53 | adapter = HistoryAdapter() 54 | adapter.likeListener = { 55 | viewModel.update(it) 56 | } 57 | recyclerview.setHasFixedSize(true) 58 | recyclerview.adapter = adapter 59 | } 60 | 61 | private fun showMessage(){ 62 | textview_history_message.visibility = View.VISIBLE 63 | } 64 | 65 | private fun hideMessage(){ 66 | textview_history_message.visibility = View.GONE 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /app/src/main/java/com/javadsh98/mjpersiondictionary/ui/main/fragment/History/HistoryViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.javadsh98.mjpersiondictionary.ui.main.fragment.History 2 | 3 | import android.app.Application 4 | import androidx.lifecycle.AndroidViewModel 5 | import androidx.lifecycle.LiveData 6 | import androidx.lifecycle.viewModelScope 7 | import com.javadsh98.mjpersiondictionary.data.Repository 8 | import com.javadsh98.mjpersiondictionary.data.db.entity.Word 9 | import kotlinx.coroutines.launch 10 | 11 | class HistoryViewModel(application: Application) : AndroidViewModel(application) { 12 | 13 | var repository : Repository 14 | lateinit var allWords: LiveData> 15 | 16 | init { 17 | repository = Repository.getInstance(application, viewModelScope) 18 | viewModelScope.launch { 19 | allWords = repository.getHistories() 20 | } 21 | } 22 | 23 | fun getHistory(): LiveData>{ 24 | return allWords 25 | } 26 | 27 | fun update(word: Word){ 28 | viewModelScope.launch { 29 | repository.update(word) 30 | } 31 | } 32 | 33 | fun clearHistories(){ 34 | viewModelScope.launch { 35 | repository.clearHistories() 36 | } 37 | } 38 | 39 | } -------------------------------------------------------------------------------- /app/src/main/java/com/javadsh98/mjpersiondictionary/ui/main/fragment/detail/DetailFragment.kt: -------------------------------------------------------------------------------- 1 | package com.javadsh98.mjpersiondictionary.ui.main.fragment.detail 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 android.widget.TextView 9 | import androidx.lifecycle.ViewModelProvider 10 | import androidx.navigation.fragment.findNavController 11 | import androidx.navigation.fragment.navArgs 12 | 13 | import com.javadsh98.mjpersiondictionary.R 14 | import com.javadsh98.mjpersiondictionary.data.db.entity.Word 15 | import kotlinx.android.synthetic.main.fragment_detail.* 16 | import java.util.* 17 | 18 | /** 19 | * A simple [Fragment] subclass. 20 | */ 21 | class DetailFragment : Fragment(R.layout.fragment_detail) { 22 | 23 | val args: DetailFragmentArgs by navArgs() 24 | lateinit var word: Word 25 | lateinit var viewModel : DetailViewModel 26 | 27 | override fun onActivityCreated(savedInstanceState: Bundle?) { 28 | super.onActivityCreated(savedInstanceState) 29 | 30 | word = args.word 31 | viewModel = ViewModelProvider(requireActivity()).get(DetailViewModel::class.java) 32 | 33 | setupComponents() 34 | } 35 | 36 | private fun setupComponents() { 37 | 38 | //edittext 39 | edittext_detail_persian.setText(word.persianWord, TextView.BufferType.EDITABLE) 40 | edittext_detail_english.setText(word.englishWord, TextView.BufferType.EDITABLE) 41 | edittext_detail_italian.setText(word.italianWord, TextView.BufferType.EDITABLE) 42 | 43 | //like 44 | word.favorite.also { 45 | setLikeDrawable(it) 46 | } 47 | 48 | setupListener() 49 | 50 | } 51 | 52 | private fun setupListener() { 53 | imageview_detail_like.setOnClickListener { 54 | word.favorite = !word.favorite 55 | setLikeDrawable(word.favorite) 56 | } 57 | 58 | button_detail_submit.setOnClickListener{ 59 | word = word.apply { 60 | englishWord = edittext_detail_english.text.toString() 61 | persianWord = edittext_detail_persian.text.toString() 62 | italianWord = edittext_detail_italian.text.toString() 63 | date = Date(System.currentTimeMillis()) 64 | } 65 | viewModel.update(word) 66 | // findNavController().popBackStack(R.id.homeFragment, false) 67 | requireActivity().onBackPressed() 68 | } 69 | } 70 | fun setLikeDrawable(like: Boolean){ 71 | if (like) 72 | imageview_detail_like.setImageResource(R.drawable.ic_all_like_24) 73 | else 74 | imageview_detail_like.setImageResource(R.drawable.ic_all_dislike_24) 75 | } 76 | } -------------------------------------------------------------------------------- /app/src/main/java/com/javadsh98/mjpersiondictionary/ui/main/fragment/detail/DetailViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.javadsh98.mjpersiondictionary.ui.main.fragment.detail 2 | 3 | import android.app.Application 4 | import androidx.lifecycle.AndroidViewModel 5 | import androidx.lifecycle.ViewModel 6 | import androidx.lifecycle.viewModelScope 7 | import com.javadsh98.mjpersiondictionary.data.Repository 8 | import com.javadsh98.mjpersiondictionary.data.db.entity.Word 9 | import kotlinx.coroutines.launch 10 | 11 | class DetailViewModel(application: Application) : AndroidViewModel(application){ 12 | 13 | val repo: Repository = Repository.getInstance(application, viewModelScope) 14 | 15 | fun update(word: Word){ 16 | viewModelScope.launch { 17 | repo.update(word) 18 | } 19 | } 20 | 21 | } -------------------------------------------------------------------------------- /app/src/main/java/com/javadsh98/mjpersiondictionary/ui/main/fragment/favorite/FavoriteAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.javadsh98.mjpersiondictionary.ui.main.fragment.favorite 2 | 3 | import android.text.TextUtils 4 | import android.view.ViewGroup 5 | import androidx.recyclerview.widget.DiffUtil 6 | import androidx.recyclerview.widget.ListAdapter 7 | import androidx.recyclerview.widget.RecyclerView 8 | import com.javadsh98.mjpersiondictionary.data.db.entity.Word 9 | import com.javadsh98.mjpersiondictionary.ui.main.fragment.home.HomeAdapter 10 | import com.javadsh98.mjpersiondictionary.ui.main.fragment.home.onItemClick 11 | 12 | class FavoriteAdapter() 13 | : ListAdapter(HomeAdapter.diff){ 14 | 15 | lateinit var likeListener: onItemClick 16 | lateinit var onItemClick: onItemClick 17 | 18 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { 19 | return FavoriteHolder.create(parent) 20 | } 21 | 22 | override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { 23 | if (holder is FavoriteHolder) 24 | holder.bind(getItem(position), onItemClick, likeListener) 25 | } 26 | 27 | 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/java/com/javadsh98/mjpersiondictionary/ui/main/fragment/favorite/FavoriteFragment.kt: -------------------------------------------------------------------------------- 1 | package com.javadsh98.mjpersiondictionary.ui.main.fragment.favorite 2 | 3 | import android.os.Bundle 4 | import android.view.View 5 | import androidx.fragment.app.Fragment 6 | import androidx.lifecycle.Observer 7 | import androidx.lifecycle.ViewModelProvider 8 | import androidx.navigation.fragment.findNavController 9 | import com.javadsh98.mjpersiondictionary.R 10 | import com.javadsh98.mjpersiondictionary.data.db.entity.Word 11 | import kotlinx.android.synthetic.main.fragment_favorite.* 12 | 13 | class FavoriteFragment(): Fragment(R.layout.fragment_favorite) { 14 | 15 | lateinit var viewmodel: FavoriteViewModel 16 | lateinit var adapter: FavoriteAdapter 17 | 18 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 19 | super.onViewCreated(view, savedInstanceState) 20 | 21 | viewmodel = ViewModelProvider(requireActivity()).get(FavoriteViewModel::class.java) 22 | 23 | setupRecycler() 24 | searchFavorites() 25 | } 26 | 27 | private fun searchFavorites() { 28 | 29 | viewmodel.searchFavorites().observe(viewLifecycleOwner, Observer { 30 | 31 | adapter.submitList(it) 32 | if (it.isEmpty()) { 33 | showMessage() 34 | } else { 35 | hideMessage() 36 | } 37 | 38 | }) 39 | } 40 | 41 | private fun hideMessage() { 42 | textview_favorite_message.visibility = View.GONE 43 | } 44 | 45 | private fun showMessage() { 46 | textview_favorite_message.visibility = View.VISIBLE 47 | } 48 | 49 | private fun setupRecycler() { 50 | adapter = FavoriteAdapter() 51 | adapter.likeListener = { 52 | viewmodel.update(it) 53 | } 54 | adapter.onItemClick = { 55 | //update view count 56 | ++it.viewCount 57 | viewmodel.update(word = it) 58 | //goto detail fragment 59 | val action = FavoriteFragmentDirections.actionFavoriteFragment2ToDetailFragment3(it, it.englishWord) 60 | findNavController().navigate(action) 61 | } 62 | 63 | recyclerview_favorite_favorites.setHasFixedSize(true) 64 | recyclerview_favorite_favorites.adapter = adapter 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /app/src/main/java/com/javadsh98/mjpersiondictionary/ui/main/fragment/favorite/FavoriteHolder.kt: -------------------------------------------------------------------------------- 1 | package com.javadsh98.mjpersiondictionary.ui.main.fragment.favorite 2 | 3 | import android.view.LayoutInflater 4 | import android.view.View 5 | import android.view.ViewGroup 6 | import androidx.recyclerview.widget.RecyclerView 7 | import com.javadsh98.mjpersiondictionary.R 8 | import com.javadsh98.mjpersiondictionary.data.db.entity.Word 9 | import com.javadsh98.mjpersiondictionary.ui.main.fragment.home.onItemClick 10 | import kotlinx.android.synthetic.main.item_normal.view.* 11 | import java.util.* 12 | 13 | class FavoriteHolder(var itemview: View): RecyclerView.ViewHolder(itemview){ 14 | 15 | companion object{ 16 | 17 | fun create(viewGroup: ViewGroup) : FavoriteHolder{ 18 | val view = LayoutInflater.from(viewGroup.context).inflate(R.layout.item_normal, viewGroup, false) 19 | return FavoriteHolder(view) 20 | } 21 | 22 | } 23 | 24 | fun bind(word: Word, onItemListener: onItemClick, likeListener: onItemClick) { 25 | itemview.textview_allrecycler_persian.text = word.persianWord 26 | itemview.textview_allrecycler_english.text = word.englishWord 27 | itemview.textview_allrecycler_italian.text = word.italianWord 28 | 29 | itemview.setOnClickListener { onItemListener(word) } 30 | 31 | //like : 32 | // likeState(word) 33 | // likeOnClickListener(word, likeListener) 34 | 35 | } 36 | 37 | // private fun likeOnClickListener(word: Word, likeListener: onItemClick) { 38 | // itemview.imageview_detail_like.setOnClickListener { 39 | // word.favorite = !word.favorite 40 | // word.date = Date(System.currentTimeMillis()) 41 | // likeState(word) 42 | // likeListener.invoke(word) 43 | // } 44 | // } 45 | // 46 | // private fun likeState(word: Word) { 47 | // if (word.favorite) 48 | // itemview.imageview_detail_like.setImageResource(R.drawable.ic_all_like_24) 49 | // else 50 | // itemview.imageview_detail_like.setImageResource(R.drawable.ic_all_dislike_24) 51 | // } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /app/src/main/java/com/javadsh98/mjpersiondictionary/ui/main/fragment/favorite/FavoriteViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.javadsh98.mjpersiondictionary.ui.main.fragment.favorite 2 | 3 | import android.app.Application 4 | import androidx.lifecycle.AndroidViewModel 5 | import androidx.lifecycle.LiveData 6 | import androidx.lifecycle.viewModelScope 7 | import com.javadsh98.mjpersiondictionary.data.Repository 8 | import com.javadsh98.mjpersiondictionary.data.db.entity.Word 9 | import kotlinx.coroutines.launch 10 | 11 | class FavoriteViewModel(application: Application): AndroidViewModel(application){ 12 | 13 | private var repository: Repository 14 | private lateinit var allFavorites: LiveData> 15 | 16 | init { 17 | repository = Repository.getInstance(application, viewModelScope) 18 | viewModelScope.launch { 19 | allFavorites = repository.searchFavorites() 20 | } 21 | } 22 | 23 | fun searchFavorites(): LiveData>{ 24 | return allFavorites 25 | } 26 | 27 | fun update(word: Word) { 28 | viewModelScope.launch { 29 | repository.update(word) 30 | } 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /app/src/main/java/com/javadsh98/mjpersiondictionary/ui/main/fragment/home/HomeAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.javadsh98.mjpersiondictionary.ui.main.fragment.home 2 | 3 | import android.text.TextUtils 4 | import android.view.ViewGroup 5 | import androidx.recyclerview.widget.DiffUtil 6 | import androidx.recyclerview.widget.ListAdapter 7 | import androidx.recyclerview.widget.RecyclerView 8 | import com.javadsh98.mjpersiondictionary.data.db.entity.Word 9 | 10 | typealias onItemClick = (Word) -> Unit 11 | 12 | class HomeAdapter () 13 | : ListAdapter(diff){ 14 | 15 | lateinit var itemListener: onItemClick 16 | 17 | private val NORMAL = 1 18 | 19 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NormalHolder { 20 | return NormalHolder.create(parent) 21 | } 22 | 23 | override fun onBindViewHolder(holder:NormalHolder, position: Int) { 24 | holder.bind(getItem(position), itemListener) 25 | 26 | } 27 | 28 | companion object{ 29 | 30 | val diff = object: DiffUtil.ItemCallback(){ 31 | override fun areItemsTheSame(oldItem: Word, newItem: Word): Boolean 32 | = oldItem.id == newItem.id 33 | 34 | override fun areContentsTheSame(oldItem: Word, newItem: Word): Boolean 35 | = TextUtils.equals(oldItem.persianWord, newItem.persianWord) 36 | && TextUtils.equals(oldItem.englishWord, newItem.englishWord) 37 | 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/com/javadsh98/mjpersiondictionary/ui/main/fragment/home/HomeFragment.kt: -------------------------------------------------------------------------------- 1 | package com.javadsh98.mjpersiondictionary.ui.main.fragment.home 2 | 3 | import android.os.Bundle 4 | import android.text.TextUtils 5 | import android.view.View 6 | import android.widget.SearchView 7 | import androidx.fragment.app.Fragment 8 | import androidx.fragment.app.viewModels 9 | import androidx.lifecycle.Observer 10 | import androidx.lifecycle.ViewModelProvider 11 | import androidx.navigation.fragment.findNavController 12 | import com.javadsh98.mjpersiondictionary.R 13 | import kotlinx.android.synthetic.main.fragment_home.* 14 | import java.util.regex.Matcher 15 | import java.util.regex.Pattern 16 | 17 | 18 | /** 19 | * A simple [Fragment] subclass. 20 | */ 21 | class HomeFragment 22 | : Fragment(R.layout.fragment_home), SearchView.OnQueryTextListener { 23 | 24 | val RTL_CHARACTERS: Pattern = 25 | Pattern.compile("[\u0600-\u06FF\u0750-\u077F\u0590-\u05FF\uFE70-\uFEFF]") 26 | 27 | lateinit var viewmodel: HomeViewModel 28 | var adapter: HomeAdapter? = null 29 | 30 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 31 | super.onViewCreated(view, savedInstanceState) 32 | 33 | viewmodel = ViewModelProvider(requireActivity()).get(HomeViewModel::class.java) 34 | 35 | setupSearchView() 36 | setupRecycler() 37 | setupListener() 38 | getPreviousData() 39 | } 40 | 41 | private fun setupSearchView() { 42 | searchview_home_search.setQuery(viewmodel.searchWord, false) 43 | } 44 | 45 | fun getPreviousData() { 46 | 47 | viewmodel.getAllWord().let { 48 | if(it != null) 49 | it.observe(viewLifecycleOwner, Observer { 50 | adapter!!.submitList(it) 51 | 52 | }) 53 | } 54 | 55 | } 56 | 57 | override fun onQueryTextChange(newText: String?): Boolean { 58 | if(TextUtils.isEmpty(newText)) 59 | getDefaultWord() 60 | 61 | return false 62 | } 63 | 64 | override fun onQueryTextSubmit(query: String?): Boolean { 65 | 66 | setSearchword(query!!) 67 | if(isPersian(query)) 68 | searchPersian(query) 69 | else 70 | searchEnglish(query) 71 | searchview_home_search.clearFocus() 72 | 73 | return false 74 | } 75 | 76 | private fun searchPersian(query: String?) { 77 | 78 | viewmodel.searchPersian(query!!).observe(viewLifecycleOwner, Observer { 79 | if(isEqualeSearchWord()) 80 | { 81 | adapter!!.submitList(it) 82 | } 83 | }) 84 | } 85 | 86 | private fun searchEnglish(query: String?) { 87 | 88 | viewmodel.searchEnglish(query!!).observe(viewLifecycleOwner, Observer { 89 | if (isEqualeSearchWord()) 90 | { 91 | adapter!!.submitList(it) 92 | } 93 | }) 94 | } 95 | 96 | private fun setupRecycler() { 97 | 98 | if(adapter == null) 99 | adapter = HomeAdapter() 100 | 101 | adapter!!.itemListener = { 102 | //insert to history 103 | ++it.viewCount 104 | viewmodel.update(it) 105 | //goto detail 106 | val action = HomeFragmentDirections.actionHomeFragment2ToDetailFragment2(it, it.englishWord) 107 | findNavController().navigate(action) 108 | } 109 | 110 | recyclerview_home_words.setHasFixedSize(true) 111 | recyclerview_home_words.adapter = adapter 112 | 113 | var list = viewmodel.allWords.value 114 | if (!list!!.isEmpty()) { 115 | adapter!!.submitList(list) 116 | } else { 117 | getDefaultWord() 118 | } 119 | 120 | } 121 | 122 | private fun setupListener() { 123 | searchview_home_search.setOnQueryTextListener(this) 124 | } 125 | 126 | fun isPersian(str: String): Boolean{ 127 | val matcher: Matcher = RTL_CHARACTERS.matcher(str) 128 | return if (matcher.find()) true else false 129 | } 130 | 131 | fun setSearchword(searchWord: String){ 132 | viewmodel.searchWord = searchWord 133 | } 134 | 135 | fun getSearchWord(): String = viewmodel.searchWord 136 | 137 | fun isEqualeSearchWord(): Boolean{ 138 | return TextUtils.equals(getSearchWord(), searchview_home_search.query) 139 | } 140 | 141 | fun getDefaultWord() { 142 | viewmodel.getDefaultWord().observe(viewLifecycleOwner, Observer { 143 | adapter!!.submitList(it) 144 | }) 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /app/src/main/java/com/javadsh98/mjpersiondictionary/ui/main/fragment/home/HomeViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.javadsh98.mjpersiondictionary.ui.main.fragment.home 2 | 3 | import android.app.Application 4 | import android.text.TextUtils 5 | import android.util.Log 6 | import android.widget.Toast 7 | import androidx.arch.core.util.Function 8 | import androidx.lifecycle.* 9 | import androidx.lifecycle.Observer 10 | import com.javadsh98.mjpersiondictionary.data.Repository 11 | import com.javadsh98.mjpersiondictionary.data.db.entity.Word 12 | import kotlinx.coroutines.launch 13 | import java.util.* 14 | 15 | class HomeViewModel(application: Application) : AndroidViewModel(application){ 16 | 17 | var repository : Repository 18 | var allWords: LiveData> = MutableLiveData(listOf()) 19 | var searchWord = "" 20 | var pageNumber : Int = 1 21 | var countNumber: Int = 25 22 | 23 | init { 24 | repository = Repository.getInstance(application, viewModelScope) 25 | } 26 | 27 | fun searchEnglish(english: String) : LiveData>{ 28 | viewModelScope.launch { 29 | allWords = repository.searchEnglish(english) 30 | } 31 | return Transformations.distinctUntilChanged(allWords!!) 32 | } 33 | 34 | fun searchPersian(english: String) : LiveData>{ 35 | viewModelScope.launch { 36 | allWords = repository.searchPersian(english) 37 | } 38 | return Transformations.distinctUntilChanged(allWords!!) 39 | } 40 | 41 | fun update(word: Word){ 42 | viewModelScope.launch { 43 | repository.update(word) 44 | } 45 | } 46 | 47 | fun getAllWord(): LiveData>{ 48 | if (allWords != null) 49 | return Transformations.distinctUntilChanged(allWords!!) 50 | else 51 | return allWords 52 | } 53 | 54 | fun getDefaultWord(): LiveData>{ 55 | viewModelScope.launch { 56 | allWords = repository.getDefaultWord(countNumber * pageNumber) 57 | } 58 | return Transformations.distinctUntilChanged(allWords!!) 59 | } 60 | 61 | fun loadMore(): LiveData>{ 62 | ++pageNumber 63 | return getDefaultWord() 64 | } 65 | 66 | } -------------------------------------------------------------------------------- /app/src/main/java/com/javadsh98/mjpersiondictionary/ui/main/fragment/home/NormalHolder.kt: -------------------------------------------------------------------------------- 1 | package com.javadsh98.mjpersiondictionary.ui.main.fragment.home 2 | 3 | import android.view.LayoutInflater 4 | import android.view.View 5 | import android.view.ViewGroup 6 | import androidx.recyclerview.widget.RecyclerView 7 | import com.javadsh98.mjpersiondictionary.R 8 | import com.javadsh98.mjpersiondictionary.data.db.entity.Word 9 | import kotlinx.android.synthetic.main.item_normal.view.* 10 | import java.util.* 11 | 12 | class NormalHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { 13 | 14 | companion object { 15 | fun create(viewGroup: ViewGroup) : NormalHolder{ 16 | val view = LayoutInflater.from(viewGroup.context).inflate(R.layout.item_normal, viewGroup, false) 17 | return NormalHolder(view) 18 | } 19 | } 20 | 21 | fun bind(word: Word, itemClick: onItemClick){ 22 | itemView.textview_allrecycler_persian.text = word.persianWord 23 | itemView.textview_allrecycler_english.text = word.englishWord 24 | itemView.textview_allrecycler_italian.text = word.italianWord 25 | 26 | 27 | cardviewClickListener(word, itemClick) 28 | 29 | } 30 | 31 | private fun cardviewClickListener(word: Word, itemClick: onItemClick) { 32 | itemView.cardview_allrecycler_item.setOnClickListener{ 33 | itemClick.invoke(word) 34 | } 35 | } 36 | 37 | } -------------------------------------------------------------------------------- /app/src/main/java/com/javadsh98/mjpersiondictionary/ui/main/fragment/more/MoreFragment.kt: -------------------------------------------------------------------------------- 1 | package com.javadsh98.mjpersiondictionary.ui.main.fragment.more 2 | 3 | import android.content.Intent 4 | import android.net.Uri 5 | import android.os.Bundle 6 | import android.view.View 7 | import android.widget.Toast 8 | import androidx.browser.customtabs.CustomTabsIntent 9 | import androidx.core.app.ShareCompat 10 | import androidx.fragment.app.Fragment 11 | import com.javadsh98.mjpersiondictionary.R 12 | import kotlinx.android.synthetic.main.fragment_more.* 13 | 14 | /** 15 | * A simple [Fragment] subclass. 16 | */ 17 | class MoreFragment: Fragment(R.layout.fragment_more) { 18 | 19 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 20 | super.onViewCreated(view, savedInstanceState) 21 | 22 | textview_more_source_code.setOnClickListener { 23 | openViaCustomTab() 24 | } 25 | 26 | textview_more_share_source_code.setOnClickListener { 27 | ShareCompat.IntentBuilder.from(requireActivity()) 28 | .setType("text/plain") 29 | .setChooserTitle(R.string.text_share_via) 30 | .setText("https://github.com/Muhammad-Javad/MVVM-persion-Dictionary") 31 | .startChooser() 32 | } 33 | 34 | textview_more_exit.setOnClickListener { 35 | requireActivity().finish() 36 | } 37 | 38 | } 39 | 40 | fun openViaIntent(){ 41 | var intent = Intent(Intent.ACTION_VIEW) 42 | intent.setData(Uri.parse("https://github.com/Muhammad-Javad/MVVM-persion-Dictionary")) 43 | if(intent.resolveActivity(context!!.packageManager) != null) { 44 | startActivity(intent) 45 | }else{ 46 | Toast.makeText(context, R.string.text_no_internet_explorer, Toast.LENGTH_SHORT).show() 47 | } 48 | } 49 | 50 | fun openViaCustomTab(){ 51 | var customTab = CustomTabsIntent.Builder() 52 | .setToolbarColor(resources.getColor(R.color.colorPrimary)) 53 | .setShowTitle(true) 54 | .build() 55 | try { 56 | customTab.launchUrl(context!!, Uri.parse("https://github.com/Muhammad-Javad/MVVM-persion-Dictionary")) 57 | }catch (e: Exception){ 58 | openViaIntent() 59 | } 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /app/src/main/java/com/javadsh98/mjpersiondictionary/utils/BottomNavigationBehavior.java: -------------------------------------------------------------------------------- 1 | package com.javadsh98.mjpersiondictionary.utils; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.view.Gravity; 6 | import android.view.View; 7 | 8 | import com.google.android.material.bottomnavigation.BottomNavigationView; 9 | import com.google.android.material.snackbar.Snackbar; 10 | 11 | import androidx.annotation.NonNull; 12 | import androidx.annotation.Nullable; 13 | import androidx.coordinatorlayout.widget.CoordinatorLayout; 14 | import androidx.core.view.ViewCompat; 15 | 16 | 17 | public final class BottomNavigationBehavior extends CoordinatorLayout.Behavior { 18 | public BottomNavigationBehavior(@NonNull Context context, @NonNull AttributeSet attrs) { 19 | super(context, attrs); 20 | } 21 | 22 | public boolean layoutDependsOn(@Nullable CoordinatorLayout parent, @NonNull BottomNavigationView child, @Nullable View dependency) { 23 | if (dependency instanceof Snackbar.SnackbarLayout) { 24 | this.updateSnackbar(child, (Snackbar.SnackbarLayout) dependency); 25 | } 26 | 27 | assert parent != null; 28 | assert dependency != null; 29 | return super.layoutDependsOn(parent, child, dependency); 30 | } 31 | 32 | public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull BottomNavigationView child, @NonNull View directTargetChild, @NonNull View target, int axes, int type) { 33 | return axes == ViewCompat.SCROLL_AXIS_VERTICAL; 34 | } 35 | 36 | public void onNestedPreScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull BottomNavigationView child, @NonNull View target, int dx, int dy, @NonNull int[] consumed, int type) { 37 | super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type); 38 | child.setTranslationY(Math.max(0.0f, Math.min(child.getHeight(), child.getTranslationY() + dy))); 39 | } 40 | 41 | private void updateSnackbar(BottomNavigationView child, Snackbar.SnackbarLayout snackbarLayout) { 42 | if (snackbarLayout.getLayoutParams() instanceof CoordinatorLayout.LayoutParams) { 43 | android.view.ViewGroup.LayoutParams layoutParams = snackbarLayout.getLayoutParams(); 44 | if (layoutParams == null) { 45 | throw new RuntimeException("null cannot be cast to non-null type android.support.design.widget.CoordinatorLayout.LayoutParams"); 46 | } 47 | 48 | CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) layoutParams; 49 | params.setAnchorId(child.getId()); 50 | params.anchorGravity = Gravity.TOP; 51 | params.gravity = Gravity.TOP; 52 | snackbarLayout.setLayoutParams(params); 53 | } 54 | 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /app/src/main/java/com/javadsh98/mjpersiondictionary/utils/Utils.kt: -------------------------------------------------------------------------------- 1 | package com.javadsh98.mjpersiondictionary.utils 2 | 3 | import android.content.Context 4 | import android.util.DisplayMetrics 5 | 6 | 7 | class Utils{ 8 | 9 | companion object{ 10 | fun getScreenHeight(context: Context):Float{ 11 | val displayMetrics: DisplayMetrics = context.getResources().getDisplayMetrics() 12 | val dpHeight = displayMetrics.heightPixels / displayMetrics.density 13 | return dpHeight 14 | } 15 | } 16 | 17 | } -------------------------------------------------------------------------------- /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/back_all_corner.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_all_dislike_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_all_like_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /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/ic_menu_favorite_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_menu_history_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_menu_home_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_menu_more_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/font/regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Muhammad-Javad/MVVM-persian-Dictionary/235432a74227d02a78390b6763368e82d188cf29/app/src/main/res/font/regular.ttf -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 13 | 14 | 21 | 22 | 23 | 24 | 28 | 29 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 55 | -------------------------------------------------------------------------------- /app/src/main/res/layout/dialog_detail.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | 18 | 26 | 27 | 39 | 40 | 52 | 53 | 65 | 66 |