coming up: adapting the layouts and VM, plugging in the API, maybe set up notificationsmain
parent
e064615a9d
commit
b4f6e61b77
@ -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)
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -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<Definition>
|
||||
|
||||
@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)
|
||||
}
|
@ -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<EntryWithDefinitions>
|
||||
|
||||
@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)
|
||||
}
|
@ -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<List<Definition>>
|
||||
|
||||
@Query("SELECT * FROM entries WHERE id = :id")
|
||||
fun query(id: Long): LiveData<Definition>
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun insert(entry: Definition)
|
||||
|
||||
@Update(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun update(entry: Definition)
|
||||
|
||||
@Delete
|
||||
suspend fun delete(entry: Definition)
|
||||
}
|
@ -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<List<Definition>> {
|
||||
Log.i(TAG, "GET ALL DEFINITIONS")
|
||||
return definitionDao.queryAll()
|
||||
}
|
||||
|
||||
fun findById(definitionId: Long): LiveData<Definition> {
|
||||
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)
|
||||
}
|
||||
}
|
@ -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<List<EntryWithDefinitions>>
|
||||
|
||||
@Query("SELECT * FROM entries WHERE id = :id")
|
||||
fun query(id: Long): LiveData<EntryWithDefinitions>
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun insert(entry: EntryWithDefinitions)
|
||||
|
||||
@Update
|
||||
suspend fun update(entry: EntryWithDefinitions)
|
||||
|
||||
@Delete
|
||||
suspend fun delete(entry: EntryWithDefinitions)
|
||||
}
|
@ -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<List<EntryWithDefinitions>> {
|
||||
Log.i(TAG, "GET ALL ENTRIES")
|
||||
return entryDao.queryAll()
|
||||
}
|
||||
|
||||
fun findById(entryId: Long): LiveData<EntryWithDefinitions> {
|
||||
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)
|
||||
}
|
||||
}
|
@ -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<Entry> = 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 ?
|
||||
}*/
|
@ -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()
|
||||
}
|
@ -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<FrameLayout>(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()
|
||||
}
|
||||
}
|
||||
}
|
@ -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<EntryPagerViewModel>()
|
||||
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<LinearLayout>(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()
|
||||
}
|
||||
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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<EntryListViewModel>()
|
||||
|
||||
|
||||
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
|
||||
}
|
||||
}
|
@ -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<EntryWithDefinitions>()
|
||||
|
||||
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<EntryWithDefinitions>) {
|
||||
this.entryList = entryList
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
@ -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<EntryWithDefinitions, EntryRecyclerViewAdapter.EntryViewHolder>(
|
||||
DiffUtilEntryCallback
|
||||
) {
|
||||
|
||||
private object DiffUtilEntryCallback : DiffUtil.ItemCallback<EntryWithDefinitions>() {
|
||||
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)
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package fr.uca.iut.urbandictionarylight.ui.utils
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
inline fun <VM : ViewModel> viewModelFactory(crossinline f: () -> VM) = object : ViewModelProvider.Factory {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T = f() as T
|
||||
}
|
@ -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) }
|
||||
|
||||
}
|
@ -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
|
||||
}
|
@ -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) }
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
package fr.uca.iut.urbandictionarylight.utils
|
||||
|
||||
public enum class Sorting {
|
||||
ASC, DESC
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#727272"
|
||||
android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
|
||||
</vector>
|
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"/>
|
||||
</vector>
|
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M9,16.2L4.8,12l-1.4,1.4L9,19 21,7l-1.4,-1.4L9,16.2z"/>
|
||||
</vector>
|
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:baselineAligned="false"
|
||||
android:orientation="horizontal"
|
||||
android:showDividers="middle"
|
||||
android:divider="?android:attr/dividerVertical">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/container_fragment"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="3" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/container_fragment_detail"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="7" />
|
||||
|
||||
</LinearLayout>
|
@ -1,82 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".MainActivity">
|
||||
|
||||
<SearchView
|
||||
android:id="@+id/search_view"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="32dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:background="#EEE"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recycler_view"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:clipToPadding="false"
|
||||
android:padding="16dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/search_view" />
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/add_definition_btn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:clickable="true"
|
||||
android:contentDescription="@string/add_def_btn"
|
||||
android:focusable="true"
|
||||
app:backgroundTint="#55752F"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/search_view"
|
||||
app:srcCompat="@android:drawable/ic_input_add" />
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/add_term_btn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="24dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:clickable="true"
|
||||
android:contentDescription="@string/add_term_btn"
|
||||
android:focusable="true"
|
||||
app:backgroundTint="#3E4E83"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/search_view"
|
||||
app:srcCompat="@android:drawable/ic_input_add" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/add_definition_btn_lbl"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:text="@string/add_def_lbl"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/add_definition_btn" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/add_term_btn_lbl"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@string/add_term_lbl"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/add_term_btn" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:id="@+id/pager_layout">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar_activity"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="?attr/colorPrimary"
|
||||
app:elevation="@dimen/toolbar_elevation"
|
||||
app:titleMarginStart="@dimen/icon_title_space" />
|
||||
|
||||
</LinearLayout>
|
@ -0,0 +1,141 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<data>
|
||||
|
||||
<variable
|
||||
name="entryVM"
|
||||
type="fr.uca.iut.urbandictionarylight.ui.viewmodel.EntryViewModel" />
|
||||
</data>
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fillViewport="true">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="@dimen/activity_margin"
|
||||
tools:context=".ui.activity.EntryActivity">
|
||||
|
||||
<androidx.constraintlayout.widget.Barrier
|
||||
android:id="@+id/barrier"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:barrierDirection="right"
|
||||
app:constraint_referenced_ids="text_overview,text_gender,text_measurement,text_aggressiveness,text_misc" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_overview"
|
||||
style="@style/CategoryStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/category_overview"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/edit_dog_name"
|
||||
style="@style/EditorFieldStyle"
|
||||
android:layout_width="0dp"
|
||||
android:hint="@string/hint_dog_name"
|
||||
android:importantForAutofill="no"
|
||||
android:inputType="textCapWords"
|
||||
android:text="@={entryVM.dog.name}"
|
||||
app:layout_constraintBaseline_toBaselineOf="@+id/text_overview"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/barrier" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/edit_dog_breed"
|
||||
style="@style/EditorFieldStyle"
|
||||
android:layout_width="0dp"
|
||||
android:hint="@string/hint_dog_breed"
|
||||
android:importantForAutofill="no"
|
||||
android:inputType="textCapWords"
|
||||
android:text="@={entryVM.dog.breed}"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/barrier"
|
||||
app:layout_constraintTop_toBottomOf="@+id/edit_dog_name" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_gender"
|
||||
style="@style/CategoryStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:text="@string/category_gender"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/edit_dog_breed" />
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/spinner_gender"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/large_space"
|
||||
android:entries="@array/array_gender_options"
|
||||
android:selectedItemPosition="@={Converters.genderToInt(dogVM.dog.gender)}"
|
||||
android:spinnerMode="dropdown"
|
||||
app:layout_constraintStart_toEndOf="@id/barrier"
|
||||
app:layout_constraintTop_toTopOf="@+id/text_gender" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_measurement"
|
||||
style="@style/CategoryStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:text="@string/category_measurement"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/spinner_gender" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/edit_dog_weight"
|
||||
style="@style/EditorFieldStyle"
|
||||
android:layout_width="0dp"
|
||||
android:hint="@string/hint_dog_weight"
|
||||
android:importantForAutofill="no"
|
||||
android:inputType="numberDecimal"
|
||||
android:text="@={Converters.floatToString(dogVM.dog.weight)}"
|
||||
app:layout_constraintBaseline_toBaselineOf="@+id/text_measurement"
|
||||
app:layout_constraintEnd_toStartOf="@id/label_weight_unit"
|
||||
app:layout_constraintStart_toEndOf="@id/barrier" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/label_weight_unit"
|
||||
style="@style/EditorUnitsStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:text="@string/unit_dog_weight"
|
||||
app:layout_constraintBaseline_toBaselineOf="@+id/text_measurement"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/edit_dog_weight" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_aggressiveness"
|
||||
style="@style/CategoryStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:text="@string/category_aggressiveness"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/edit_dog_weight" />
|
||||
|
||||
<RatingBar
|
||||
android:id="@+id/ratingbar_aggressiveness"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:numStars="3"
|
||||
android:rating="@={(float) dogVM.dog.aggressiveness}"
|
||||
android:stepSize="1"
|
||||
app:layout_constraintStart_toEndOf="@id/barrier"
|
||||
app:layout_constraintTop_toTopOf="@+id/text_aggressiveness" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_misc"
|
||||
style="@style/CategoryStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_marginTop="@dimen/large_space"
|
||||
android:text="@string/category_misc"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/ratingbar_aggressiveness" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</ScrollView>
|
||||
</layout>
|
@ -0,0 +1,55 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<data>
|
||||
|
||||
<import type="android.view.View" />
|
||||
|
||||
<variable
|
||||
name="entryListVM"
|
||||
type="fr.uca.iut.urbandictionarylight.ui.viewmodel.EntryListViewModel" />
|
||||
</data>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".ui.activity.EntryListActivity">
|
||||
|
||||
<SearchView
|
||||
android:id="@+id/search_view"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="32dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:background="#EEE"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recycler_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
||||
app:spanCount="2"
|
||||
tools:listitem="@layout/item_list_entry" />
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/add_entry_btn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="24dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:clickable="true"
|
||||
android:contentDescription="@string/add_entry_btn"
|
||||
android:focusable="true"
|
||||
app:backgroundTint="#3E4E83"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/search_view"
|
||||
app:srcCompat="@android:drawable/ic_input_add" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</layout>
|
@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:card_view="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<data>
|
||||
<variable
|
||||
name="entryWithDefinitions"
|
||||
type="fr.uca.iut.urbandictionarylight.model.EntryWithDefinitions" />
|
||||
</data>
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/cardview_entry"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="2dp"
|
||||
card_view:cardUseCompatPadding="true">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_cardview_entry"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="@dimen/activity_margin">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/view_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-medium"
|
||||
android:text="@{entryWithDefinitions.entry.phrase}"
|
||||
android:textAppearance="?android:textAppearanceMedium"
|
||||
android:textColor="@color/black"
|
||||
tools:text="Entry phrase" />
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
</layout>
|
@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
tools:context=".ui.activity.EntryActivity">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar_activity"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="?attr/colorPrimary"
|
||||
android:theme="@style/Theme.UrbanDictionaryLight.AppBarOverlay"
|
||||
app:elevation="@dimen/toolbar_elevation"
|
||||
app:popupTheme="@style/Theme.UrbanDictionaryLight.PopupOverlay"
|
||||
app:titleMarginStart="@dimen/icon_title_space" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/container_fragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
</LinearLayout>
|
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
tools:context=".ui.activity.EntryListActivity">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar_activity"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="?attr/colorPrimary"
|
||||
app:elevation="@dimen/toolbar_elevation"
|
||||
android:theme="@style/Theme.UrbanDictionaryLight.AppBarOverlay"
|
||||
app:popupTheme="@style/Theme.UrbanDictionaryLight.PopupOverlay"
|
||||
app:titleMarginStart="@dimen/icon_title_space" />
|
||||
|
||||
<include layout="@layout/toolbar_md_activity_content" />
|
||||
|
||||
</LinearLayout>
|
@ -0,0 +1,4 @@
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/container_fragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item
|
||||
android:id="@+id/action_save"
|
||||
android:icon="@drawable/ic_done"
|
||||
android:title="@string/save_lbl"
|
||||
app:showAsAction="always"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/action_delete"
|
||||
android:icon="@drawable/ic_delete"
|
||||
android:title="@string/delete_lbl"
|
||||
app:showAsAction="ifRoom"/>
|
||||
</menu>
|
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_item_new_entry"
|
||||
android:icon="@drawable/ic_add"
|
||||
android:title="@string/add_entry_lbl"
|
||||
app:showAsAction="never"/>
|
||||
|
||||
</menu>
|
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Urban Dictionary Light</string>
|
||||
<string name="add_def_lbl">Nouvelle définition</string>
|
||||
<string name="add_def_btn">Cliquez pour ajouter une nouvelle définition</string>
|
||||
<string name="add_entry_lbl">Nouveau terme</string>
|
||||
<string name="add_entry_btn">Cliquez pour ajouter un nouveau terme</string>
|
||||
<string name="save_lbl">Enregistrer</string>
|
||||
<string name="delete_lbl">Supprimer</string>
|
||||
</resources>
|
@ -1,10 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="purple_200">#FFBB86FC</color>
|
||||
<color name="purple_500">#FF6200EE</color>
|
||||
<color name="purple_700">#FF3700B3</color>
|
||||
<color name="teal_200">#FF03DAC5</color>
|
||||
<color name="teal_700">#FF018786</color>
|
||||
<color name="teal_700">#0097A7</color>
|
||||
<color name="teal_900">#006064</color>
|
||||
|
||||
<color name="blue_200">#62A3FF</color>
|
||||
<color name="blue_700">#1675D1</color>
|
||||
<color name="blue_900">#004A9F</color>
|
||||
|
||||
<color name="black">#FF000000</color>
|
||||
<color name="white">#FFFFFFFF</color>
|
||||
</resources>
|
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- Common margin value used throughout the app -->
|
||||
<dimen name="activity_margin">16dp</dimen>
|
||||
<dimen name="text_margin">16dp</dimen>
|
||||
|
||||
<dimen name="toolbar_elevation">4dp</dimen>
|
||||
|
||||
<!-- Common spaces used throughout the app -->
|
||||
<dimen name="large_space">32dp</dimen>
|
||||
<dimen name="medium_space">16dp</dimen>
|
||||
<dimen name="small_space">8dp</dimen>
|
||||
<dimen name="icon_title_space">24dp</dimen>
|
||||
</resources>
|
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<item name="view_pager" type="id" />
|
||||
</resources>
|
@ -1,7 +1,12 @@
|
||||
<resources>
|
||||
<string name="app_name">Urban Dictionary Light</string>
|
||||
|
||||
<string name="add_def_lbl">New definition</string>
|
||||
<string name="add_term_lbl">New term</string>
|
||||
<string name="add_term_btn">Click to add a new term</string>
|
||||
<string name="add_def_btn">Click to add a new definition</string>
|
||||
<string name="add_entry_lbl">New term</string>
|
||||
<string name="add_entry_btn">Click to add a new term</string>
|
||||
<string name="save_lbl">Save</string>
|
||||
<string name="delete_lbl">Delete</string>
|
||||
<string name="create_entry_error_dialog_title" translatable="false">That\'s a woopsie</string>
|
||||
<string name="create_entry_error_message" translatable="false">sorry bud, no can do</string>
|
||||
</resources>
|
Loading…
Reference in new issue