diff --git a/app/build.gradle b/app/build.gradle
index 75fbc7c..79975f1 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -15,7 +15,11 @@ android {
targetSdk 33
versionCode 1
versionName "1.0"
-
+ kapt {
+ arguments {
+ arg("room.schemaLocation", "$projectDir/schemas".toString())
+ }
+ }
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
@@ -36,22 +40,30 @@ android {
}
buildFeatures {
- viewBinding = true
+ dataBinding true
}
}
dependencies {
+
implementation 'androidx.core:core-ktx:1.9.0'
implementation 'androidx.appcompat:appcompat:1.6.0'
implementation 'com.google.android.material:material:1.8.0'
+ implementation "androidx.fragment:fragment-ktx:1.5.5"
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
- testImplementation 'junit:junit:4.13.2'
- androidTestImplementation 'androidx.test.ext:junit:1.1.5'
- androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
+ implementation 'androidx.recyclerview:recyclerview:1.2.1'
+ implementation "androidx.cardview:cardview:1.0.0"
+
+ // Room ORM
def room_version = "2.5.0"
- def kotlin_version = "1.7.21"
- def nav_version = "2.5.3"
implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version"
implementation "androidx.room:room-ktx:$room_version"
+
+ // Coroutines KTX
+ def lifecycle_version = "2.5.1"
+ implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
+ implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
+ implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version"
+
}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 92bed61..e25a657 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -3,6 +3,7 @@
xmlns:tools="http://schemas.android.com/tools">
+ tools:targetApi="33">
+ android:name=".ui.activity.EntryListActivity"
+ android:exported="true"
+ android:launchMode="singleTop">
-
-
-
+
+
\ No newline at end of file
diff --git a/app/src/main/java/fr/uca/iut/urbandictionarylight/MainActivity.kt b/app/src/main/java/fr/uca/iut/urbandictionarylight/MainActivity.kt
deleted file mode 100644
index 16ad1dd..0000000
--- a/app/src/main/java/fr/uca/iut/urbandictionarylight/MainActivity.kt
+++ /dev/null
@@ -1,17 +0,0 @@
-package fr.uca.iut.urbandictionarylight
-
-import android.os.Bundle
-import androidx.appcompat.app.AppCompatActivity
-import fr.uca.iut.urbandictionarylight.data.UrbanDictionaryDatabase
-import fr.uca.iut.urbandictionarylight.databinding.ActivityMainBinding
-
-class MainActivity : AppCompatActivity() {
-
- lateinit var binding: ActivityMainBinding
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- binding = ActivityMainBinding.inflate(layoutInflater)
- setContentView(binding.root)
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/fr/uca/iut/urbandictionarylight/UrbanDictionaryLight.kt b/app/src/main/java/fr/uca/iut/urbandictionarylight/UrbanDictionaryLight.kt
new file mode 100644
index 0000000..cba32fe
--- /dev/null
+++ b/app/src/main/java/fr/uca/iut/urbandictionarylight/UrbanDictionaryLight.kt
@@ -0,0 +1,11 @@
+package fr.uca.iut.urbandictionarylight
+
+import android.app.Application
+import fr.uca.iut.urbandictionarylight.data.UrbanDictionaryDatabase
+
+class UrbanDictionaryLight : Application() {
+ override fun onCreate() {
+ super.onCreate()
+ UrbanDictionaryDatabase.initialize(this)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/fr/uca/iut/urbandictionarylight/data/DefinitionDao.kt b/app/src/main/java/fr/uca/iut/urbandictionarylight/data/DefinitionDao.kt
deleted file mode 100644
index d655bdc..0000000
--- a/app/src/main/java/fr/uca/iut/urbandictionarylight/data/DefinitionDao.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-package fr.uca.iut.urbandictionarylight.data
-
-import androidx.room.*
-import fr.uca.iut.urbandictionarylight.model.Definition
-
-@Dao
-interface DefinitionDao {
- @Query("SELECT * FROM definitions")
- fun queryAll(): Set
-
- @Query("SELECT * FROM entries WHERE id == :id")
- fun query(id: Int): Definition?
-
- @Insert(onConflict = OnConflictStrategy.REPLACE)
- fun insert(entry: Definition)
-
- @Update
- fun update(entry: Definition)
-
- @Delete
- fun delete(entry: Definition)
-}
\ No newline at end of file
diff --git a/app/src/main/java/fr/uca/iut/urbandictionarylight/data/EntryDao.kt b/app/src/main/java/fr/uca/iut/urbandictionarylight/data/EntryDao.kt
deleted file mode 100644
index e134831..0000000
--- a/app/src/main/java/fr/uca/iut/urbandictionarylight/data/EntryDao.kt
+++ /dev/null
@@ -1,23 +0,0 @@
-package fr.uca.iut.urbandictionarylight.data
-
-import androidx.room.*
-import fr.uca.iut.urbandictionarylight.model.EntryWithDefinitions
-
-
-@Dao
-interface EntryDao {
- @Query("SELECT * FROM entries")
- fun queryAll(): List
-
- @Query("SELECT * FROM entries WHERE id == :id")
- fun query(id: Int): EntryWithDefinitions?
-
- @Insert(onConflict = OnConflictStrategy.REPLACE)
- fun insert(entry: EntryWithDefinitions)
-
- @Update
- fun update(entry: EntryWithDefinitions)
-
- @Delete
- fun delete(entry: EntryWithDefinitions)
-}
\ No newline at end of file
diff --git a/app/src/main/java/fr/uca/iut/urbandictionarylight/data/UrbanDictionaryDatabase.kt b/app/src/main/java/fr/uca/iut/urbandictionarylight/data/UrbanDictionaryDatabase.kt
index 5987386..48ce3a2 100644
--- a/app/src/main/java/fr/uca/iut/urbandictionarylight/data/UrbanDictionaryDatabase.kt
+++ b/app/src/main/java/fr/uca/iut/urbandictionarylight/data/UrbanDictionaryDatabase.kt
@@ -4,9 +4,9 @@ import android.app.Application
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
+import fr.uca.iut.urbandictionarylight.data.entry.EntryDao
import fr.uca.iut.urbandictionarylight.model.Definition
import fr.uca.iut.urbandictionarylight.model.Entry
-import fr.uca.iut.urbandictionarylight.model.EntryWithDefinitions
private const val DB_FILE = "u_d_light.db"
@@ -14,7 +14,7 @@ private const val DB_FILE = "u_d_light.db"
abstract class UrbanDictionaryDatabase : RoomDatabase() {
abstract fun entryDao(): EntryDao
- abstract fun definitionDao(): DefinitionDao
+ abstract fun definitionDao(): fr.uca.iut.urbandictionarylight.data.definition.DefinitionDao
companion object {
private lateinit var application: Application
@@ -32,65 +32,19 @@ abstract class UrbanDictionaryDatabase : RoomDatabase() {
UrbanDictionaryDatabase::class.java,
DB_FILE
)
- .allowMainThreadQueries()
.build()
-
- instance?.entryDao()?.let {
- if (it.queryAll().isEmpty()) emptyDatabaseStub(it)
- }
}
}
return instance!!
} else
- throw RuntimeException("the database must be initialized first")
+ throw RuntimeException("must initialize DB first")
}
- //????
@Synchronized
fun initialize(app: Application) {
if (Companion::application.isInitialized)
- throw RuntimeException("the database must not be initialized twice")
-
+ throw RuntimeException("can't initialize DB twice")
application = app
}
-
-
- private fun emptyDatabaseStub(
- entryDao: EntryDao,
- definitionDao: DefinitionDao
- ): () -> Unit = {
-
- val def1 = Definition("bleep", "bleep blap bloop", 1)
- val def2 = Definition("blarp", "bleeeep blap bloop", 7)
- val def3 = Definition("blep", "bleep blap blooop", 4)
- val def4 = Definition("blupo", "bleep blaap bloop", 56)
-
- with(definitionDao) {
- insert(def1)
- insert(def2)
- insert(def3)
- insert(def4)
- }
- with(entryDao) {
- insert(
- EntryWithDefinitions(
- Entry("shrimping"),
- listOf(def1, def4)
- )
- )
- insert(
- EntryWithDefinitions(
- Entry("blunderkegel"),
- listOf(def2)
- )
- )
- insert(
- EntryWithDefinitions(
- Entry("something horrible and dumb"),
- listOf(def3)
- )
- )
- }
- }
}
}
diff --git a/app/src/main/java/fr/uca/iut/urbandictionarylight/data/definition/DefinitionDao.kt b/app/src/main/java/fr/uca/iut/urbandictionarylight/data/definition/DefinitionDao.kt
new file mode 100644
index 0000000..3150267
--- /dev/null
+++ b/app/src/main/java/fr/uca/iut/urbandictionarylight/data/definition/DefinitionDao.kt
@@ -0,0 +1,23 @@
+package fr.uca.iut.urbandictionarylight.data.definition
+
+import androidx.lifecycle.LiveData
+import androidx.room.*
+import fr.uca.iut.urbandictionarylight.model.Definition
+
+@Dao
+interface DefinitionDao {
+ @Query("SELECT * FROM definitions")
+ fun queryAll(): LiveData>
+
+ @Query("SELECT * FROM entries WHERE id = :id")
+ fun query(id: Long): LiveData
+
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ suspend fun insert(entry: Definition)
+
+ @Update(onConflict = OnConflictStrategy.REPLACE)
+ suspend fun update(entry: Definition)
+
+ @Delete
+ suspend fun delete(entry: Definition)
+}
\ No newline at end of file
diff --git a/app/src/main/java/fr/uca/iut/urbandictionarylight/data/definition/DefinitionRepository.kt b/app/src/main/java/fr/uca/iut/urbandictionarylight/data/definition/DefinitionRepository.kt
new file mode 100644
index 0000000..28140bc
--- /dev/null
+++ b/app/src/main/java/fr/uca/iut/urbandictionarylight/data/definition/DefinitionRepository.kt
@@ -0,0 +1,38 @@
+package fr.uca.iut.urbandictionarylight.data.definition
+
+import android.util.Log
+import androidx.lifecycle.LiveData
+import fr.uca.iut.urbandictionarylight.model.Definition
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+
+private const val TAG = "DefinitionRepository"
+
+
+class DefinitionRepository(private val definitionDao: DefinitionDao) {
+
+ fun getAll(): LiveData> {
+ Log.i(TAG, "GET ALL DEFINITIONS")
+ return definitionDao.queryAll()
+ }
+
+ fun findById(definitionId: Long): LiveData {
+ Log.i(TAG, "GET DEFINITION")
+ return definitionDao.query(definitionId)
+ }
+
+ suspend fun insert(definition: Definition) = withContext(Dispatchers.IO) {
+ Log.i(TAG, "POST DEFINITION")
+ definitionDao.insert(definition)
+ }
+
+ suspend fun delete(definition: Definition) = withContext(Dispatchers.IO) {
+ Log.i(TAG, "DELETE DEFINITION")
+ definitionDao.delete(definition)
+ }
+
+ suspend fun update(definition: Definition) = withContext(Dispatchers.IO) {
+ Log.i(TAG, "PUT DEFINITION")
+ definitionDao.update(definition)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/fr/uca/iut/urbandictionarylight/data/entry/EntryDao.kt b/app/src/main/java/fr/uca/iut/urbandictionarylight/data/entry/EntryDao.kt
new file mode 100644
index 0000000..faef797
--- /dev/null
+++ b/app/src/main/java/fr/uca/iut/urbandictionarylight/data/entry/EntryDao.kt
@@ -0,0 +1,24 @@
+package fr.uca.iut.urbandictionarylight.data.entry
+
+import androidx.lifecycle.LiveData
+import androidx.room.*
+import fr.uca.iut.urbandictionarylight.model.EntryWithDefinitions
+
+
+@Dao
+interface EntryDao {
+ @Query("SELECT * FROM entries")
+ fun queryAll(): LiveData>
+
+ @Query("SELECT * FROM entries WHERE id = :id")
+ fun query(id: Long): LiveData
+
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ suspend fun insert(entry: EntryWithDefinitions)
+
+ @Update
+ suspend fun update(entry: EntryWithDefinitions)
+
+ @Delete
+ suspend fun delete(entry: EntryWithDefinitions)
+}
\ No newline at end of file
diff --git a/app/src/main/java/fr/uca/iut/urbandictionarylight/data/entry/EntryRepository.kt b/app/src/main/java/fr/uca/iut/urbandictionarylight/data/entry/EntryRepository.kt
new file mode 100644
index 0000000..1b5bb47
--- /dev/null
+++ b/app/src/main/java/fr/uca/iut/urbandictionarylight/data/entry/EntryRepository.kt
@@ -0,0 +1,37 @@
+package fr.uca.iut.urbandictionarylight.data.entry
+
+import android.util.Log
+import androidx.lifecycle.LiveData
+import fr.uca.iut.urbandictionarylight.model.EntryWithDefinitions
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+
+private const val TAG = "EntryRepository"
+
+class EntryRepository(private val entryDao: EntryDao) {
+
+ fun getAll(): LiveData> {
+ Log.i(TAG, "GET ALL ENTRIES")
+ return entryDao.queryAll()
+ }
+
+ fun findById(entryId: Long): LiveData {
+ Log.i(TAG, "GET ENTRY")
+ return entryDao.query(entryId)
+ }
+
+ suspend fun insert(entry: EntryWithDefinitions) = withContext(Dispatchers.IO) {
+ Log.i(TAG, "POST ENTRY")
+ entryDao.insert(entry)
+ }
+
+ suspend fun delete(entry: EntryWithDefinitions) = withContext(Dispatchers.IO) {
+ Log.i(TAG, "DELETE ENTRY")
+ entryDao.delete(entry)
+ }
+
+ suspend fun update(entry: EntryWithDefinitions) = withContext(Dispatchers.IO) {
+ Log.i(TAG, "PUT ENTRY")
+ entryDao.update(entry)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/fr/uca/iut/urbandictionarylight/model/Dictionary.kt b/app/src/main/java/fr/uca/iut/urbandictionarylight/model/Dictionary.kt
deleted file mode 100644
index 5f72e47..0000000
--- a/app/src/main/java/fr/uca/iut/urbandictionarylight/model/Dictionary.kt
+++ /dev/null
@@ -1,45 +0,0 @@
-// UHM THIS WILL PROBABLY BE THE REPOSITORY ITSELF
-
-/*
-package fr.uca.iut.urbandictionarylight.model
-
-@Entity
-class Dictionary {
- private val entries: MutableSet = mutableSetOf()
-
- fun createEntry(entry: Entry) {
- if (entry.phrase.isNotBlank() && entry.readAllDefinitions().isNotEmpty()) {
- entries.add(entry)
- }
- }
-
- fun readEntry(id: Int): Entry {
- // TODO implement once ORM is in place
- return Entry(phrase = "womp womp -- WIP").also { e -> e.createDefinition(e.readDefinition(-1)) }
- }
-
- fun readAllEntries() = entries.toSet()
-
- fun updateEntry(entry: Entry) {
- // TODO implement once ORM is in place
-// removeEntry(entry.id)
- createEntry(entry)
- }
-
- // TODO throw this away soon
- fun updateEntry(old: Entry, new: Entry) {
- deleteEntry(old)
- createEntry(new)
- }
-
- fun deleteEntry(entry: Entry) {
- entries.remove(entry)
- }
-
- fun deleteEntry(id: Int) {
- // TODO implement once ORM is in place
-// entries.removeIf { e -> id == e.id }
- }
-
- // TODO ? redefine equals and hashset ?
-}*/
diff --git a/app/src/main/java/fr/uca/iut/urbandictionarylight/model/Entry.kt b/app/src/main/java/fr/uca/iut/urbandictionarylight/model/Entry.kt
index 929debf..c4d2d1d 100644
--- a/app/src/main/java/fr/uca/iut/urbandictionarylight/model/Entry.kt
+++ b/app/src/main/java/fr/uca/iut/urbandictionarylight/model/Entry.kt
@@ -7,6 +7,6 @@ const val NEW_ENTRY_ID = 0L
@Entity(tableName = "entries")
data class Entry(
- val phrase: String,
+ val phrase: String = "",
@PrimaryKey(autoGenerate = true) val id: Long = NEW_ENTRY_ID
)
\ No newline at end of file
diff --git a/app/src/main/java/fr/uca/iut/urbandictionarylight/model/EntryWithDefinitions.kt b/app/src/main/java/fr/uca/iut/urbandictionarylight/model/EntryWithDefinitions.kt
index 2611045..bf48de9 100644
--- a/app/src/main/java/fr/uca/iut/urbandictionarylight/model/EntryWithDefinitions.kt
+++ b/app/src/main/java/fr/uca/iut/urbandictionarylight/model/EntryWithDefinitions.kt
@@ -4,9 +4,9 @@ import androidx.room.Embedded
import androidx.room.Relation
data class EntryWithDefinitions(
- @Embedded val entry: Entry,
+ @Embedded val entry: Entry = Entry(),
@Relation(
parentColumn = "id",
entityColumn = "entryId"
- ) val definitions: List
+ ) val definitions: List = listOf()
)
\ No newline at end of file
diff --git a/app/src/main/java/fr/uca/iut/urbandictionarylight/ui/activity/EntryActivity.kt b/app/src/main/java/fr/uca/iut/urbandictionarylight/ui/activity/EntryActivity.kt
new file mode 100644
index 0000000..4436f09
--- /dev/null
+++ b/app/src/main/java/fr/uca/iut/urbandictionarylight/ui/activity/EntryActivity.kt
@@ -0,0 +1,35 @@
+package fr.uca.iut.urbandictionarylight.ui.activity
+
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import fr.uca.iut.urbandictionarylight.R
+import fr.uca.iut.urbandictionarylight.model.NEW_ENTRY_ID
+import fr.uca.iut.urbandictionarylight.ui.fragment.EntryFragment
+
+class EntryActivity : SimpleFragmentActivity(), EntryFragment.OnInteractionListener {
+ companion object {
+ private const val EXTRA_ENTRY_ID =
+ "fr.uca.iut.urbandictionarylight.ui.activity.extra_entry_id"
+
+ fun getIntent(context: Context, entryId: Long) =
+ Intent(context, EntryActivity::class.java).apply {
+ putExtra(EXTRA_ENTRY_ID, entryId)
+ }
+ }
+
+ private var entryId = NEW_ENTRY_ID
+
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ entryId = intent.getLongExtra(EXTRA_ENTRY_ID, NEW_ENTRY_ID)
+ super.onCreate(savedInstanceState)
+ supportActionBar?.setDisplayHomeAsUpEnabled(true)
+ }
+
+
+ override fun createFragment() = EntryFragment.newInstance(entryId)
+ override fun getLayoutResId() = R.layout.toolbar_activity
+ override fun onEntrySaved() = finish()
+ override fun onEntryDeleted() = finish()
+}
\ No newline at end of file
diff --git a/app/src/main/java/fr/uca/iut/urbandictionarylight/ui/activity/EntryListActivity.kt b/app/src/main/java/fr/uca/iut/urbandictionarylight/ui/activity/EntryListActivity.kt
new file mode 100644
index 0000000..5622780
--- /dev/null
+++ b/app/src/main/java/fr/uca/iut/urbandictionarylight/ui/activity/EntryListActivity.kt
@@ -0,0 +1,70 @@
+package fr.uca.iut.urbandictionarylight.ui.activity
+
+import android.os.Bundle
+import android.widget.FrameLayout
+import fr.uca.iut.urbandictionarylight.R
+import fr.uca.iut.urbandictionarylight.model.NEW_ENTRY_ID
+import fr.uca.iut.urbandictionarylight.ui.fragment.EntryFragment
+import fr.uca.iut.urbandictionarylight.ui.fragment.EntryListFragment
+
+class EntryListActivity : SimpleFragmentActivity(),
+ EntryListFragment.OnInteractionListener, EntryFragment.OnInteractionListener {
+
+ private var isTwoPane: Boolean = false
+ private lateinit var masterFragment: EntryListFragment
+
+ override fun createFragment() = EntryListFragment().also { masterFragment = it }
+ override fun getLayoutResId() = R.layout.toolbar_md_activity
+
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ isTwoPane = findViewById(R.id.container_fragment_detail) != null
+ if (savedInstanceState != null)
+ masterFragment = supportFragmentManager.findFragmentById(R.id.container_fragment) as EntryListFragment
+
+ if (!isTwoPane) {
+ removeDisplayedFragment()
+ }
+ }
+
+
+ override fun onEntrySelected(entryId: Long) {
+ if (isTwoPane) {
+ supportFragmentManager.beginTransaction()
+ .replace(R.id.container_fragment_detail, EntryFragment.newInstance(entryId))
+ .commit()
+ } else {
+ startActivity(EntryPagerActivity.getIntent(this, entryId))
+ }
+ }
+
+
+ override fun onAddNewEntry() = startActivity(EntryActivity.getIntent(this, NEW_ENTRY_ID))
+
+
+ override fun onEntrySaved() { }
+
+
+ private fun removeDisplayedFragment() {
+ supportFragmentManager.findFragmentById(R.id.container_fragment_detail)?.let {
+ supportFragmentManager.beginTransaction().remove(it).commit()
+ }
+ }
+
+
+ override fun onEntryDeleted() {
+ if (isTwoPane) {
+ removeDisplayedFragment()
+ } else
+ finish()
+ }
+
+
+ override fun onEntrySwiped() {
+ if (isTwoPane) {
+ removeDisplayedFragment()
+ }
+ }
+}
diff --git a/app/src/main/java/fr/uca/iut/urbandictionarylight/ui/activity/EntryPagerActivity.kt b/app/src/main/java/fr/uca/iut/urbandictionarylight/ui/activity/EntryPagerActivity.kt
new file mode 100644
index 0000000..c259ceb
--- /dev/null
+++ b/app/src/main/java/fr/uca/iut/urbandictionarylight/ui/activity/EntryPagerActivity.kt
@@ -0,0 +1,73 @@
+package fr.uca.iut.urbandictionarylight.ui.activity
+
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import android.widget.LinearLayout
+import androidx.activity.viewModels
+import androidx.appcompat.app.AppCompatActivity
+import androidx.viewpager2.widget.ViewPager2
+import fr.uca.iut.urbandictionarylight.R
+import fr.uca.iut.urbandictionarylight.model.NEW_ENTRY_ID
+import fr.uca.iut.urbandictionarylight.ui.fragment.EntryFragment
+import fr.uca.iut.urbandictionarylight.ui.utils.EntryPagerAdapter
+import fr.uca.iut.urbandictionarylight.ui.viewmodel.EntryPagerViewModel
+
+
+class EntryPagerActivity : AppCompatActivity(), EntryFragment.OnInteractionListener {
+
+ companion object {
+ private const val EXTRA_ENTRY_ID =
+ "fr.uca.iut.urbandictionarylight.ui.activity.extra_entry_id"
+
+ fun getIntent(context: Context, entryId: Long) =
+ Intent(context, EntryPagerActivity::class.java).apply {
+ putExtra(EXTRA_ENTRY_ID, entryId)
+ }
+ }
+
+ private val pagerAdapter = EntryPagerAdapter(this)
+ private val entryPagerVM by viewModels()
+ private lateinit var viewPager: ViewPager2
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_pager)
+
+ setSupportActionBar(findViewById(R.id.toolbar_activity))
+ supportActionBar?.setDisplayHomeAsUpEnabled(true)
+
+ viewPager = ViewPager2(this)
+ viewPager.id = R.id.view_pager
+ findViewById(R.id.pager_layout).addView(viewPager)
+
+ viewPager.adapter = pagerAdapter
+ entryPagerVM.currentEntryId =
+ savedInstanceState?.getLong(EXTRA_ENTRY_ID) ?: intent.getLongExtra(
+ EXTRA_ENTRY_ID,
+ NEW_ENTRY_ID
+ )
+
+ entryPagerVM.entryList.observe(this) {
+ pagerAdapter.submitList(it)
+ var position = pagerAdapter.positionFromId(entryPagerVM.currentEntryId)
+ if (position == -1) position = 0
+ viewPager.currentItem = position
+ }
+
+ viewPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
+ override fun onPageSelected(position: Int) {
+ entryPagerVM.currentEntryId = pagerAdapter.entryIdAt(position)
+ }
+ })
+ }
+
+ override fun onSaveInstanceState(outState: Bundle) {
+ super.onSaveInstanceState(outState)
+ outState.putLong(EXTRA_ENTRY_ID, entryPagerVM.currentEntryId)
+ }
+
+ override fun onEntrySaved() = finish()
+ override fun onEntryDeleted() = finish()
+}
+
diff --git a/app/src/main/java/fr/uca/iut/urbandictionarylight/ui/activity/SimpleFragmentActivity.kt b/app/src/main/java/fr/uca/iut/urbandictionarylight/ui/activity/SimpleFragmentActivity.kt
new file mode 100644
index 0000000..7f97090
--- /dev/null
+++ b/app/src/main/java/fr/uca/iut/urbandictionarylight/ui/activity/SimpleFragmentActivity.kt
@@ -0,0 +1,29 @@
+package fr.uca.iut.urbandictionarylight.ui.activity
+
+import android.os.Bundle
+import androidx.annotation.LayoutRes
+import androidx.appcompat.app.AppCompatActivity
+import androidx.fragment.app.Fragment
+import fr.uca.iut.urbandictionarylight.R
+
+abstract class SimpleFragmentActivity : AppCompatActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ setContentView(getLayoutResId())
+
+ setSupportActionBar(findViewById(R.id.toolbar_activity))
+
+ if (supportFragmentManager.findFragmentById(R.id.container_fragment) == null) {
+ supportFragmentManager.beginTransaction()
+ .add(R.id.container_fragment, createFragment())
+ .commit()
+ }
+ }
+
+ protected abstract fun createFragment(): Fragment
+
+ @LayoutRes
+ protected abstract fun getLayoutResId(): Int
+}
diff --git a/app/src/main/java/fr/uca/iut/urbandictionarylight/ui/fragment/EntryFragment.kt b/app/src/main/java/fr/uca/iut/urbandictionarylight/ui/fragment/EntryFragment.kt
new file mode 100644
index 0000000..b6b51d2
--- /dev/null
+++ b/app/src/main/java/fr/uca/iut/urbandictionarylight/ui/fragment/EntryFragment.kt
@@ -0,0 +1,138 @@
+package fr.uca.iut.urbandictionarylight.ui.fragment
+
+import android.content.Context
+import android.os.Bundle
+import android.view.*
+import androidx.appcompat.app.AlertDialog
+import androidx.core.os.bundleOf
+import androidx.core.view.MenuProvider
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.get
+import androidx.lifecycle.viewmodel.viewModelFactory
+import fr.uca.iut.urbandictionarylight.R
+import fr.uca.iut.urbandictionarylight.databinding.FragmentEntryBinding
+import fr.uca.iut.urbandictionarylight.model.NEW_ENTRY_ID
+import fr.uca.iut.urbandictionarylight.ui.viewmodel.EntryViewModel
+
+class EntryFragment : Fragment() {
+
+ companion object {
+ private const val EXTRA_ENTRY_ID =
+ "fr.uca.iut.urbandictionarylight.ui.activity.extra_entry_id"
+
+ fun newInstance(entryId: Long) = EntryFragment().apply {
+ arguments = bundleOf(EXTRA_ENTRY_ID to entryId)
+ }
+ }
+
+ private var entryId: Long = NEW_ENTRY_ID
+ private lateinit var entryVM: EntryViewModel
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ entryId = savedInstanceState?.getLong(EXTRA_ENTRY_ID) ?: arguments?.getLong(EXTRA_ENTRY_ID)
+ ?: NEW_ENTRY_ID
+
+
+ if (entryId == NEW_ENTRY_ID) {
+ requireActivity().setTitle(R.string.add_entry_lbl)
+ }
+
+ entryVM = ViewModelProvider(this, viewModelFactory { EntryViewModel(entryId) }).get()
+ }
+
+ override fun onSaveInstanceState(outState: Bundle) {
+ super.onSaveInstanceState(outState)
+ outState.putLong(EXTRA_ENTRY_ID, entryId)
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ val binding = FragmentEntryBinding.inflate(inflater)
+ binding.entryVM = entryVM
+ binding.lifecycleOwner = viewLifecycleOwner
+
+ return binding.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ setupMenu()
+ }
+
+ private fun setupMenu() {
+ requireActivity().addMenuProvider(object : MenuProvider {
+ override fun onPrepareMenu(menu: Menu) {
+ super.onPrepareMenu(menu)
+ if (entryId == NEW_ENTRY_ID) {
+ menu.findItem(R.id.action_delete)?.isVisible = false
+ }
+ }
+
+ override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
+ menuInflater.inflate(R.menu.fragment_entry, menu)
+ }
+
+ override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
+ return when (menuItem.itemId) {
+ R.id.action_save -> {
+ saveEntry()
+ true
+ }
+ R.id.action_delete -> {
+ deleteEntry()
+ true
+ }
+ else -> false
+ }
+ }
+
+ }, viewLifecycleOwner, Lifecycle.State.RESUMED)
+ }
+
+ private fun saveEntry() {
+ if (entryVM.saveEntry() == true) {
+ listener?.onEntrySaved()
+ } else {
+ AlertDialog.Builder(requireActivity())
+ .setTitle(R.string.create_entry_error_dialog_title)
+ .setMessage(R.string.create_entry_error_message)
+ .setNeutralButton(android.R.string.ok, null)
+ .show()
+ return
+ }
+ }
+
+ private fun deleteEntry() {
+ if (entryId != NEW_ENTRY_ID) {
+ entryVM.deleteEntry()
+ listener?.onEntryDeleted()
+ }
+ }
+
+ interface OnInteractionListener {
+ fun onEntrySaved()
+ fun onEntryDeleted()
+ }
+
+ private var listener: OnInteractionListener? = null
+
+ override fun onAttach(context: Context) {
+ super.onAttach(context)
+ if (context is OnInteractionListener) {
+ listener = context
+ } else {
+ throw RuntimeException("$context must implement OnInteractionListener")
+ }
+ }
+
+ override fun onDetach() {
+ super.onDetach()
+ listener = null
+ }
+}
diff --git a/app/src/main/java/fr/uca/iut/urbandictionarylight/ui/fragment/EntryListFragment.kt b/app/src/main/java/fr/uca/iut/urbandictionarylight/ui/fragment/EntryListFragment.kt
new file mode 100644
index 0000000..6d87898
--- /dev/null
+++ b/app/src/main/java/fr/uca/iut/urbandictionarylight/ui/fragment/EntryListFragment.kt
@@ -0,0 +1,129 @@
+package fr.uca.iut.urbandictionarylight.ui.fragment
+
+import android.content.Context
+import android.os.Bundle
+import android.view.*
+import androidx.core.view.MenuProvider
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.viewModels
+import androidx.lifecycle.Lifecycle
+import androidx.recyclerview.widget.ItemTouchHelper
+import androidx.recyclerview.widget.RecyclerView
+import fr.uca.iut.urbandictionarylight.R
+import fr.uca.iut.urbandictionarylight.databinding.FragmentListEntryBinding
+import fr.uca.iut.urbandictionarylight.ui.utils.EntryRecyclerViewAdapter
+import fr.uca.iut.urbandictionarylight.ui.viewmodel.EntryListViewModel
+
+
+class EntryListFragment : Fragment(), EntryRecyclerViewAdapter.Callbacks {
+
+ private val entryListAdapter = EntryRecyclerViewAdapter(this)
+ private val entryListVM by viewModels()
+
+
+ override fun onCreateView(
+ inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ val binding = FragmentListEntryBinding.inflate(inflater)
+ binding.entryListVM = entryListVM
+ binding.lifecycleOwner = viewLifecycleOwner
+ with(binding.recyclerView) {
+ adapter = entryListAdapter
+ ItemTouchHelper(EntryListItemTouchHelper()).attachToRecyclerView(this)
+ }
+ binding.addEntryBtn.setOnClickListener { addNewEntry() }
+ return binding.root
+ }
+
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ setupMenu()
+
+ entryListVM.entryList.observe(viewLifecycleOwner) {
+ entryListAdapter.submitList(it)
+ }
+ }
+
+
+ private fun setupMenu() {
+ requireActivity().addMenuProvider(object : MenuProvider {
+ override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
+ menuInflater.inflate(R.menu.fragment_list_entry, menu)
+ }
+
+ override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
+ return when (menuItem.itemId) {
+ R.id.menu_item_new_entry -> {
+ addNewEntry()
+ true
+ }
+ else -> false
+ }
+ }
+ }, viewLifecycleOwner, Lifecycle.State.RESUMED)
+ }
+
+
+ private fun addNewEntry() {
+ listener?.onAddNewEntry()
+ }
+
+
+ override fun onEntrySelected(entryId: Long) {
+ listener?.onEntrySelected(entryId)
+ }
+
+
+ private inner class EntryListItemTouchHelper : ItemTouchHelper.Callback() {
+ override fun isLongPressDragEnabled() = false
+
+ override fun getMovementFlags(
+ recyclerView: RecyclerView,
+ viewHolder: RecyclerView.ViewHolder
+ ) =
+ makeMovementFlags(
+ ItemTouchHelper.UP or ItemTouchHelper.DOWN,
+ ItemTouchHelper.START or ItemTouchHelper.END
+ )
+
+ override fun onMove(
+ recyclerView: RecyclerView,
+ viewHolder: RecyclerView.ViewHolder,
+ target: RecyclerView.ViewHolder
+ ) = false
+
+ override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
+ (viewHolder as EntryRecyclerViewAdapter.EntryViewHolder).entry?.also {
+ entryListVM.delete(it)
+ listener?.onEntrySwiped()
+ }
+ }
+ }
+
+
+ interface OnInteractionListener {
+ fun onEntrySelected(entryId: Long)
+ fun onAddNewEntry()
+ fun onEntrySwiped()
+ }
+
+ private var listener: OnInteractionListener? = null
+
+
+ override fun onAttach(context: Context) {
+ super.onAttach(context)
+ if (context is OnInteractionListener) {
+ listener = context
+ } else {
+ throw RuntimeException("$context must implement OnInteractionListener")
+ }
+ }
+
+
+ override fun onDetach() {
+ super.onDetach()
+ listener = null
+ }
+}
diff --git a/app/src/main/java/fr/uca/iut/urbandictionarylight/ui/utils/EntryPagerAdapter.kt b/app/src/main/java/fr/uca/iut/urbandictionarylight/ui/utils/EntryPagerAdapter.kt
new file mode 100644
index 0000000..f3f671b
--- /dev/null
+++ b/app/src/main/java/fr/uca/iut/urbandictionarylight/ui/utils/EntryPagerAdapter.kt
@@ -0,0 +1,27 @@
+package fr.uca.iut.urbandictionarylight.ui.utils
+
+import androidx.fragment.app.FragmentActivity
+import androidx.viewpager2.adapter.FragmentStateAdapter
+import fr.uca.iut.urbandictionarylight.model.EntryWithDefinitions
+import fr.uca.iut.urbandictionarylight.model.NEW_ENTRY_ID
+import fr.uca.iut.urbandictionarylight.ui.fragment.EntryFragment
+
+class EntryPagerAdapter(fragmentActivity: FragmentActivity) :
+ FragmentStateAdapter(fragmentActivity) {
+ private var entryList = listOf()
+
+ override fun getItemCount() = entryList.size
+
+ override fun createFragment(position: Int) =
+ EntryFragment.newInstance(entryList[position].entry.id)
+
+ fun positionFromId(entryId: Long) = entryList.indexOfFirst { it.entry.id == entryId }
+
+ fun entryIdAt(position: Int) =
+ if (entryList.isEmpty()) NEW_ENTRY_ID else entryList[position].entry.id
+
+ fun submitList(entryList: List) {
+ this.entryList = entryList
+ notifyDataSetChanged()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/fr/uca/iut/urbandictionarylight/ui/utils/EntryRecyclerViewAdapter.kt b/app/src/main/java/fr/uca/iut/urbandictionarylight/ui/utils/EntryRecyclerViewAdapter.kt
new file mode 100644
index 0000000..fb1aeb6
--- /dev/null
+++ b/app/src/main/java/fr/uca/iut/urbandictionarylight/ui/utils/EntryRecyclerViewAdapter.kt
@@ -0,0 +1,56 @@
+package fr.uca.iut.urbandictionarylight.ui.utils
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.ListAdapter
+import androidx.recyclerview.widget.RecyclerView
+import fr.uca.iut.urbandictionarylight.databinding.ItemListEntryBinding
+import fr.uca.iut.urbandictionarylight.model.EntryWithDefinitions
+
+class EntryRecyclerViewAdapter(private val listener: Callbacks) :
+ ListAdapter(
+ DiffUtilEntryCallback
+ ) {
+
+ private object DiffUtilEntryCallback : DiffUtil.ItemCallback() {
+ override fun areItemsTheSame(oldItem: EntryWithDefinitions, newItem: EntryWithDefinitions) =
+ oldItem.entry.id == newItem.entry.id
+
+ override fun areContentsTheSame(
+ oldItem: EntryWithDefinitions,
+ newItem: EntryWithDefinitions
+ ) = oldItem == newItem
+ }
+
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
+ EntryViewHolder(
+ ItemListEntryBinding.inflate(LayoutInflater.from(parent.context)), listener
+ )
+
+
+ override fun onBindViewHolder(holder: EntryViewHolder, position: Int) =
+ holder.bind(getItem(position))
+
+
+ class EntryViewHolder(private val binding: ItemListEntryBinding, listener: Callbacks) :
+ RecyclerView.ViewHolder(binding.root) {
+
+ val entry: EntryWithDefinitions? get() = binding.entryWithDefinitions
+
+ init {
+ itemView.setOnClickListener { entry?.let { listener.onEntrySelected(it.entry.id) } }
+ }
+
+ fun bind(entryWithDefinitions: EntryWithDefinitions) {
+ binding.entryWithDefinitions = entryWithDefinitions
+ binding.executePendingBindings()
+ }
+
+ }
+
+ interface Callbacks {
+ fun onEntrySelected(entryId: Long)
+ }
+}
diff --git a/app/src/main/java/fr/uca/iut/urbandictionarylight/ui/utils/Utils.kt b/app/src/main/java/fr/uca/iut/urbandictionarylight/ui/utils/Utils.kt
new file mode 100644
index 0000000..f8202dc
--- /dev/null
+++ b/app/src/main/java/fr/uca/iut/urbandictionarylight/ui/utils/Utils.kt
@@ -0,0 +1,10 @@
+package fr.uca.iut.urbandictionarylight.ui.utils
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+
+
+@Suppress("UNCHECKED_CAST")
+inline fun viewModelFactory(crossinline f: () -> VM) = object : ViewModelProvider.Factory {
+ override fun create(modelClass: Class): T = f() as T
+}
diff --git a/app/src/main/java/fr/uca/iut/urbandictionarylight/ui/viewmodel/EntryListViewModel.kt b/app/src/main/java/fr/uca/iut/urbandictionarylight/ui/viewmodel/EntryListViewModel.kt
new file mode 100644
index 0000000..4b12a59
--- /dev/null
+++ b/app/src/main/java/fr/uca/iut/urbandictionarylight/ui/viewmodel/EntryListViewModel.kt
@@ -0,0 +1,18 @@
+package fr.uca.iut.urbandictionarylight.ui.viewmodel
+
+import androidx.lifecycle.Transformations
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import fr.uca.iut.urbandictionarylight.data.UrbanDictionaryDatabase
+import fr.uca.iut.urbandictionarylight.data.entry.EntryRepository
+import fr.uca.iut.urbandictionarylight.model.EntryWithDefinitions
+import kotlinx.coroutines.launch
+
+class EntryListViewModel : ViewModel() {
+ private val entryRepo = EntryRepository(UrbanDictionaryDatabase.getInstance().entryDao())
+
+ val entryList = entryRepo.getAll()
+
+ fun delete(entry: EntryWithDefinitions) = viewModelScope.launch { entryRepo.delete(entry) }
+
+}
diff --git a/app/src/main/java/fr/uca/iut/urbandictionarylight/ui/viewmodel/EntryPagerViewModel.kt b/app/src/main/java/fr/uca/iut/urbandictionarylight/ui/viewmodel/EntryPagerViewModel.kt
new file mode 100644
index 0000000..2f12347
--- /dev/null
+++ b/app/src/main/java/fr/uca/iut/urbandictionarylight/ui/viewmodel/EntryPagerViewModel.kt
@@ -0,0 +1,12 @@
+package fr.uca.iut.urbandictionarylight.ui.viewmodel
+
+import androidx.lifecycle.ViewModel
+import fr.uca.iut.urbandictionarylight.data.UrbanDictionaryDatabase
+import fr.uca.iut.urbandictionarylight.data.entry.EntryRepository
+import fr.uca.iut.urbandictionarylight.model.NEW_ENTRY_ID
+
+class EntryPagerViewModel : ViewModel() {
+ private val entryRepo = EntryRepository(UrbanDictionaryDatabase.getInstance().entryDao())
+ val entryList = entryRepo.getAll()
+ var currentEntryId = NEW_ENTRY_ID
+}
\ No newline at end of file
diff --git a/app/src/main/java/fr/uca/iut/urbandictionarylight/ui/viewmodel/EntryViewModel.kt b/app/src/main/java/fr/uca/iut/urbandictionarylight/ui/viewmodel/EntryViewModel.kt
new file mode 100644
index 0000000..a10e11c
--- /dev/null
+++ b/app/src/main/java/fr/uca/iut/urbandictionarylight/ui/viewmodel/EntryViewModel.kt
@@ -0,0 +1,35 @@
+package fr.uca.iut.urbandictionarylight.ui.viewmodel
+
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import fr.uca.iut.urbandictionarylight.data.UrbanDictionaryDatabase
+import fr.uca.iut.urbandictionarylight.data.entry.EntryRepository
+import fr.uca.iut.urbandictionarylight.model.EntryWithDefinitions
+import fr.uca.iut.urbandictionarylight.model.NEW_ENTRY_ID
+import kotlinx.coroutines.launch
+
+
+class EntryViewModel(entryId: Long) : ViewModel() {
+ private val entryRepo = EntryRepository(UrbanDictionaryDatabase.getInstance().entryDao())
+
+ val entry =
+ if (entryId == NEW_ENTRY_ID) MutableLiveData(EntryWithDefinitions()) else entryRepo.findById(
+ entryId
+ )
+
+ fun saveEntry() = entry.value?.let {
+ if (it.entry.phrase.isBlank() || it.definitions.isEmpty())
+ false
+ else {
+ viewModelScope.launch {
+ if (it.entry.id == NEW_ENTRY_ID) entryRepo.insert(it) else entryRepo.update(it)
+ }
+ true
+ }
+ }
+
+ fun deleteEntry() = viewModelScope.launch {
+ entry.value?.let { if (it.entry.id != NEW_ENTRY_ID) entryRepo.delete(it) }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/fr/uca/iut/urbandictionarylight/utils/Sorting.kt b/app/src/main/java/fr/uca/iut/urbandictionarylight/utils/Sorting.kt
deleted file mode 100644
index 4699a98..0000000
--- a/app/src/main/java/fr/uca/iut/urbandictionarylight/utils/Sorting.kt
+++ /dev/null
@@ -1,5 +0,0 @@
-package fr.uca.iut.urbandictionarylight.utils
-
-public enum class Sorting {
- ASC, DESC
-}
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_add.xml b/app/src/main/res/drawable/ic_add.xml
new file mode 100644
index 0000000..a07919d
--- /dev/null
+++ b/app/src/main/res/drawable/ic_add.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_delete.xml b/app/src/main/res/drawable/ic_delete.xml
new file mode 100644
index 0000000..f9213d2
--- /dev/null
+++ b/app/src/main/res/drawable/ic_delete.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_done.xml b/app/src/main/res/drawable/ic_done.xml
new file mode 100644
index 0000000..99caef9
--- /dev/null
+++ b/app/src/main/res/drawable/ic_done.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/layout-land/toolbar_md_activity_content.xml b/app/src/main/res/layout-land/toolbar_md_activity_content.xml
new file mode 100644
index 0000000..db8cb86
--- /dev/null
+++ b/app/src/main/res/layout-land/toolbar_md_activity_content.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
deleted file mode 100644
index 05d671d..0000000
--- a/app/src/main/res/layout/activity_main.xml
+++ /dev/null
@@ -1,82 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_pager.xml b/app/src/main/res/layout/activity_pager.xml
new file mode 100644
index 0000000..4e2eb16
--- /dev/null
+++ b/app/src/main/res/layout/activity_pager.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_entry.xml b/app/src/main/res/layout/fragment_entry.xml
new file mode 100644
index 0000000..9fe8d21
--- /dev/null
+++ b/app/src/main/res/layout/fragment_entry.xml
@@ -0,0 +1,141 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_list_entry.xml b/app/src/main/res/layout/fragment_list_entry.xml
new file mode 100644
index 0000000..56b6ee1
--- /dev/null
+++ b/app/src/main/res/layout/fragment_list_entry.xml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_list_entry.xml b/app/src/main/res/layout/item_list_entry.xml
new file mode 100644
index 0000000..8c86529
--- /dev/null
+++ b/app/src/main/res/layout/item_list_entry.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/toolbar_activity.xml b/app/src/main/res/layout/toolbar_activity.xml
new file mode 100644
index 0000000..cd00391
--- /dev/null
+++ b/app/src/main/res/layout/toolbar_activity.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/toolbar_md_activity.xml b/app/src/main/res/layout/toolbar_md_activity.xml
new file mode 100644
index 0000000..fa9e6ce
--- /dev/null
+++ b/app/src/main/res/layout/toolbar_md_activity.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/toolbar_md_activity_content.xml b/app/src/main/res/layout/toolbar_md_activity_content.xml
new file mode 100644
index 0000000..59d3295
--- /dev/null
+++ b/app/src/main/res/layout/toolbar_md_activity_content.xml
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/fragment_entry.xml b/app/src/main/res/menu/fragment_entry.xml
new file mode 100644
index 0000000..12879b2
--- /dev/null
+++ b/app/src/main/res/menu/fragment_entry.xml
@@ -0,0 +1,15 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/fragment_list_entry.xml b/app/src/main/res/menu/fragment_list_entry.xml
new file mode 100644
index 0000000..95aa0db
--- /dev/null
+++ b/app/src/main/res/menu/fragment_list_entry.xml
@@ -0,0 +1,11 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
new file mode 100644
index 0000000..fd83c8d
--- /dev/null
+++ b/app/src/main/res/values-fr/strings.xml
@@ -0,0 +1,10 @@
+
+
+ Urban Dictionary Light
+ Nouvelle définition
+ Cliquez pour ajouter une nouvelle définition
+ Nouveau terme
+ Cliquez pour ajouter un nouveau terme
+ Enregistrer
+ Supprimer
+
\ No newline at end of file
diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml
index beea633..d0008f8 100644
--- a/app/src/main/res/values-night/themes.xml
+++ b/app/src/main/res/values-night/themes.xml
@@ -2,12 +2,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file