diff --git a/.gitignore b/.gitignore
index bd39932..badd818 100644
--- a/.gitignore
+++ b/.gitignore
@@ -42,6 +42,8 @@ captures/
.idea/libraries
.idea/caches
.idea/sonarlint
+.idea/markdown-navigator.xml
+.idea/markdown-navigator
# Keystore files
# Uncomment the following line if you do not want to check your keystore files in.
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
index 30aa626..4edc5b9 100644
--- a/.idea/codeStyles/Project.xml
+++ b/.idea/codeStyles/Project.xml
@@ -1,29 +1,232 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ GETTERS_AND_SETTERS
+ KEEP
+
+
+ OVERRIDDEN_METHODS
+ KEEP
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ xmlns:android
+ ^$
+
+
+
+
+
+
+
+
+ xmlns:.*
+ ^$
+
+
+ BY_NAME
+
+
+
+
+
+
+ .*:id
+ http://schemas.android.com/apk/res/android
+
+
+
+
+
+
+
+
+ .*:name
+ http://schemas.android.com/apk/res/android
+
+
+
+
+
+
+
+
+ name
+ ^$
+
+
+
+
+
+
+
+
+ style
+ ^$
+
+
+
+
+
+
+
+
+ .*
+ ^$
+
+
+ BY_NAME
+
+
+
+
+
+
+ .*
+ http://schemas.android.com/apk/res/android
+
+
+ ANDROID_ATTRIBUTE_ORDER
+
+
+
+
+
+
+ .*
+ .*
+
+
+ BY_NAME
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml
new file mode 100644
index 0000000..79ee123
--- /dev/null
+++ b/.idea/codeStyles/codeStyleConfig.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
new file mode 100644
index 0000000..15a15b2
--- /dev/null
+++ b/.idea/encodings.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index 109feca..4c2eb3a 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -12,8 +12,9 @@ android {
applicationId "app.marcdev.earworm"
minSdkVersion 23
targetSdkVersion 28
- versionCode 12
- versionName "1.0.0"
+ versionCode 15
+ versionName "1.1.0"
+ setProperty("archivesBaseName", "Earworm-v$versionName")
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
@@ -30,14 +31,23 @@ android {
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
- implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
- implementation 'androidx.appcompat:appcompat:1.0.2'
+ // Kotlin
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.1.1'
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.1.1'
+
+ // AndroidX
+ implementation 'androidx.appcompat:appcompat:1.1.0-alpha02'
+ implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha5'
implementation 'androidx.preference:preference:1.0.0'
- implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha2'
implementation 'com.google.android.material:material:1.0.0'
+ implementation "androidx.lifecycle:lifecycle-extensions:2.2.0-alpha01"
+ implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0-alpha01"
+
+ // Testing
testImplementation 'junit:junit:4.12'
- androidTestImplementation 'androidx.test:runner:1.1.0'
- androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0'
+ androidTestImplementation 'androidx.test:runner:1.2.0'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
// Timber for logging
implementation('com.jakewharton.timber:timber:4.7.0')
@@ -47,14 +57,14 @@ dependencies {
kapt 'androidx.room:room-compiler:2.0.0'
androidTestImplementation 'androidx.room:room-testing:2.0.0'
- // Kotlin co-routines for asynchronous code
- implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.0.0'
- implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.0.0'
+ // Kodein
+ implementation 'org.kodein.di:kodein-di-generic-jvm:6.0.1'
+ implementation 'org.kodein.di:kodein-di-framework-android-x:6.0.1'
// Glide for image loading and caching
- implementation 'com.github.bumptech.glide:glide:4.8.0'
+ implementation 'com.github.bumptech.glide:glide:4.9.0'
kapt 'com.github.bumptech.glide:compiler:4.8.0'
// Android File Picker for choosing an image
- implementation 'com.droidninja:filepicker:2.2.0'
+ implementation 'com.droidninja:filepicker:2.2.1'
}
diff --git a/app/src/androidTest/java/app/marcdev/earworm/database/DAOTest.kt b/app/src/androidTest/java/app/marcdev/earworm/data/database/DAOTest.kt
similarity index 88%
rename from app/src/androidTest/java/app/marcdev/earworm/database/DAOTest.kt
rename to app/src/androidTest/java/app/marcdev/earworm/data/database/DAOTest.kt
index f7311c2..1c09362 100644
--- a/app/src/androidTest/java/app/marcdev/earworm/database/DAOTest.kt
+++ b/app/src/androidTest/java/app/marcdev/earworm/data/database/DAOTest.kt
@@ -1,4 +1,4 @@
-package app.marcdev.earworm.database
+package app.marcdev.earworm.data.database
import androidx.room.Room
import androidx.test.platform.app.InstrumentationRegistry
@@ -10,7 +10,7 @@ import org.junit.Test
class DAOTest {
- private var database: AppDatabase? = null
+ private var database: ProductionAppDatabase? = null
private var dao: DAO? = null
// Default values
@@ -27,7 +27,7 @@ class DAOTest {
@Before
fun setUp() {
database =
- Room.inMemoryDatabaseBuilder(InstrumentationRegistry.getInstrumentation().context, AppDatabase::class.java)
+ Room.inMemoryDatabaseBuilder(InstrumentationRegistry.getInstrumentation().context, ProductionAppDatabase::class.java)
.allowMainThreadQueries().build()
dao = database!!.dao()
}
@@ -48,7 +48,7 @@ class DAOTest {
val returnedItemsWhenNothingInserted: MutableList = database?.dao()!!.getAllItems()
Assert.assertEquals(0, returnedItemsWhenNothingInserted.size)
- database?.dao()!!.insertOrUpdateItem(testItem)
+ database?.dao()!!.insertItem(testItem)
val returnedItemsWhenOneInserted: MutableList = database?.dao()!!.getAllItems()
Assert.assertEquals(1, returnedItemsWhenOneInserted.size)
@@ -62,8 +62,8 @@ class DAOTest {
val returnedItemsWhenNothingInserted: MutableList = database?.dao()!!.getAllItems()
Assert.assertEquals(0, returnedItemsWhenNothingInserted.size)
- database?.dao()!!.insertOrUpdateItem(testItem1)
- database?.dao()!!.insertOrUpdateItem(testItem2)
+ database?.dao()!!.insertItem(testItem1)
+ database?.dao()!!.insertItem(testItem2)
val returnedItemsWhenOneInserted: MutableList = database?.dao()!!.getAllItems()
Assert.assertEquals(2, returnedItemsWhenOneInserted.size)
@@ -79,7 +79,7 @@ class DAOTest {
val returnedItemsWhenNothingInserted: MutableList = database?.dao()!!.getItemById(testId)
Assert.assertEquals(0, returnedItemsWhenNothingInserted.size)
- database?.dao()!!.insertOrUpdateItem(testItem)
+ database?.dao()!!.insertItem(testItem)
val returnedItemsWhenOneInserted: MutableList = database?.dao()!!.getItemById(testId)
Assert.assertEquals(1, returnedItemsWhenOneInserted.size)
@@ -103,8 +103,8 @@ class DAOTest {
val returnedItemsWhenNothingInserted: MutableList = database?.dao()!!.getAllItems()
Assert.assertEquals(0, returnedItemsWhenNothingInserted.size)
- database?.dao()!!.insertOrUpdateItem(testItem1)
- database?.dao()!!.insertOrUpdateItem(testItem2)
+ database?.dao()!!.insertItem(testItem1)
+ database?.dao()!!.insertItem(testItem2)
val returnedItemsWhenTwoInserted: MutableList = database?.dao()!!.getAllItems()
Assert.assertEquals(2, returnedItemsWhenTwoInserted.size)
@@ -131,8 +131,8 @@ class DAOTest {
val returnedItemsWhenNothingInserted: MutableList = database?.dao()!!.getAllItems()
Assert.assertEquals(0, returnedItemsWhenNothingInserted.size)
- database?.dao()!!.insertOrUpdateItem(testItem1)
- database?.dao()!!.insertOrUpdateItem(testItem2)
+ database?.dao()!!.insertItem(testItem1)
+ database?.dao()!!.insertItem(testItem2)
val returnedAllItemsWhenTwoInserted: MutableList = database?.dao()!!.getAllItems()
Assert.assertEquals(2, returnedAllItemsWhenTwoInserted.size)
@@ -155,9 +155,9 @@ class DAOTest {
val testItem3 = createTestItem()
testItem3.imageName = testImage2
- database?.dao()!!.insertOrUpdateItem(testItem1)
- database?.dao()!!.insertOrUpdateItem(testItem2)
- database?.dao()!!.insertOrUpdateItem(testItem3)
+ database?.dao()!!.insertItem(testItem1)
+ database?.dao()!!.insertItem(testItem2)
+ database?.dao()!!.insertItem(testItem3)
val returnedValueWhenSearchedForTestImage1: Int = database?.dao()!!.getNumberOfEntriesUsingImage(testImage1)
Assert.assertEquals(1, returnedValueWhenSearchedForTestImage1)
@@ -186,9 +186,9 @@ class DAOTest {
testItem3.imageName = testImage2
testItem3.id = testId3
- database?.dao()!!.insertOrUpdateItem(testItem1)
- database?.dao()!!.insertOrUpdateItem(testItem2)
- database?.dao()!!.insertOrUpdateItem(testItem3)
+ database?.dao()!!.insertItem(testItem1)
+ database?.dao()!!.insertItem(testItem2)
+ database?.dao()!!.insertItem(testItem3)
val returnedValueWhenSearchedForTestImage1: Int = database?.dao()!!.getNumberOfEntriesUsingImage(testImage1)
Assert.assertEquals(1, returnedValueWhenSearchedForTestImage1)
@@ -225,9 +225,9 @@ class DAOTest {
testItem3.imageName = testImage2
testItem3.id = testId3
- database?.dao()!!.insertOrUpdateItem(testItem1)
- database?.dao()!!.insertOrUpdateItem(testItem2)
- database?.dao()!!.insertOrUpdateItem(testItem3)
+ database?.dao()!!.insertItem(testItem1)
+ database?.dao()!!.insertItem(testItem2)
+ database?.dao()!!.insertItem(testItem3)
val returnedValueWhenSearchedForTestImage1: Int = database?.dao()!!.getNumberOfEntriesUsingImage(testImage1)
Assert.assertEquals(1, returnedValueWhenSearchedForTestImage1)
diff --git a/app/src/androidTest/java/app/marcdev/earworm/repository/FavouriteItemRepositoryTest.kt b/app/src/androidTest/java/app/marcdev/earworm/data/repository/FavouriteItemRepositoryTest.kt
similarity index 87%
rename from app/src/androidTest/java/app/marcdev/earworm/repository/FavouriteItemRepositoryTest.kt
rename to app/src/androidTest/java/app/marcdev/earworm/data/repository/FavouriteItemRepositoryTest.kt
index d56cfce..beabca3 100644
--- a/app/src/androidTest/java/app/marcdev/earworm/repository/FavouriteItemRepositoryTest.kt
+++ b/app/src/androidTest/java/app/marcdev/earworm/data/repository/FavouriteItemRepositoryTest.kt
@@ -1,10 +1,10 @@
-package app.marcdev.earworm.repository
+package app.marcdev.earworm.data.repository
import androidx.room.Room
import androidx.test.platform.app.InstrumentationRegistry
-import app.marcdev.earworm.database.AppDatabase
-import app.marcdev.earworm.database.DAO
-import app.marcdev.earworm.database.FavouriteItem
+import app.marcdev.earworm.data.database.ProductionAppDatabase
+import app.marcdev.earworm.data.database.DAO
+import app.marcdev.earworm.data.database.FavouriteItem
import app.marcdev.earworm.utils.SONG
import kotlinx.coroutines.runBlocking
import org.junit.After
@@ -14,7 +14,7 @@ import org.junit.Test
class FavouriteItemRepositoryTest {
- private var database: AppDatabase? = null
+ private var database: ProductionAppDatabase? = null
private var repository: FavouriteItemRepository? = null
private var dao: DAO? = null
@@ -32,7 +32,7 @@ class FavouriteItemRepositoryTest {
@Before
fun setUp() {
database =
- Room.inMemoryDatabaseBuilder(InstrumentationRegistry.getInstrumentation().context, AppDatabase::class.java)
+ Room.inMemoryDatabaseBuilder(InstrumentationRegistry.getInstrumentation().context, ProductionAppDatabase::class.java)
.allowMainThreadQueries().build()
dao = database!!.dao()
@@ -56,8 +56,8 @@ class FavouriteItemRepositoryTest {
val returnedItemsWhenNothingInserted: MutableList = repository!!.getAllItems()
Assert.assertEquals(0, returnedItemsWhenNothingInserted.size)
- repository!!.insertOrUpdateItem(testItem1)
- repository!!.insertOrUpdateItem(testItem2)
+ repository!!.addItem(testItem1)
+ repository!!.addItem(testItem2)
val returnedItemsWhenOneInserted: MutableList = repository!!.getAllItems()
Assert.assertEquals(2, returnedItemsWhenOneInserted.size)
@@ -73,7 +73,7 @@ class FavouriteItemRepositoryTest {
val returnedItemsWhenNothingInserted: MutableList = repository!!.getItem(testId)
Assert.assertEquals(0, returnedItemsWhenNothingInserted.size)
- repository!!.insertOrUpdateItem(testItem)
+ repository!!.addItem(testItem)
val returnedItemsWhenOneInserted: MutableList = repository!!.getItem(testId)
Assert.assertEquals(1, returnedItemsWhenOneInserted.size)
@@ -97,8 +97,8 @@ class FavouriteItemRepositoryTest {
val returnedItemsWhenNothingInserted: MutableList = repository!!.getAllItems()
Assert.assertEquals(0, returnedItemsWhenNothingInserted.size)
- repository!!.insertOrUpdateItem(testItem1)
- repository!!.insertOrUpdateItem(testItem2)
+ repository!!.addItem(testItem1)
+ repository!!.addItem(testItem2)
val returnedItemsWhenTwoInserted: MutableList = repository!!.getAllItems()
Assert.assertEquals(2, returnedItemsWhenTwoInserted.size)
@@ -125,8 +125,8 @@ class FavouriteItemRepositoryTest {
val returnedItemsWhenNothingInserted: MutableList = repository!!.getAllItems()
Assert.assertEquals(0, returnedItemsWhenNothingInserted.size)
- repository!!.insertOrUpdateItem(testItem1)
- repository!!.insertOrUpdateItem(testItem2)
+ repository!!.addItem(testItem1)
+ repository!!.addItem(testItem2)
val returnedAllItemsWhenTwoInserted: MutableList = repository!!.getAllItems()
Assert.assertEquals(2, returnedAllItemsWhenTwoInserted.size)
@@ -149,9 +149,9 @@ class FavouriteItemRepositoryTest {
val testItem3 = createTestItem()
testItem3.imageName = testImage2
- repository!!.insertOrUpdateItem(testItem1)
- repository!!.insertOrUpdateItem(testItem2)
- repository!!.insertOrUpdateItem(testItem3)
+ repository!!.addItem(testItem1)
+ repository!!.addItem(testItem2)
+ repository!!.addItem(testItem3)
val returnedValueWhenSearchedForTestImage1: Int = repository!!.countUsesOfImage(testImage1)
Assert.assertEquals(1, returnedValueWhenSearchedForTestImage1)
@@ -180,9 +180,9 @@ class FavouriteItemRepositoryTest {
testItem3.imageName = testImage2
testItem3.id = testId3
- repository!!.insertOrUpdateItem(testItem1)
- repository!!.insertOrUpdateItem(testItem2)
- repository!!.insertOrUpdateItem(testItem3)
+ repository!!.addItem(testItem1)
+ repository!!.addItem(testItem2)
+ repository!!.addItem(testItem3)
val returnedValueWhenSearchedForTestImage1: Int = repository!!.countUsesOfImage(testImage1)
Assert.assertEquals(1, returnedValueWhenSearchedForTestImage1)
@@ -219,9 +219,9 @@ class FavouriteItemRepositoryTest {
testItem3.imageName = testImage2
testItem3.id = testId3
- repository!!.insertOrUpdateItem(testItem1)
- repository!!.insertOrUpdateItem(testItem2)
- repository!!.insertOrUpdateItem(testItem3)
+ repository!!.addItem(testItem1)
+ repository!!.addItem(testItem2)
+ repository!!.addItem(testItem3)
val returnedValueWhenSearchedForTestImage1: Int = repository!!.countUsesOfImage(testImage1)
Assert.assertEquals(1, returnedValueWhenSearchedForTestImage1)
diff --git a/app/src/main/java/app/marcdev/earworm/Earworm.kt b/app/src/main/java/app/marcdev/earworm/Earworm.kt
index ccbdf72..2678b54 100644
--- a/app/src/main/java/app/marcdev/earworm/Earworm.kt
+++ b/app/src/main/java/app/marcdev/earworm/Earworm.kt
@@ -1,13 +1,47 @@
package app.marcdev.earworm
import android.app.Application
+import app.marcdev.earworm.additem.AddItemViewModelFactory
+import app.marcdev.earworm.data.database.AppDatabase
+import app.marcdev.earworm.data.database.DAO
+import app.marcdev.earworm.data.database.ProductionAppDatabase
+import app.marcdev.earworm.data.repository.FavouriteItemRepository
+import app.marcdev.earworm.data.repository.FavouriteItemRepositoryImpl
+import app.marcdev.earworm.mainscreen.MainFragmentViewModelFactory
+import app.marcdev.earworm.utils.FileUtils
+import app.marcdev.earworm.utils.FileUtilsImpl
+import org.kodein.di.Kodein
+import org.kodein.di.KodeinAware
+import org.kodein.di.android.x.androidXModule
+import org.kodein.di.generic.bind
+import org.kodein.di.generic.instance
+import org.kodein.di.generic.provider
+import org.kodein.di.generic.singleton
import timber.log.Timber
-class Earworm : Application() {
+class Earworm : Application(), KodeinAware {
+ override val kodein = Kodein.lazy {
+ import(androidXModule(this@Earworm))
+
+ //
+ bind() with singleton { ProductionAppDatabase.invoke(applicationContext) }
+ bind() with singleton { instance().dao() }
+ bind() with singleton { FavouriteItemRepositoryImpl.getInstance(instance()) }
+ //
+
+ //
+ bind() with provider { FileUtilsImpl(instance()) }
+ //
+
+ //
+ bind() from provider { MainFragmentViewModelFactory(instance(), instance()) }
+ bind() from provider { AddItemViewModelFactory(instance(), instance()) }
+ //
+ }
override fun onCreate() {
super.onCreate()
- if (BuildConfig.DEBUG) {
+ if(BuildConfig.DEBUG) {
Timber.plant(Timber.DebugTree())
Timber.i("Log: Timber Debug Tree planted")
}
diff --git a/app/src/main/java/app/marcdev/earworm/MainActivity.kt b/app/src/main/java/app/marcdev/earworm/MainActivity.kt
index 4a56954..cfff7ab 100644
--- a/app/src/main/java/app/marcdev/earworm/MainActivity.kt
+++ b/app/src/main/java/app/marcdev/earworm/MainActivity.kt
@@ -1,33 +1,28 @@
package app.marcdev.earworm
import android.os.Bundle
-import androidx.appcompat.app.AppCompatActivity
import androidx.coordinatorlayout.widget.CoordinatorLayout
-import app.marcdev.earworm.mainscreen.MainFragmentViewImpl
-import app.marcdev.earworm.utils.DARK_THEME
-import app.marcdev.earworm.utils.LIGHT_THEME
+import app.marcdev.earworm.internal.DARK_THEME
+import app.marcdev.earworm.internal.LIGHT_THEME
+import app.marcdev.earworm.internal.base.EarwormActivity
+import app.marcdev.earworm.mainscreen.MainFragment
import app.marcdev.earworm.utils.getTheme
-import app.marcdev.earworm.utils.setFragment
import timber.log.Timber
-class MainActivity : AppCompatActivity() {
+class MainActivity : EarwormActivity() {
private lateinit var mainFrame: CoordinatorLayout
private var activityTheme: Int = -1
override fun onCreate(savedInstanceState: Bundle?) {
- Timber.d("Log: onCreate: Started")
-
/* Theme changes must be done before super.onCreate otherwise it will be overridden with the value
in the manifest */
- if(getTheme(applicationContext) == DARK_THEME) {
- Timber.v("Log: onCreate: Is dark mode")
+ activityTheme = if(getTheme(applicationContext) == DARK_THEME) {
setTheme(R.style.Earworm_DarkTheme)
- activityTheme = DARK_THEME
+ DARK_THEME
} else {
- Timber.v("Log: onCreate: Is not dark mode")
setTheme(R.style.Earworm_LightTheme)
- activityTheme = LIGHT_THEME
+ LIGHT_THEME
}
super.onCreate(savedInstanceState)
@@ -39,13 +34,11 @@ class MainActivity : AppCompatActivity() {
}
private fun bindViews() {
- Timber.v("Log: bindViews: Started")
this.mainFrame = findViewById(R.id.frame_main)
}
private fun setDefaultFragment() {
- Timber.v("Log: setDefaultFragment: Started")
- val fragment = MainFragmentViewImpl()
+ val fragment = MainFragment()
if(intent.action == "app.marcdev.earworm.intent.ADD_ITEM") {
Timber.d("Log: onCreate: Started from Add Item app shortcut")
@@ -60,7 +53,6 @@ class MainActivity : AppCompatActivity() {
override fun onResume() {
super.onResume()
if(getTheme(applicationContext) != activityTheme) {
- Timber.d("Log: onResume: Theme was changed, recreating activity")
recreate()
}
}
diff --git a/app/src/main/java/app/marcdev/earworm/additem/AddItemBottomSheet.kt b/app/src/main/java/app/marcdev/earworm/additem/AddItemBottomSheet.kt
index 27b8660..1dfe1b2 100644
--- a/app/src/main/java/app/marcdev/earworm/additem/AddItemBottomSheet.kt
+++ b/app/src/main/java/app/marcdev/earworm/additem/AddItemBottomSheet.kt
@@ -2,198 +2,119 @@ package app.marcdev.earworm.additem
import android.Manifest
import android.app.Activity
-import android.app.Dialog
import android.content.Intent
import android.content.pm.PackageManager
-import android.graphics.Color
-import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import android.preference.PreferenceManager
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import android.widget.*
+import android.widget.EditText
+import android.widget.ImageView
+import android.widget.Toast
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
+import androidx.lifecycle.Observer
+import androidx.lifecycle.ViewModelProviders
import app.marcdev.earworm.R
-import app.marcdev.earworm.database.FavouriteItem
-import app.marcdev.earworm.uicomponents.RoundedBottomDialogFragment
-import app.marcdev.earworm.utils.*
+import app.marcdev.earworm.internal.ALBUM
+import app.marcdev.earworm.internal.ARTIST
+import app.marcdev.earworm.internal.PREF_CLEAR_INPUTS
+import app.marcdev.earworm.internal.SONG
+import app.marcdev.earworm.internal.base.EarwormBottomSheetDialogFragment
+import app.marcdev.earworm.uicomponents.AddItemDatePickerDialog
+import app.marcdev.earworm.uicomponents.BinaryOptionDialog
+import app.marcdev.earworm.utils.changeColorOfDrawable
+import app.marcdev.earworm.utils.changeColorOfImageViewDrawable
import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions
import com.google.android.material.button.MaterialButton
import com.google.android.material.chip.Chip
import droidninja.filepicker.FilePickerBuilder
import droidninja.filepicker.FilePickerConst
-import timber.log.Timber
-import java.util.*
+import org.kodein.di.Kodein
+import org.kodein.di.KodeinAware
+import org.kodein.di.android.x.closestKodein
+import org.kodein.di.generic.instance
-class AddItemBottomSheet : RoundedBottomDialogFragment(), AddItemView {
+class AddItemBottomSheet : EarwormBottomSheetDialogFragment(), KodeinAware {
+ override val kodein: Kodein by closestKodein()
+ //
+ private val viewModelFactory: AddItemViewModelFactory by instance()
+ private lateinit var viewModel: AddItemViewModel
+ //
+
+ //
private lateinit var saveButton: MaterialButton
private lateinit var primaryInput: EditText
private lateinit var secondaryInput: EditText
- private lateinit var songButton: ImageButton
- private lateinit var albumButton: ImageButton
- private lateinit var artistButton: ImageButton
- private lateinit var presenter: AddItemPresenter
- private lateinit var datePickerDialog: Dialog
- private lateinit var datePicker: DatePicker
- private lateinit var datePickerOk: MaterialButton
- private lateinit var datePickerCancel: MaterialButton
+ private lateinit var songButton: ImageView
+ private lateinit var albumButton: ImageView
+ private lateinit var artistButton: ImageView
+ private lateinit var datePickerDialog: AddItemDatePickerDialog
private lateinit var iconImageView: ImageView
private lateinit var dateChip: Chip
- private lateinit var confirmDeleteDialog: Dialog
- private val dateChosen = Calendar.getInstance()
- // If the itemID is anything other than -1 then it is in edit mode
- private var itemId: Int = -1
+ private lateinit var confirmImageDeleteDialog: BinaryOptionDialog
+ //
- private var type: Int = 0
- private var recyclerUpdateView: RecyclerUpdateView? = null
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ viewModel = ViewModelProviders.of(this, viewModelFactory).get(AddItemViewModel::class.java)
+ }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.dialog_add_item, container, false)
- presenter = AddItemPresenterImpl(this, activity!!.applicationContext)
bindViews(view)
+ setupObservers()
- arguments?.let {
- Timber.d("Log: onCreateView: Arguments not null")
- this.itemId = arguments!!.getInt("item_id")
- presenter.getItem(itemId)
- } ?: run {
- Timber.d("Log: onCreateView: Arguments null")
- setupDefaults()
+ var itemId = -1
+ if(arguments != null) {
+ itemId = requireArguments().getInt("item_id", -1)
}
- return view
- }
+ viewModel.passArguments(itemId)
- override fun convertToEditMode(item: FavouriteItem) {
- Timber.d("Log: convertToEditMode: Started")
-
- when(item.type) {
- SONG -> {
- activateButton(songButton)
- primaryInput.setText(item.songName)
- secondaryInput.setText(item.artistName)
- }
-
- ALBUM -> {
- activateButton(albumButton)
- primaryInput.setText(item.albumName)
- secondaryInput.setText(item.artistName)
- }
-
- ARTIST -> {
- activateButton(artistButton)
- primaryInput.setText(item.artistName)
- secondaryInput.setText(item.genre)
- }
- }
-
- if(item.imageName.isNotBlank()) {
- presenter.updateFilePath(getArtworkDirectory(requireContext()) + item.imageName)
- }
- updateDateAndDisplay(item.day, item.month, item.year)
- }
-
- fun bindRecyclerUpdateView(view: RecyclerUpdateView) {
- Timber.d("Log: bindRecyclerUpdateView: Started")
- this.recyclerUpdateView = view
+ return view
}
private fun bindViews(view: View) {
- Timber.v("Log: bindViews: Started")
- this.saveButton = view.findViewById(R.id.btn_add_item_save)
- saveButton.setOnClickListener(saveOnClickListener)
-
- this.primaryInput = view.findViewById(R.id.edt_item_primary_input)
- this.secondaryInput = view.findViewById(R.id.edt_item_secondary_input)
-
- this.songButton = view.findViewById(R.id.btn_add_item_song_choice)
- songButton.setOnClickListener(songOnClickListener)
-
- this.albumButton = view.findViewById(R.id.btn_add_item_album_choice)
- albumButton.setOnClickListener(albumOnClickListener)
+ saveButton = view.findViewById(R.id.btn_add_item_save)
+ saveButton.setOnClickListener {
+ viewModel.save(primaryInput.text.toString(), secondaryInput.text.toString())
+ }
- this.artistButton = view.findViewById(R.id.btn_add_item_artist_choice)
- artistButton.setOnClickListener(artistOnClickListener)
+ primaryInput = view.findViewById(R.id.edt_item_primary_input)
+ secondaryInput = view.findViewById(R.id.edt_item_secondary_input)
- this.datePickerDialog = Dialog(this.requireActivity())
- datePickerDialog.setContentView(R.layout.dialog_datepicker)
- datePickerDialog.window!!.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
+ songButton = view.findViewById(R.id.btn_add_item_song_choice)
+ songButton.setOnClickListener { viewModel.setType(SONG) }
- this.datePicker = datePickerDialog.findViewById(R.id.datepicker)
+ albumButton = view.findViewById(R.id.btn_add_item_album_choice)
+ albumButton.setOnClickListener { viewModel.setType(ALBUM) }
- this.datePickerOk = datePickerDialog.findViewById(R.id.btn_datepicker_ok)
- datePickerOk.setOnClickListener(dateOnOkClickListener)
+ artistButton = view.findViewById(R.id.btn_add_item_artist_choice)
+ artistButton.setOnClickListener { viewModel.setType(ARTIST) }
- this.datePickerCancel = datePickerDialog.findViewById(R.id.btn_datepicker_cancel)
- datePickerCancel.setOnClickListener(dateOnCancelClickListener)
+ datePickerDialog = AddItemDatePickerDialog(viewModel.date) { day, month, year ->
+ viewModel.setDate(day, month, year)
+ datePickerDialog.dismiss()
+ }
- this.dateChip = view.findViewById(R.id.chip_add_item_date_display)
- dateChip.setOnClickListener(dateOnClickListener)
+ dateChip = view.findViewById(R.id.chip_add_item_date_display)
+ dateChip.setOnClickListener {
+ datePickerDialog.show(requireFragmentManager(), "Add Item Date Picker Dialog")
+ }
- this.iconImageView = view.findViewById(R.id.img_add_icon)
+ iconImageView = view.findViewById(R.id.img_add_icon)
iconImageView.setOnClickListener(iconOnClickListener)
- iconImageView.setOnLongClickListener(iconOnLongClickListener)
+ iconImageView.setOnLongClickListener { confirmImageDeleteDialog.show(requireFragmentManager(), "Confirm Image Delete Dialog"); true }
// Convert to dark mode if needed
changeColorOfDrawable(requireContext(), iconImageView.drawable, false)
- initEditDialog()
- }
-
- private val saveOnClickListener = View.OnClickListener {
- Timber.d("Log: SaveClick: Clicked")
- if(itemId == -1) {
- Timber.d("Log: saveOnClickListener: Adding new item")
- presenter.addItem(primaryInput.text.toString(), secondaryInput.text.toString(), type, dateChosen, null)
- } else {
- Timber.d("Log: saveOnClickListener: Updating item with id = $itemId")
- presenter.addItem(primaryInput.text.toString(), secondaryInput.text.toString(), type, dateChosen, itemId)
- }
- }
-
- private val dateOnClickListener = View.OnClickListener {
- Timber.d("Log: DateClick: Clicked")
- datePicker.updateDate(dateChosen.get(Calendar.YEAR), dateChosen.get(Calendar.MONTH), dateChosen.get(Calendar.DAY_OF_MONTH))
- datePickerDialog.show()
- }
-
- private val dateOnOkClickListener = View.OnClickListener {
- Timber.d("Log: DateOkClick: Clicked")
-
- val day = datePicker.dayOfMonth
- val monthRaw = datePicker.month
- val year = datePicker.year
-
- updateDateAndDisplay(day, monthRaw, year)
-
- datePickerDialog.dismiss()
- }
-
- private val dateOnCancelClickListener = View.OnClickListener {
- Timber.d("Log: DateCancelClick: Clicked")
- datePickerDialog.dismiss()
- }
-
- private val songOnClickListener = View.OnClickListener {
- Timber.d("Log: SongClick: Clicked")
- activateButtonIfNecessary(songButton)
- }
-
- private val albumOnClickListener = View.OnClickListener {
- Timber.d("Log: AlbumClick: Clicked")
- activateButtonIfNecessary(albumButton)
- }
-
- private val artistOnClickListener = View.OnClickListener {
- Timber.d("Log: ArtistClick: Clicked")
- activateButtonIfNecessary(artistButton)
+ initImageDeleteDialog()
}
private val iconOnClickListener = View.OnClickListener {
- Timber.d("Log: IconClick: Started")
-
if(ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
askForStoragePermissions()
} else {
@@ -204,37 +125,19 @@ class AddItemBottomSheet : RoundedBottomDialogFragment(), AddItemView {
}
}
- private val iconOnLongClickListener = View.OnLongClickListener {
- Timber.d("Log: IconLongClick: Started")
-
- confirmDeleteDialog.show()
- return@OnLongClickListener true
- }
-
- private fun initEditDialog() {
- this.confirmDeleteDialog = Dialog(requireContext())
- confirmDeleteDialog.setContentView(R.layout.dialog_delete_image)
- confirmDeleteDialog.window!!.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
-
- val confirmDeleteButton = confirmDeleteDialog.findViewById(R.id.btn_delete_image_confirm)
- confirmDeleteButton.setOnClickListener(confirmDeleteOnClickListener)
- val cancelButton = confirmDeleteDialog.findViewById(R.id.btn_delete_image_cancel)
- cancelButton.setOnClickListener(cancelDeleteOnClickListener)
- }
-
- private val confirmDeleteOnClickListener = View.OnClickListener {
- Timber.d("Log: ConfirmDelete: Clicked")
- presenter.updateFilePath("")
- confirmDeleteDialog.dismiss()
- }
-
- private val cancelDeleteOnClickListener = View.OnClickListener {
- Timber.d("Log: CancelDelete: Clicked")
- confirmDeleteDialog.dismiss()
+ private fun initImageDeleteDialog() {
+ val dialogBuilder = BinaryOptionDialog.Builder()
+ dialogBuilder
+ .setTitle(resources.getString(R.string.confirm_image_deletion))
+ .setMessageVisible(false)
+ .setPositiveButton(resources.getString(R.string.cancel), {}, true)
+ .setNegativeButton(resources.getString(R.string.delete), {
+ viewModel.removeImage()
+ }, true)
+ confirmImageDeleteDialog = dialogBuilder.build()
}
private fun askForStoragePermissions() {
- Timber.d("Log: askForStoragePermissions: Started")
ActivityCompat.requestPermissions(requireActivity(), arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), 1)
}
@@ -244,66 +147,100 @@ class AddItemBottomSheet : RoundedBottomDialogFragment(), AddItemView {
if(requestCode == FilePickerConst.REQUEST_CODE_PHOTO && resultCode == Activity.RESULT_OK && data != null) {
val photoPathArray = data.getStringArrayListExtra(FilePickerConst.KEY_SELECTED_MEDIA)
val photoPath = photoPathArray[0]
- presenter.updateFilePath(photoPath)
-
- displayImage(photoPath)
+ viewModel.setNewImage(photoPath)
}
}
- private fun setupDefaults() {
- Timber.d("Log: setupDefaults: Started")
- type = SONG
- changeColorOfImageButtonDrawable(activity!!.applicationContext, songButton, true)
- changeColorOfImageButtonDrawable(activity!!.applicationContext, albumButton, false)
- changeColorOfImageButtonDrawable(activity!!.applicationContext, artistButton, false)
- dateChip.text = resources.getString(R.string.today)
- }
+ private fun setupObservers() {
+ viewModel.displayAdded.observe(this, Observer { value ->
+ value?.let { show ->
+ if(show)
+ Toast.makeText(requireActivity(), resources.getString(R.string.item_added), Toast.LENGTH_SHORT).show()
+ }
+ })
- private fun activateButtonIfNecessary(button: ImageButton) {
- Timber.d("Log: activateButtonIfNecessary: Started")
+ viewModel.displayEmpty.observe(this, Observer { value ->
+ value?.let { show ->
+ if(show)
+ Toast.makeText(requireActivity(), resources.getString(R.string.empty), Toast.LENGTH_SHORT).show()
+ }
+ })
- if(type == SONG && button == songButton
- || type == ALBUM && button == albumButton
- || type == ARTIST && button == artistButton
- ) {
- Timber.d("Log: activateButtonIfNecessary: No need to update")
- } else {
- activateButton(button)
- }
- }
+ viewModel.displayError.observe(this, Observer { value ->
+ value?.let { show ->
+ if(show)
+ Toast.makeText(requireActivity(), resources.getString(R.string.error), Toast.LENGTH_SHORT).show()
+ }
+ })
+
+ viewModel.selectedType.observe(this, Observer { value ->
+ value?.let { type ->
+ setTypeSelected(type)
+ }
+ })
+
+ viewModel.dateDisplay.observe(this, Observer { value ->
+ value?.let { dateToDisplay ->
+ if(dateToDisplay.isBlank())
+ dateChip.text = resources.getString(R.string.today)
+ else
+ dateChip.text = dateToDisplay
+ }
+ })
+
+ viewModel.primaryInputDisplay.observe(this, Observer { value ->
+ value?.let { input ->
+ primaryInput.setText(input)
+ primaryInput.setSelection(input.length)
+ }
+ })
- private fun activateButton(button: ImageButton) {
- Timber.d("Log: activateButtonIfNecessary: Activating button $button")
+ viewModel.secondaryInputDisplay.observe(this, Observer { value ->
+ value?.let { input ->
+ secondaryInput.setText(input)
+ }
+ })
+
+ viewModel.displayImage.observe(this, Observer { value ->
+ value?.let { filePath ->
+ displayImage(filePath)
+ }
+ })
+
+ viewModel.dismiss.observe(this, Observer { value ->
+ value?.let { dismiss ->
+ if(dismiss)
+ dismiss()
+ }
+ })
+ }
- when(button) {
- songButton -> {
- changeColorOfImageButtonDrawable(activity!!.applicationContext, songButton, true)
- changeColorOfImageButtonDrawable(activity!!.applicationContext, albumButton, false)
- changeColorOfImageButtonDrawable(activity!!.applicationContext, artistButton, false)
- type = SONG
+ private fun setTypeSelected(type: Int) {
+ when(type) {
+ SONG -> {
+ changeColorOfImageViewDrawable(requireActivity(), songButton, true)
+ changeColorOfImageViewDrawable(requireActivity(), albumButton, false)
+ changeColorOfImageViewDrawable(requireActivity(), artistButton, false)
primaryInput.hint = resources.getString(R.string.song_name)
secondaryInput.hint = resources.getString(R.string.artist)
}
-
- albumButton -> {
- changeColorOfImageButtonDrawable(activity!!.applicationContext, songButton, false)
- changeColorOfImageButtonDrawable(activity!!.applicationContext, albumButton, true)
- changeColorOfImageButtonDrawable(activity!!.applicationContext, artistButton, false)
- type = ALBUM
+ ALBUM -> {
+ changeColorOfImageViewDrawable(requireActivity(), songButton, false)
+ changeColorOfImageViewDrawable(requireActivity(), albumButton, true)
+ changeColorOfImageViewDrawable(requireActivity(), artistButton, false)
primaryInput.hint = resources.getString(R.string.album)
secondaryInput.hint = resources.getString(R.string.artist)
}
-
- artistButton -> {
- changeColorOfImageButtonDrawable(activity!!.applicationContext, songButton, false)
- changeColorOfImageButtonDrawable(activity!!.applicationContext, albumButton, false)
- changeColorOfImageButtonDrawable(activity!!.applicationContext, artistButton, true)
- type = ARTIST
+ ARTIST -> {
+ changeColorOfImageViewDrawable(requireActivity(), songButton, false)
+ changeColorOfImageViewDrawable(requireActivity(), albumButton, false)
+ changeColorOfImageViewDrawable(requireActivity(), artistButton, true)
primaryInput.hint = resources.getString(R.string.artist)
secondaryInput.hint = resources.getString(R.string.genre)
}
}
+ // TODO convert to switch preference that uses boolean
if(PreferenceManager.getDefaultSharedPreferences(requireContext()).getString(PREF_CLEAR_INPUTS, resources.getString(R.string.yes)) == resources.getString(R.string.yes)) {
primaryInput.setText("")
secondaryInput.setText("")
@@ -311,51 +248,7 @@ class AddItemBottomSheet : RoundedBottomDialogFragment(), AddItemView {
}
}
- private fun updateDateAndDisplay(day: Int, month: Int, year: Int) {
- val todayCalendar = Calendar.getInstance()
- val todayDay = todayCalendar.get(Calendar.DAY_OF_MONTH)
- val todayMonthRaw = todayCalendar.get(Calendar.MONTH)
- val todayYear = todayCalendar.get(Calendar.YEAR)
-
- if(day == todayDay
- && month == todayMonthRaw
- && year == todayYear
- ) {
- // Display "Today" on chip
- datePickerDialog.dismiss()
- dateChip.text = resources.getString(R.string.today)
- setDate(todayDay, todayMonthRaw, todayYear)
- } else {
-
- val date = formatDateForDisplay(day, month, year)
- dateChip.text = date
-
- setDate(day, month, year)
- datePickerDialog.dismiss()
- }
- }
-
- private fun setDate(day: Int, month: Int, year: Int) {
- Timber.d("Log: setDate: Started with day = $day, month = $month, year = $year")
- dateChosen.set(Calendar.DAY_OF_MONTH, day)
- dateChosen.set(Calendar.MONTH, month)
- dateChosen.set(Calendar.YEAR, year)
- }
-
- override fun saveCallback() {
- Timber.d("Log: saveCallback: Started")
- if(recyclerUpdateView == null) {
- Timber.e("Log: saveCallback: RecyclerUpdateView is null, cannot update recycler")
- } else {
- recyclerUpdateView!!.fillData()
- }
- Toast.makeText(activity, resources.getString(R.string.item_added), Toast.LENGTH_SHORT).show()
- dismiss()
- }
-
- override fun displayImage(imagePath: String) {
- Timber.d("Log: displayImage: Started with imagePath = $imagePath")
-
+ private fun displayImage(imagePath: String) {
if(imagePath.isBlank()) {
Glide.with(this)
.load(resources.getDrawable(R.drawable.ic_add_a_photo_24px, null))
@@ -370,14 +263,4 @@ class AddItemBottomSheet : RoundedBottomDialogFragment(), AddItemView {
.into(iconImageView)
}
}
-
- override fun displayEmptyToast() {
- Timber.d("Log: displayEmptyToast: Started")
- Toast.makeText(activity, resources.getString(R.string.empty), Toast.LENGTH_SHORT).show()
- }
-
- override fun displayErrorToast() {
- Timber.d("Log: displayErrorToast: Started")
- Toast.makeText(activity, resources.getString(R.string.error), Toast.LENGTH_SHORT).show()
- }
}
\ No newline at end of file
diff --git a/app/src/main/java/app/marcdev/earworm/additem/AddItemModel.kt b/app/src/main/java/app/marcdev/earworm/additem/AddItemModel.kt
deleted file mode 100644
index 28f1c8e..0000000
--- a/app/src/main/java/app/marcdev/earworm/additem/AddItemModel.kt
+++ /dev/null
@@ -1,17 +0,0 @@
-package app.marcdev.earworm.additem
-
-import app.marcdev.earworm.database.FavouriteItem
-import java.io.File
-
-interface AddItemModel {
-
- fun addItemAsync(item: FavouriteItem)
-
- fun getItemAsync(itemId: Int)
-
- fun saveFileToAppStorage(file: File)
-
- fun countUsesOfImage(filePath: String)
-
- fun deleteImage(filePath: String)
-}
\ No newline at end of file
diff --git a/app/src/main/java/app/marcdev/earworm/additem/AddItemModelImpl.kt b/app/src/main/java/app/marcdev/earworm/additem/AddItemModelImpl.kt
deleted file mode 100644
index 75a5600..0000000
--- a/app/src/main/java/app/marcdev/earworm/additem/AddItemModelImpl.kt
+++ /dev/null
@@ -1,95 +0,0 @@
-package app.marcdev.earworm.additem
-
-import android.content.Context
-import app.marcdev.earworm.database.AppDatabase
-import app.marcdev.earworm.database.FavouriteItem
-import app.marcdev.earworm.repository.FavouriteItemRepository
-import app.marcdev.earworm.repository.FavouriteItemRepositoryImpl
-import app.marcdev.earworm.utils.getArtworkDirectory
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.GlobalScope
-import kotlinx.coroutines.async
-import kotlinx.coroutines.launch
-import timber.log.Timber
-import java.io.File
-
-class AddItemModelImpl(private val presenter: AddItemPresenter, private val context: Context) : AddItemModel {
-
- private var repository: FavouriteItemRepository
-
- init {
- val db: AppDatabase = AppDatabase.getDatabase(context)
- repository = FavouriteItemRepositoryImpl(db.dao())
- }
-
- override fun addItemAsync(item: FavouriteItem) {
- Timber.d("Log: addItemAsync: Started")
-
- GlobalScope.launch(Dispatchers.Main) {
- async(Dispatchers.IO) {
- repository.insertOrUpdateItem(item)
- }.await()
-
- presenter.addItemCallback()
- }
- }
-
- override fun getItemAsync(itemId: Int) {
- Timber.d("Log: getItemAsync: Started")
-
- GlobalScope.launch(Dispatchers.Main) {
- val item = async(Dispatchers.IO) {
- repository.getItem(itemId)
- }.await()
-
- presenter.getItemCallback(item)
- }
- }
-
- override fun saveFileToAppStorage(file: File) {
- Timber.d("Log: saveFileToAppStorage: Started")
- val toPath = getArtworkDirectory(context) + file.name
- Timber.d("Log: saveFileToAppStorage: fileName = ${file.name}")
- val toFile = File(toPath)
- if(toFile.compareTo(file) != 0) {
- Timber.d("Log: saveFileToAppStorage: External file, saving")
- try {
- file.copyTo(toFile, true)
- presenter.saveFileToAppStorageCallback(file.name, null)
- } catch(e: NoSuchFileException) {
- presenter.saveFileToAppStorageCallback("", e)
- Timber.e("Log: saveFileToAppStorage: $e")
- }
- } else {
- Timber.d("Log: saveFileToAppStorage: Internal file of same path, not saving")
- presenter.saveFileToAppStorageCallback(file.name, null)
- }
- }
-
- override fun countUsesOfImage(filePath: String) {
- Timber.d("Log: countUsesOfImage: Started with filePath = $filePath")
- val file = File(filePath)
- val fileName = file.name
-
- GlobalScope.launch(Dispatchers.Main) {
- val uses = async(Dispatchers.IO) {
- repository.countUsesOfImage(fileName)
- }.await()
-
- presenter.countUsesOfImageCallback(filePath, uses)
- }
- }
-
- override fun deleteImage(filePath: String) {
- Timber.d("Log: deleteImage: Started with filePath = $filePath")
-
- val file = File(filePath)
- if(file.exists()) {
- Timber.d("Log: deleteImage: File exists, deleting")
- val deletionStatus = file.delete()
- Timber.d("Log: deleteImage: Deletion: $deletionStatus")
- } else {
- Timber.w("Log: deleteImage: File doesn't exist")
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/app/marcdev/earworm/additem/AddItemPresenter.kt b/app/src/main/java/app/marcdev/earworm/additem/AddItemPresenter.kt
deleted file mode 100644
index 3361d06..0000000
--- a/app/src/main/java/app/marcdev/earworm/additem/AddItemPresenter.kt
+++ /dev/null
@@ -1,21 +0,0 @@
-package app.marcdev.earworm.additem
-
-import app.marcdev.earworm.database.FavouriteItem
-import java.util.*
-
-interface AddItemPresenter {
-
- fun addItem(primaryInput: String, secondaryInput: String, type: Int, dateChosen: Calendar, itemId: Int?)
-
- fun addItemCallback()
-
- fun getItem(itemId: Int)
-
- fun getItemCallback(items: MutableList)
-
- fun saveFileToAppStorageCallback(fileName: String, exception: NoSuchFileException?)
-
- fun updateFilePath(filePath: String)
-
- fun countUsesOfImageCallback(filePath: String, uses: Int)
-}
\ No newline at end of file
diff --git a/app/src/main/java/app/marcdev/earworm/additem/AddItemPresenterImpl.kt b/app/src/main/java/app/marcdev/earworm/additem/AddItemPresenterImpl.kt
deleted file mode 100644
index d7ff254..0000000
--- a/app/src/main/java/app/marcdev/earworm/additem/AddItemPresenterImpl.kt
+++ /dev/null
@@ -1,119 +0,0 @@
-package app.marcdev.earworm.additem
-
-import android.content.Context
-import app.marcdev.earworm.database.FavouriteItem
-import app.marcdev.earworm.utils.ALBUM
-import app.marcdev.earworm.utils.ARTIST
-import app.marcdev.earworm.utils.SONG
-import app.marcdev.earworm.utils.getArtworkDirectory
-import timber.log.Timber
-import java.io.File
-import java.util.*
-
-class AddItemPresenterImpl(private val view: AddItemView, private val context: Context) : AddItemPresenter {
-
- private val model: AddItemModel
- private var imageFilePath: String = ""
- private var oldImageFilePath: String = ""
-
- init {
- model = AddItemModelImpl(this, context)
- }
-
- override fun addItem(primaryInput: String, secondaryInput: String, type: Int, dateChosen: Calendar, itemId: Int?) {
- Timber.d("Log: addItem: Started")
-
- if(primaryInput.isBlank() || secondaryInput.isBlank()) {
- Timber.d("Log: addItem: Empty input")
- view.displayEmptyToast()
- } else {
- Timber.d("Log: addItem: Adding item")
- val day = dateChosen.get(Calendar.DAY_OF_MONTH)
- val month = dateChosen.get(Calendar.MONTH)
- val year = dateChosen.get(Calendar.YEAR)
-
- var imageName = ""
-
- if(imageFilePath.isNotBlank()) {
- Timber.d("Log: addItem: imageFilePath = $imageFilePath")
-
- val imageFile = File(imageFilePath)
- imageName = imageFile.name
- model.saveFileToAppStorage(imageFile)
- }
-
- val item: FavouriteItem = when(type) {
- SONG -> FavouriteItem(primaryInput, "", secondaryInput, "", day, month, year, type, imageName)
- ALBUM -> FavouriteItem("", primaryInput, secondaryInput, "", day, month, year, type, imageName)
- ARTIST -> FavouriteItem("", "", primaryInput, secondaryInput, day, month, year, type, imageName)
- else -> FavouriteItem("", "", "", "", 0, 0, 0, type, imageName)
- }
-
- if(itemId != null) {
- Timber.d("Log: addItem: Updating old item with ID = $itemId")
- item.id = itemId
- }
-
- model.addItemAsync(item)
- }
- }
-
- override fun addItemCallback() {
- Timber.d("Log: addItemCallback: Started")
- view.saveCallback()
- }
-
- override fun getItem(itemId: Int) {
- Timber.d("Log: getItem: Started")
- model.getItemAsync(itemId)
- }
-
- override fun getItemCallback(items: MutableList) {
- Timber.d("Log: getItemCallback: Started")
-
- if(items.isNotEmpty()) {
- view.convertToEditMode(items.first())
- if(items.first().imageName.isNotBlank()) {
- view.displayImage(getArtworkDirectory(context) + items.first().imageName)
- }
- } else {
- Timber.e("Log: getItemCallback: Returned empty list")
- }
- }
-
- override fun saveFileToAppStorageCallback(fileName: String, exception: NoSuchFileException?) {
- Timber.d("Log: saveFileToAppStorageCallback: Started")
- if(exception != null) {
- view.displayErrorToast()
- }
- }
-
- override fun updateFilePath(filePath: String) {
- Timber.d("Log: updateFilePath: Started")
-
- this.oldImageFilePath = imageFilePath
- this.imageFilePath = filePath
- Timber.d("Log: updateFilePath: oldImageFilePath = $oldImageFilePath")
- Timber.d("Log: updateFilePath: imageFilePath = $imageFilePath")
-
- if(oldImageFilePath.isNotBlank()) {
- model.countUsesOfImage(oldImageFilePath)
- }
-
- if(imageFilePath.isBlank()) {
- view.displayImage("")
- model.countUsesOfImage(oldImageFilePath)
- }
- }
-
- override fun countUsesOfImageCallback(filePath: String, uses: Int) {
- Timber.d("Log: countUsesOfImageCallback: Started with filePath = $filePath and uses = $uses")
-
- if(uses <= 1) {
- Timber.d("Log: countUsesOfImageCallback: No other uses, deleting image at $filePath")
- model.deleteImage(filePath)
- } else {
- Timber.d("Log: countUsesOfImageCallback: Used elsewhere, not deleting")
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/app/marcdev/earworm/additem/AddItemView.kt b/app/src/main/java/app/marcdev/earworm/additem/AddItemView.kt
deleted file mode 100644
index aa06a9d..0000000
--- a/app/src/main/java/app/marcdev/earworm/additem/AddItemView.kt
+++ /dev/null
@@ -1,16 +0,0 @@
-package app.marcdev.earworm.additem
-
-import app.marcdev.earworm.database.FavouriteItem
-
-interface AddItemView {
-
- fun saveCallback()
-
- fun displayEmptyToast()
-
- fun convertToEditMode(item: FavouriteItem)
-
- fun displayErrorToast()
-
- fun displayImage(imagePath: String)
-}
\ No newline at end of file
diff --git a/app/src/main/java/app/marcdev/earworm/additem/AddItemViewModel.kt b/app/src/main/java/app/marcdev/earworm/additem/AddItemViewModel.kt
new file mode 100644
index 0000000..29e6d62
--- /dev/null
+++ b/app/src/main/java/app/marcdev/earworm/additem/AddItemViewModel.kt
@@ -0,0 +1,206 @@
+package app.marcdev.earworm.additem
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import app.marcdev.earworm.data.database.FavouriteItem
+import app.marcdev.earworm.data.repository.FavouriteItemRepository
+import app.marcdev.earworm.internal.ALBUM
+import app.marcdev.earworm.internal.ARTIST
+import app.marcdev.earworm.internal.SONG
+import app.marcdev.earworm.utils.FileUtils
+import app.marcdev.earworm.utils.formatDateForDisplay
+import kotlinx.coroutines.launch
+import java.io.File
+import java.util.*
+
+class AddItemViewModel(private val repository: FavouriteItemRepository,
+ private val fileUtils: FileUtils)
+ : ViewModel() {
+
+ private var itemId = -1
+ private var _date = Calendar.getInstance()
+ val date: Calendar
+ get() = _date
+
+ private val _displayEmptyError = MutableLiveData()
+ val displayEmpty: LiveData
+ get() = _displayEmptyError
+
+ private val _displayError = MutableLiveData()
+ val displayError: LiveData
+ get() = _displayError
+
+ private val _displayAdded = MutableLiveData()
+ val displayAdded: LiveData
+ get() = _displayAdded
+
+ private val _selectedType = MutableLiveData()
+ val selectedType: LiveData
+ get() = _selectedType
+
+ private val _dateDisplay = MutableLiveData()
+ val dateDisplay: LiveData
+ get() = _dateDisplay
+
+ private val _primaryInputDisplay = MutableLiveData()
+ val primaryInputDisplay: LiveData
+ get() = _primaryInputDisplay
+
+ private val _secondaryInputDisplay = MutableLiveData()
+ val secondaryInputDisplay: LiveData
+ get() = _secondaryInputDisplay
+
+ private var oldImageName = ""
+ private var currentImageName = ""
+ private var newImagePath = ""
+ private val _displayImage = MutableLiveData()
+ val displayImage: LiveData
+ get() = _displayImage
+
+ private val _dismiss = MutableLiveData()
+ val dismiss: LiveData
+ get() = _dismiss
+
+ fun passArguments(itemIdArg: Int) {
+ itemId = itemIdArg
+ if(itemId != -1) {
+ convertToEditMode()
+ } else {
+ setDefaults()
+ }
+ }
+
+ private fun convertToEditMode() {
+ viewModelScope.launch {
+ val item = repository.getItem(itemId)
+ _selectedType.value = item.type
+ setDate(item.day, item.month, item.year)
+ setInputs(item)
+ if(item.imageName.isNotBlank()) {
+ currentImageName = item.imageName
+ _displayImage.value = fileUtils.artworkDirectory + item.imageName
+ }
+ }
+ }
+
+ private fun setDefaults() {
+ _selectedType.value = SONG
+ getDateDisplay()
+ }
+
+ fun save(primaryInput: String, secondaryInput: String) {
+ if(primaryInput.isBlank() || secondaryInput.isBlank()) {
+ _displayEmptyError.value = true
+ } else {
+ val day = _date.get(Calendar.DAY_OF_MONTH)
+ val month = _date.get(Calendar.MONTH)
+ val year = _date.get(Calendar.YEAR)
+
+ val imageNameToSave = if(newImagePath.isBlank())
+ currentImageName
+ else {
+ saveNewImage()
+ File(newImagePath).name
+ }
+
+ if(oldImageName.isNotBlank())
+ deleteOldImageIfNecessary()
+
+ selectedType.value?.let { type ->
+ val item: FavouriteItem = when(type) {
+ SONG -> FavouriteItem(primaryInput, "", secondaryInput, "", day, month, year, type, imageNameToSave)
+ ALBUM -> FavouriteItem("", primaryInput, secondaryInput, "", day, month, year, type, imageNameToSave)
+ ARTIST -> FavouriteItem("", "", primaryInput, secondaryInput, day, month, year, type, imageNameToSave)
+ else -> FavouriteItem("", "", "", "", 0, 0, 0, type, imageNameToSave)
+ }
+
+ if(itemId != -1) {
+ item.id = itemId
+ }
+
+ viewModelScope.launch {
+ repository.addItem(item)
+ _displayAdded.value = true
+ _dismiss.value = true
+ }
+ }
+ }
+ }
+
+ fun setDate(day: Int, month: Int, year: Int) {
+ _date.set(Calendar.DAY_OF_MONTH, day)
+ _date.set(Calendar.MONTH, month)
+ _date.set(Calendar.YEAR, year)
+ getDateDisplay()
+ }
+
+ fun setNewImage(filePath: String) {
+ newImagePath = filePath
+ _displayImage.value = filePath
+ }
+
+ private fun saveNewImage() {
+ fileUtils.saveImage(File(newImagePath))
+ }
+
+ private fun deleteOldImageIfNecessary() {
+ viewModelScope.launch {
+ if(oldImageName.isNotBlank()) {
+ val uses = repository.countUsesOfImage(oldImageName)
+ if(uses <= 1) {
+ fileUtils.deleteImage(oldImageName)
+ }
+ }
+ }
+ }
+
+ fun removeImage() {
+ if(currentImageName.isNotBlank()) {
+ oldImageName = currentImageName
+ currentImageName = ""
+ _displayImage.value = ""
+ }
+ if(newImagePath.isNotBlank()) {
+ newImagePath = ""
+ _displayImage.value = ""
+ }
+ }
+
+ fun setType(type: Int) {
+ if(_selectedType.value != type)
+ _selectedType.value = type
+ }
+
+ private fun setInputs(item: FavouriteItem) {
+ when(item.type) {
+ SONG -> {
+ _primaryInputDisplay.value = item.songName
+ _secondaryInputDisplay.value = item.artistName
+ }
+ ALBUM -> {
+ _primaryInputDisplay.value = item.albumName
+ _secondaryInputDisplay.value = item.artistName
+ }
+ ARTIST -> {
+ _primaryInputDisplay.value = item.artistName
+ _secondaryInputDisplay.value = item.genre
+ }
+ }
+ }
+
+ private fun getDateDisplay() {
+ val day = _date.get(Calendar.DAY_OF_MONTH)
+ val month = _date.get(Calendar.MONTH)
+ val year = _date.get(Calendar.YEAR)
+
+ val today = Calendar.getInstance()
+ val displayValue = if(day == today.get(Calendar.DAY_OF_MONTH) && month == today.get(Calendar.MONTH) && year == today.get(Calendar.YEAR)) {
+ ""
+ } else {
+ formatDateForDisplay(_date)
+ }
+ _dateDisplay.value = displayValue
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/app/marcdev/earworm/additem/AddItemViewModelFactory.kt b/app/src/main/java/app/marcdev/earworm/additem/AddItemViewModelFactory.kt
new file mode 100644
index 0000000..fa76306
--- /dev/null
+++ b/app/src/main/java/app/marcdev/earworm/additem/AddItemViewModelFactory.kt
@@ -0,0 +1,15 @@
+package app.marcdev.earworm.additem
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import app.marcdev.earworm.data.repository.FavouriteItemRepository
+import app.marcdev.earworm.utils.FileUtils
+
+class AddItemViewModelFactory(private val favouriteItemRepository: FavouriteItemRepository, private val fileUtils: FileUtils)
+ : ViewModelProvider.NewInstanceFactory() {
+
+ @Suppress("UNCHECKED_CAST")
+ override fun create(modelClass: Class): T {
+ return AddItemViewModel(favouriteItemRepository, fileUtils) as T
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/app/marcdev/earworm/additem/RecyclerUpdateView.kt b/app/src/main/java/app/marcdev/earworm/additem/RecyclerUpdateView.kt
deleted file mode 100644
index 55045bc..0000000
--- a/app/src/main/java/app/marcdev/earworm/additem/RecyclerUpdateView.kt
+++ /dev/null
@@ -1,5 +0,0 @@
-package app.marcdev.earworm.additem
-
-interface RecyclerUpdateView {
- fun fillData()
-}
\ No newline at end of file
diff --git a/app/src/main/java/app/marcdev/earworm/data/database/AppDatabase.kt b/app/src/main/java/app/marcdev/earworm/data/database/AppDatabase.kt
new file mode 100644
index 0000000..bf21434
--- /dev/null
+++ b/app/src/main/java/app/marcdev/earworm/data/database/AppDatabase.kt
@@ -0,0 +1,5 @@
+package app.marcdev.earworm.data.database
+
+interface AppDatabase {
+ fun dao(): DAO
+}
\ No newline at end of file
diff --git a/app/src/main/java/app/marcdev/earworm/data/database/DAO.kt b/app/src/main/java/app/marcdev/earworm/data/database/DAO.kt
new file mode 100644
index 0000000..af1708f
--- /dev/null
+++ b/app/src/main/java/app/marcdev/earworm/data/database/DAO.kt
@@ -0,0 +1,26 @@
+package app.marcdev.earworm.data.database
+
+import androidx.lifecycle.LiveData
+import androidx.room.*
+
+@Dao
+interface DAO {
+
+ @Insert(onConflict = OnConflictStrategy.FAIL)
+ fun insertItem(item: FavouriteItem)
+
+ @Update
+ fun updateItem(item: FavouriteItem)
+
+ @Query("SELECT * FROM FavouriteItems ORDER BY year DESC, month DESC, day DESC, id DESC")
+ fun getAllItems(): LiveData>
+
+ @Query("SELECT * FROM FavouriteItems where id = :id")
+ fun getItemById(id: Int): FavouriteItem
+
+ @Query("DELETE FROM FavouriteItems where id = :id")
+ fun deleteItemById(id: Int)
+
+ @Query("SELECT COUNT(*) FROM FavouriteItems WHERE imageName = :imageName")
+ fun getNumberOfEntriesUsingImage(imageName: String): Int
+}
\ No newline at end of file
diff --git a/app/src/main/java/app/marcdev/earworm/database/FavouriteItem.kt b/app/src/main/java/app/marcdev/earworm/data/database/FavouriteItem.kt
similarity index 76%
rename from app/src/main/java/app/marcdev/earworm/database/FavouriteItem.kt
rename to app/src/main/java/app/marcdev/earworm/data/database/FavouriteItem.kt
index 45fb289..cecacb4 100644
--- a/app/src/main/java/app/marcdev/earworm/database/FavouriteItem.kt
+++ b/app/src/main/java/app/marcdev/earworm/data/database/FavouriteItem.kt
@@ -1,9 +1,9 @@
-package app.marcdev.earworm.database
+package app.marcdev.earworm.data.database
import androidx.room.Entity
import androidx.room.PrimaryKey
-@Entity(tableName = "favourite_items")
+@Entity(tableName = "FavouriteItems")
data class FavouriteItem(
var songName: String,
var albumName: String,
@@ -17,5 +17,5 @@ data class FavouriteItem(
) {
@PrimaryKey(autoGenerate = true)
- var id: Int? = null
+ var id: Int = 0
}
\ No newline at end of file
diff --git a/app/src/main/java/app/marcdev/earworm/data/database/ProductionAppDatabase.kt b/app/src/main/java/app/marcdev/earworm/data/database/ProductionAppDatabase.kt
new file mode 100644
index 0000000..77f9537
--- /dev/null
+++ b/app/src/main/java/app/marcdev/earworm/data/database/ProductionAppDatabase.kt
@@ -0,0 +1,54 @@
+package app.marcdev.earworm.data.database
+
+import android.content.Context
+import androidx.room.Database
+import androidx.room.Room
+import androidx.room.RoomDatabase
+import androidx.room.migration.Migration
+import androidx.sqlite.db.SupportSQLiteDatabase
+
+@Database(entities = [FavouriteItem::class], version = 5)
+abstract class ProductionAppDatabase : RoomDatabase(), AppDatabase {
+ abstract override fun dao(): DAO
+
+ companion object {
+ @Volatile
+ private var INSTANCE: ProductionAppDatabase? = null
+ private val LOCK = Any()
+
+ operator fun invoke(context: Context) = INSTANCE
+ ?: synchronized(LOCK) {
+ INSTANCE
+ ?: buildDatabase(context).also { INSTANCE = it }
+ }
+
+ private fun buildDatabase(context: Context) =
+ Room.databaseBuilder(
+ context.applicationContext,
+ ProductionAppDatabase::class.java,
+ "AppDatabase.db"
+ )
+ .setJournalMode(JournalMode.TRUNCATE)
+ .addMigrations(MIGRATION_4_TO_5())
+ .build()
+ }
+
+ class MIGRATION_4_TO_5 : Migration(4, 5) {
+ override fun migrate(database: SupportSQLiteDatabase) {
+ database.execSQL("ALTER TABLE favourite_items RENAME TO favourite_items_old")
+ database.execSQL("CREATE TABLE FavouriteItems(" +
+ "id INTEGER PRIMARY KEY NOT NULL," +
+ "songName TEXT NOT NULL," +
+ "albumName TEXT NOT NULL," +
+ "artistName TEXT NOT NULL," +
+ "genre TEXT NOT NULL," +
+ "day INTEGER NOT NULL," +
+ "month INTEGER NOT NULL," +
+ "year INTEGER NOT NULL," +
+ "type INTEGER NOT NULL," +
+ "imageName TEXT NOT NULL)")
+ database.execSQL("INSERT INTO FavouriteItems SELECT * FROM favourite_items_old")
+ database.execSQL("DROP TABLE favourite_items_old")
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/app/marcdev/earworm/data/repository/FavouriteItemRepository.kt b/app/src/main/java/app/marcdev/earworm/data/repository/FavouriteItemRepository.kt
new file mode 100644
index 0000000..ae66852
--- /dev/null
+++ b/app/src/main/java/app/marcdev/earworm/data/repository/FavouriteItemRepository.kt
@@ -0,0 +1,17 @@
+package app.marcdev.earworm.data.repository
+
+import androidx.lifecycle.LiveData
+import app.marcdev.earworm.data.database.FavouriteItem
+
+interface FavouriteItemRepository {
+
+ val allItems: LiveData>
+
+ suspend fun addItem(item: FavouriteItem)
+
+ suspend fun getItem(id: Int): FavouriteItem
+
+ suspend fun deleteItem(id: Int)
+
+ suspend fun countUsesOfImage(imageName: String): Int
+}
\ No newline at end of file
diff --git a/app/src/main/java/app/marcdev/earworm/data/repository/FavouriteItemRepositoryImpl.kt b/app/src/main/java/app/marcdev/earworm/data/repository/FavouriteItemRepositoryImpl.kt
new file mode 100644
index 0000000..49d235b
--- /dev/null
+++ b/app/src/main/java/app/marcdev/earworm/data/repository/FavouriteItemRepositoryImpl.kt
@@ -0,0 +1,56 @@
+package app.marcdev.earworm.data.repository
+
+import android.database.sqlite.SQLiteConstraintException
+import androidx.lifecycle.LiveData
+import app.marcdev.earworm.data.database.DAO
+import app.marcdev.earworm.data.database.FavouriteItem
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+import timber.log.Timber
+
+class FavouriteItemRepositoryImpl(private val dao: DAO) : FavouriteItemRepository {
+
+ override val allItems: LiveData> by lazy {
+ dao.getAllItems()
+ }
+
+ override suspend fun addItem(item: FavouriteItem) {
+ withContext(Dispatchers.IO) {
+ try {
+ Timber.d("Log: addItem: Item doesn't exist, adding new")
+ dao.insertItem(item)
+ } catch(exception: SQLiteConstraintException) {
+ Timber.d("Log: addItem: Item already exists, updating existing")
+ dao.updateItem(item)
+ }
+ }
+ }
+
+
+ override suspend fun getItem(id: Int): FavouriteItem {
+ return withContext(Dispatchers.IO) {
+ return@withContext dao.getItemById(id)
+ }
+ }
+
+ override suspend fun deleteItem(id: Int) {
+ withContext(Dispatchers.IO) {
+ dao.deleteItemById(id)
+ }
+ }
+
+ override suspend fun countUsesOfImage(imageName: String): Int {
+ return withContext(Dispatchers.IO) {
+ return@withContext dao.getNumberOfEntriesUsingImage(imageName)
+ }
+ }
+
+ companion object {
+ @Volatile private var instance: FavouriteItemRepositoryImpl? = null
+
+ fun getInstance(dao: DAO) =
+ instance ?: synchronized(this) {
+ instance ?: FavouriteItemRepositoryImpl(dao).also { instance = it }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/app/marcdev/earworm/database/AppDatabase.kt b/app/src/main/java/app/marcdev/earworm/database/AppDatabase.kt
deleted file mode 100644
index 3cb8774..0000000
--- a/app/src/main/java/app/marcdev/earworm/database/AppDatabase.kt
+++ /dev/null
@@ -1,34 +0,0 @@
-package app.marcdev.earworm.database
-
-import android.content.Context
-import androidx.room.Database
-import androidx.room.Room
-import androidx.room.RoomDatabase
-
-@Database(entities = [FavouriteItem::class], version = 4)
-abstract class AppDatabase : RoomDatabase() {
-
- abstract fun dao(): DAO
-
- companion object {
- @Volatile
- private var INSTANCE: AppDatabase? = null
-
- fun getDatabase(context: Context): AppDatabase {
- val tempInstance = INSTANCE
-
- if(tempInstance != null) {
- return tempInstance
- }
-
- synchronized(this) {
-
- val instance = Room.databaseBuilder(context.applicationContext, AppDatabase::class.java, "AppDatabase.db")
- .fallbackToDestructiveMigration().build()
-
- INSTANCE = instance
- return instance
- }
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/app/marcdev/earworm/database/DAO.kt b/app/src/main/java/app/marcdev/earworm/database/DAO.kt
deleted file mode 100644
index b799ce7..0000000
--- a/app/src/main/java/app/marcdev/earworm/database/DAO.kt
+++ /dev/null
@@ -1,25 +0,0 @@
-package app.marcdev.earworm.database
-
-import androidx.room.Dao
-import androidx.room.Insert
-import androidx.room.OnConflictStrategy
-import androidx.room.Query
-
-@Dao
-interface DAO {
-
- @Insert(onConflict = OnConflictStrategy.REPLACE)
- fun insertOrUpdateItem(item: FavouriteItem)
-
- @Query("SELECT * FROM favourite_items")
- fun getAllItems(): MutableList
-
- @Query("SELECT * FROM favourite_items where id = :id")
- fun getItemById(id: Int): MutableList
-
- @Query("DELETE FROM favourite_items where id = :id")
- fun deleteItemById(id: Int)
-
- @Query("SELECT COUNT(*) FROM favourite_items WHERE imageName = :imageName")
- fun getNumberOfEntriesUsingImage(imageName: String): Int
-}
\ No newline at end of file
diff --git a/app/src/main/java/app/marcdev/earworm/internal/Constants.kt b/app/src/main/java/app/marcdev/earworm/internal/Constants.kt
new file mode 100644
index 0000000..7190ff7
--- /dev/null
+++ b/app/src/main/java/app/marcdev/earworm/internal/Constants.kt
@@ -0,0 +1,27 @@
+package app.marcdev.earworm.internal
+
+import app.marcdev.earworm.utils.ItemFilter
+
+val DEFAULT_FILTER = ItemFilter(1, 0, 1900, 31, 11, 2099, true, true, true, "")
+
+//
+const val SONG = 0
+const val ALBUM = 1
+const val ARTIST = 2
+const val GENRE = 3
+const val HEADER = 4
+//
+
+//
+const val LIGHT_THEME = 0
+const val DARK_THEME = 1
+//
+
+//
+const val PREF_THEME = "pref_theme"
+const val PREF_SHOW_TIPS = "pref_show_tips"
+const val PREF_BUILD_NUMBER = "pref_build_number"
+const val PREF_LICENSES = "pref_licenses"
+const val PREF_GITHUB = "pref_github"
+const val PREF_CLEAR_INPUTS = "pref_clear_inputs_on_type_change"
+//
\ No newline at end of file
diff --git a/app/src/main/java/app/marcdev/earworm/internal/base/EarwormActivity.kt b/app/src/main/java/app/marcdev/earworm/internal/base/EarwormActivity.kt
new file mode 100644
index 0000000..b279f0f
--- /dev/null
+++ b/app/src/main/java/app/marcdev/earworm/internal/base/EarwormActivity.kt
@@ -0,0 +1,19 @@
+package app.marcdev.earworm.internal.base
+
+import androidx.appcompat.app.AppCompatActivity
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.FragmentManager
+
+abstract class EarwormActivity : AppCompatActivity() {
+ /**
+ * Replaces a fragment in a frame with another fragment
+ * @param fragment The fragment to display
+ * @param fragmentManager The Fragment Manager
+ * @param frameId The ID of the frame to display the new fragment in
+ */
+ protected fun setFragment(fragment: Fragment, fragmentManager: FragmentManager, frameId: Int) {
+ val fragmentTransaction = fragmentManager.beginTransaction()
+ fragmentTransaction.replace(frameId, fragment)
+ fragmentTransaction.commit()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/app/marcdev/earworm/uicomponents/RoundedBottomDialogFragment.kt b/app/src/main/java/app/marcdev/earworm/internal/base/EarwormBottomSheetDialogFragment.kt
similarity index 77%
rename from app/src/main/java/app/marcdev/earworm/uicomponents/RoundedBottomDialogFragment.kt
rename to app/src/main/java/app/marcdev/earworm/internal/base/EarwormBottomSheetDialogFragment.kt
index f8eb9c1..9f00774 100644
--- a/app/src/main/java/app/marcdev/earworm/uicomponents/RoundedBottomDialogFragment.kt
+++ b/app/src/main/java/app/marcdev/earworm/internal/base/EarwormBottomSheetDialogFragment.kt
@@ -1,4 +1,4 @@
-package app.marcdev.earworm.uicomponents
+package app.marcdev.earworm.internal.base
import android.app.Dialog
import android.os.Bundle
@@ -6,7 +6,7 @@ import app.marcdev.earworm.R
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
-open class RoundedBottomDialogFragment : BottomSheetDialogFragment() {
+abstract class EarwormBottomSheetDialogFragment : BottomSheetDialogFragment() {
override fun getTheme(): Int {
return R.style.Earworm_BottomSheetDialogTheme
diff --git a/app/src/main/java/app/marcdev/earworm/internal/base/EarwormDialogFragment.kt b/app/src/main/java/app/marcdev/earworm/internal/base/EarwormDialogFragment.kt
new file mode 100644
index 0000000..d6b545e
--- /dev/null
+++ b/app/src/main/java/app/marcdev/earworm/internal/base/EarwormDialogFragment.kt
@@ -0,0 +1,11 @@
+package app.marcdev.earworm.internal.base
+
+import androidx.fragment.app.DialogFragment
+import app.marcdev.earworm.R
+
+abstract class EarwormDialogFragment: DialogFragment() {
+ override fun onStart() {
+ super.onStart()
+ requireDialog().window?.setBackgroundDrawableResource(R.drawable.rounded_dialog_background)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/app/marcdev/earworm/mainscreen/MainFragmentViewImpl.kt b/app/src/main/java/app/marcdev/earworm/mainscreen/MainFragment.kt
similarity index 50%
rename from app/src/main/java/app/marcdev/earworm/mainscreen/MainFragmentViewImpl.kt
rename to app/src/main/java/app/marcdev/earworm/mainscreen/MainFragment.kt
index 0024ef0..2fdd8f7 100644
--- a/app/src/main/java/app/marcdev/earworm/mainscreen/MainFragmentViewImpl.kt
+++ b/app/src/main/java/app/marcdev/earworm/mainscreen/MainFragment.kt
@@ -2,30 +2,51 @@ package app.marcdev.earworm.mainscreen
import android.content.Intent
import android.os.Bundle
+import android.preference.PreferenceManager
import android.text.Editable
import android.text.TextWatcher
import android.view.KeyEvent
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import android.widget.*
+import android.widget.EditText
+import android.widget.ImageView
+import android.widget.ProgressBar
+import android.widget.TextView
import androidx.core.widget.NestedScrollView
import androidx.fragment.app.Fragment
+import androidx.lifecycle.Observer
+import androidx.lifecycle.ViewModelProviders
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import app.marcdev.earworm.R
import app.marcdev.earworm.additem.AddItemBottomSheet
-import app.marcdev.earworm.additem.RecyclerUpdateView
-import app.marcdev.earworm.database.FavouriteItem
+import app.marcdev.earworm.data.database.FavouriteItem
+import app.marcdev.earworm.internal.DARK_THEME
+import app.marcdev.earworm.internal.PREF_SHOW_TIPS
import app.marcdev.earworm.mainscreen.mainrecycler.MainRecyclerAdapter
import app.marcdev.earworm.settingsscreen.SettingsActivity
+import app.marcdev.earworm.uicomponents.BinaryOptionDialog
import app.marcdev.earworm.uicomponents.FilterDialog
-import app.marcdev.earworm.utils.*
+import app.marcdev.earworm.utils.ItemFilter
+import app.marcdev.earworm.utils.changeColorOfDrawable
+import app.marcdev.earworm.utils.changeColorOfImageViewDrawable
+import app.marcdev.earworm.utils.getTheme
import com.google.android.material.floatingactionbutton.FloatingActionButton
-import timber.log.Timber
+import com.google.android.material.snackbar.Snackbar
+import org.kodein.di.KodeinAware
+import org.kodein.di.android.x.closestKodein
+import org.kodein.di.generic.instance
-class MainFragmentViewImpl : Fragment(), MainFragmentView, RecyclerUpdateView {
+class MainFragment : Fragment(), KodeinAware {
+ override val kodein by closestKodein()
+ //
+ private val viewModelFactory: MainFragmentViewModelFactory by instance()
+ private lateinit var viewModel: MainFragmentViewModel
+ //
+
+ //
private lateinit var fab: FloatingActionButton
private lateinit var noEntriesWarning: TextView
private lateinit var noEntriesWarningImage: ImageView
@@ -33,29 +54,28 @@ class MainFragmentViewImpl : Fragment(), MainFragmentView, RecyclerUpdateView {
private lateinit var noFilteredResultsWarningImage: ImageView
private lateinit var progressBar: ProgressBar
private lateinit var searchInput: EditText
- private lateinit var searchButton: ImageButton
- private lateinit var filterButton: ImageButton
- private lateinit var settingsButton: ImageButton
+ private lateinit var searchButton: ImageView
+ private lateinit var filterButton: ImageView
+ private lateinit var settingsButton: ImageView
private lateinit var filterDialog: FilterDialog
private lateinit var recyclerAdapter: MainRecyclerAdapter
- private lateinit var presenter: MainFragmentPresenter
- private var isSearchMode = true
+ //
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ viewModel = ViewModelProviders.of(this, viewModelFactory).get(MainFragmentViewModel::class.java)
+ }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
- Timber.d("Log: onCreateView: Started")
val view = inflater.inflate(R.layout.fragment_mainscreen, container, false)
-
- presenter = MainFragmentPresenterImpl(this, activity!!.applicationContext)
bindViews(view)
+ setupRecycler(view)
+ setupObservers()
if(getTheme(requireContext()) == DARK_THEME) {
- Timber.d("Log: onCreateView: Is dark mode, converting")
convertToDarkMode()
}
- setupRecycler(view)
- fillData()
-
// If arguments is not null, see if the app has been opened from an app shortcut
arguments?.let {
if(arguments!!.getBoolean("add_item", false)) {
@@ -67,7 +87,6 @@ class MainFragmentViewImpl : Fragment(), MainFragmentView, RecyclerUpdateView {
}
private fun bindViews(view: View) {
- Timber.v("Log: bindViews: Started")
this.fab = view.findViewById(R.id.fab_main)
fab.setOnClickListener(fabOnClickListener)
@@ -94,21 +113,18 @@ class MainFragmentViewImpl : Fragment(), MainFragmentView, RecyclerUpdateView {
val nestedScrollView: NestedScrollView = view.findViewById(R.id.scroll_main)
nestedScrollView.setOnScrollChangeListener(scrollViewOnScrollChangeListener)
- this.filterDialog = FilterDialog(requireActivity(), presenter)
+ this.filterDialog = FilterDialog(::filterOkClick)
this.settingsButton = view.findViewById(R.id.img_settings)
settingsButton.setOnClickListener(settingsOnClickListener)
}
private val fabOnClickListener = View.OnClickListener {
- Timber.d("Log: Fab Clicked")
val addDialog = AddItemBottomSheet()
- addDialog.bindRecyclerUpdateView(this)
- addDialog.show(fragmentManager, "Add Item Bottom Sheet Dialog")
+ addDialog.show(requireFragmentManager(), "Add Item Bottom Sheet Dialog")
}
private val settingsOnClickListener = View.OnClickListener {
- Timber.d("Log: Settings Clicked")
val intent = Intent(requireContext(), SettingsActivity::class.java)
startActivity(intent)
}
@@ -126,32 +142,29 @@ class MainFragmentViewImpl : Fragment(), MainFragmentView, RecyclerUpdateView {
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
if(s.isNullOrBlank()) {
- presenter.search("")
+ viewModel.search("")
}
}
}
private fun testIfSubmitButtonClicked(keyEvent: KeyEvent, keyCode: Int): Boolean {
- Timber.d("Log: testIfSubmitButtonClicked: Submit button clicked")
if((keyEvent.action == KeyEvent.ACTION_DOWN) && keyCode == KeyEvent.KEYCODE_ENTER) {
- presenter.search(searchInput.text.toString())
+ viewModel.search(searchInput.text.toString())
return true
}
return false
}
private val searchOnClickListener = View.OnClickListener {
- Timber.d("Log: Search Clicked")
- if(isSearchMode) {
- presenter.search(searchInput.text.toString())
- } else {
- searchInput.setText("")
- }
+ viewModel.search(searchInput.text.toString())
+ }
+
+ private val clearSearchClickListener = View.OnClickListener {
+ searchInput.setText("")
}
private val filterOnClickListener = View.OnClickListener {
- Timber.d("Log: Filter Clicked")
- filterDialog.show()
+ filterDialog.show(requireFragmentManager(), "Filter Dialog")
}
private var scrollViewOnScrollChangeListener = { _: View, _: Int, scrollY: Int, _: Int, oldScrollY: Int -> hideFabOnScroll(scrollY, oldScrollY) }
@@ -164,109 +177,106 @@ class MainFragmentViewImpl : Fragment(), MainFragmentView, RecyclerUpdateView {
}
}
+ private fun filterOkClick(filter: ItemFilter) {
+ viewModel.filter(filter)
+ }
+
private fun setupRecycler(view: View) {
- Timber.v("Log: setupRecycler: Started")
val recycler: RecyclerView = view.findViewById(R.id.recycler_main)
- this.recyclerAdapter = MainRecyclerAdapter(context, presenter)
+ this.recyclerAdapter = MainRecyclerAdapter(requireContext(), ::itemClick, ::itemLongClick)
recycler.adapter = recyclerAdapter
recycler.layoutManager = LinearLayoutManager(context)
}
- override fun fillData() {
- Timber.d("Log: fillData: Started")
- displayProgress(true)
- displayNoEntriesWarning(false)
- presenter.getAllItems()
- }
+ private fun setupObservers() {
+ viewModel.displayLoading.observe(this, Observer { value ->
+ value?.let { show ->
+ progressBar.visibility = if(show) View.VISIBLE else View.GONE
+ }
+ })
- override fun displayNoEntriesWarning(display: Boolean) {
- Timber.d("Log: displayNoEntriesWarning: Started with display = $display")
+ viewModel.displayNoEntries.observe(this, Observer { value ->
+ value?.let { show ->
+ noEntriesWarning.visibility = if(show) View.VISIBLE else View.GONE
+ noEntriesWarningImage.visibility = if(show) View.VISIBLE else View.GONE
+ }
+ })
- if(display) {
- Timber.d("Log: displayNoEntriesWarning: Displaying")
- noEntriesWarning.visibility = View.VISIBLE
- noEntriesWarningImage.visibility = View.VISIBLE
- } else {
- Timber.d("Log: displayNoEntriesWarning: Hiding")
- noEntriesWarning.visibility = View.GONE
- noEntriesWarningImage.visibility = View.GONE
- }
- }
+ viewModel.displayNoFilteredResults.observe(this, Observer { value ->
+ value?.let { show ->
+ noFilteredResultsWarning.visibility = if(show) View.VISIBLE else View.GONE
+ noFilteredResultsWarningImage.visibility = if(show) View.VISIBLE else View.GONE
+ }
+ })
- override fun updateRecycler(items: List) {
- Timber.d("Log: updateRecycler: Started")
- recyclerAdapter.updateItems(items)
- }
+ viewModel.displayData.observe(this, Observer { items ->
+ items?.let {
+ recyclerAdapter.updateItems(items)
+ }
+ })
- override fun displayAddedToast() {
- Timber.d("Log: displayAddedToast: Started")
- Toast.makeText(activity, resources.getString(R.string.item_added), Toast.LENGTH_SHORT).show()
+ viewModel.colorFilterIcon.observe(this, Observer { value ->
+ value?.let { colorIt ->
+ changeColorOfImageViewDrawable(context!!, filterButton, colorIt)
+ }
+ })
+
+ viewModel.displaySearchIcon.observe(this, Observer { value ->
+ value?.let { show ->
+ if(show) {
+ searchButton.setImageDrawable(resources.getDrawable(R.drawable.ic_search_24px, null))
+ searchButton.setOnClickListener(searchOnClickListener)
+ } else {
+ searchButton.setImageDrawable(resources.getDrawable(R.drawable.ic_close_24px, null))
+ searchButton.setOnClickListener(clearSearchClickListener)
+ }
+ }
+ })
}
- override fun displayItemDeletedToast() {
- Timber.d("Log: displayItemDeletedToast: Started")
- Toast.makeText(activity, resources.getString(R.string.item_deleted), Toast.LENGTH_SHORT).show()
+ private fun itemClick() {
+ val prefs = PreferenceManager.getDefaultSharedPreferences(requireContext())
+ if(prefs.getBoolean(PREF_SHOW_TIPS, true)) {
+ val snackbar = Snackbar.make(requireView(), resources.getString(R.string.long_click_hint), Snackbar.LENGTH_SHORT)
+ snackbar.setAction(resources.getString(R.string.dont_show)) {
+ prefs.edit().putBoolean("pref_show_tips", false).apply()
+ }
+ snackbar.show()
+ }
}
- override fun displayProgress(isVisible: Boolean) {
- Timber.d("Log: displayProgress: Started with isVisible = $isVisible")
- if(isVisible) {
- progressBar.visibility = View.VISIBLE
- } else {
- progressBar.visibility = View.GONE
- }
+ private fun itemLongClick(favouriteItem: FavouriteItem) {
+ val dialogBuilder = BinaryOptionDialog.Builder()
+ dialogBuilder
+ .setTitle(resources.getString(R.string.edit_or_delete))
+ .setMessageVisible(false)
+ .setNegativeButton(resources.getString(R.string.delete), {
+ deleteClick(favouriteItem)
+ }, true)
+ .setPositiveButton(resources.getString(R.string.edit), {
+ editClick(favouriteItem)
+ }, true)
+ dialogBuilder.build().show(requireFragmentManager(), "Edit or Delete Dialog")
}
- override fun displayEditItemSheet(itemId: Int) {
- Timber.d("Log: displayEditItemSheet: Started with itemId = $itemId")
+ private fun editClick(favouriteItem: FavouriteItem) {
val addDialog = AddItemBottomSheet()
- addDialog.bindRecyclerUpdateView(this)
val args = Bundle()
- args.putInt("item_id", itemId)
+ args.putInt("item_id", favouriteItem.id)
addDialog.arguments = args
- addDialog.show(fragmentManager, "Add Item Bottom Sheet Dialog")
- }
-
- override fun displayNoFilteredResultsWarning(display: Boolean) {
- Timber.d("Log: displayNoFilteredResultsWarning: Started with display = $display")
-
- if(display && (noEntriesWarning.visibility == View.GONE)) {
- this.noFilteredResultsWarning.visibility = View.VISIBLE
- this.noFilteredResultsWarningImage.visibility = View.VISIBLE
- } else {
- this.noFilteredResultsWarning.visibility = View.GONE
- this.noFilteredResultsWarningImage.visibility = View.GONE
- }
- }
-
- override fun getActiveFilter(): ItemFilter {
- Timber.d("Log: getActiveFilter: Started")
- return filterDialog.activeFilter
+ addDialog.show(requireFragmentManager(), "Add Item Bottom Sheet Dialog")
}
- override fun changeSearchIcon(isSearch: Boolean) {
- Timber.d("Log: changeSearchIcon: Started")
- if(isSearch) {
- searchButton.setImageDrawable(resources.getDrawable(R.drawable.ic_search_24px, null))
- this.isSearchMode = true
- } else {
- searchButton.setImageDrawable(resources.getDrawable(R.drawable.ic_close_24px, null))
- this.isSearchMode = false
- }
- }
-
- override fun activateFilterIcon(isActive: Boolean) {
- Timber.d("Log: activateFilterIcon: Started with isActive = $isActive")
-
- changeColorOfImageButtonDrawable(context!!, filterButton, isActive)
+ private fun deleteClick(favouriteItem: FavouriteItem) {
+ viewModel.deleteItem(favouriteItem)
}
private fun convertToDarkMode() {
- changeColorOfImageButtonDrawable(requireContext(), filterButton, false)
- changeColorOfImageButtonDrawable(requireContext(), settingsButton, false)
- changeColorOfImageButtonDrawable(requireContext(), searchButton, false)
+ changeColorOfImageViewDrawable(requireContext(), filterButton, false)
+ changeColorOfImageViewDrawable(requireContext(), settingsButton, false)
+ changeColorOfImageViewDrawable(requireContext(), searchButton, false)
changeColorOfDrawable(requireContext(), noEntriesWarningImage.drawable, false)
changeColorOfDrawable(requireContext(), noFilteredResultsWarningImage.drawable, false)
}
diff --git a/app/src/main/java/app/marcdev/earworm/mainscreen/MainFragmentModel.kt b/app/src/main/java/app/marcdev/earworm/mainscreen/MainFragmentModel.kt
deleted file mode 100644
index b6ac979..0000000
--- a/app/src/main/java/app/marcdev/earworm/mainscreen/MainFragmentModel.kt
+++ /dev/null
@@ -1,17 +0,0 @@
-package app.marcdev.earworm.mainscreen
-
-import app.marcdev.earworm.database.FavouriteItem
-import app.marcdev.earworm.utils.ItemFilter
-
-interface MainFragmentModel {
-
- fun getAllItemsAsync()
-
- fun getAllItemsAsync(filter: ItemFilter)
-
- fun deleteItemAsync(item: FavouriteItem)
-
- fun countUsesOfImage(item: FavouriteItem)
-
- fun deleteImage(filePath: String)
-}
\ No newline at end of file
diff --git a/app/src/main/java/app/marcdev/earworm/mainscreen/MainFragmentModelImpl.kt b/app/src/main/java/app/marcdev/earworm/mainscreen/MainFragmentModelImpl.kt
deleted file mode 100644
index 27eccdd..0000000
--- a/app/src/main/java/app/marcdev/earworm/mainscreen/MainFragmentModelImpl.kt
+++ /dev/null
@@ -1,89 +0,0 @@
-package app.marcdev.earworm.mainscreen
-
-import android.content.Context
-import app.marcdev.earworm.database.AppDatabase
-import app.marcdev.earworm.database.FavouriteItem
-import app.marcdev.earworm.repository.FavouriteItemRepository
-import app.marcdev.earworm.repository.FavouriteItemRepositoryImpl
-import app.marcdev.earworm.utils.ItemFilter
-import app.marcdev.earworm.utils.getArtworkDirectory
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.GlobalScope
-import kotlinx.coroutines.async
-import kotlinx.coroutines.launch
-import timber.log.Timber
-import java.io.File
-
-class MainFragmentModelImpl(private val presenter: MainFragmentPresenter, private val context: Context) : MainFragmentModel {
-
- private var repository: FavouriteItemRepository
-
- init {
- val db: AppDatabase = AppDatabase.getDatabase(context)
- repository = FavouriteItemRepositoryImpl(db.dao())
- }
-
- override fun getAllItemsAsync() {
- Timber.d("Log: getAllItemsAsync: Started")
-
- GlobalScope.launch(Dispatchers.Main) {
- val allItems = async(Dispatchers.IO) {
- repository.getAllItems()
- }.await()
-
- presenter.getAllItemsCallback(allItems)
- }
- }
-
- override fun getAllItemsAsync(filter: ItemFilter) {
- Timber.d("Log: getAllItemsAsync: Started")
-
- GlobalScope.launch(Dispatchers.Main) {
- val allItems = async(Dispatchers.IO) {
- repository.getAllItems()
- }.await()
-
- presenter.getAllItemsCallback(allItems, filter)
- }
- }
-
- override fun deleteItemAsync(item: FavouriteItem) {
- Timber.d("Log: deleteItemAsync: Started")
-
- GlobalScope.launch(Dispatchers.Main) {
- async(Dispatchers.IO) {
- repository.deleteItem(item.id!!)
- }.await()
-
- presenter.deleteItemCallback()
- }
- }
-
- override fun countUsesOfImage(item: FavouriteItem) {
- Timber.d("Log: countUsesOfImage: Started with item = $item")
- val filePath = getArtworkDirectory(context) + item.imageName
- val file = File(filePath)
- val fileName = file.name
-
- GlobalScope.launch(Dispatchers.Main) {
- val uses = async(Dispatchers.IO) {
- repository.countUsesOfImage(fileName)
- }.await()
-
- presenter.countUsesOfImageCallback(item, uses)
- }
- }
-
- override fun deleteImage(filePath: String) {
- Timber.d("Log: deleteImage: Started with filePath = $filePath")
-
- val file = File(filePath)
- if(file.exists()) {
- Timber.d("Log: deleteImage: File exists, deleting")
- val deletionStatus = file.delete()
- Timber.d("Log: deleteImage: Deletion: $deletionStatus")
- } else {
- Timber.w("Log: deleteImage: File doesn't exist")
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/app/marcdev/earworm/mainscreen/MainFragmentPresenter.kt b/app/src/main/java/app/marcdev/earworm/mainscreen/MainFragmentPresenter.kt
deleted file mode 100644
index 8aae525..0000000
--- a/app/src/main/java/app/marcdev/earworm/mainscreen/MainFragmentPresenter.kt
+++ /dev/null
@@ -1,25 +0,0 @@
-package app.marcdev.earworm.mainscreen
-
-import app.marcdev.earworm.database.FavouriteItem
-import app.marcdev.earworm.utils.ItemFilter
-
-interface MainFragmentPresenter {
-
- fun getAllItems()
-
- fun getAllItems(filter: ItemFilter)
-
- fun getAllItemsCallback(items: MutableList)
-
- fun getAllItemsCallback(items: MutableList, filter: ItemFilter)
-
- fun deleteItem(item: FavouriteItem)
-
- fun deleteItemCallback()
-
- fun editItemClick(itemId: Int)
-
- fun search(input: String)
-
- fun countUsesOfImageCallback(item: FavouriteItem, uses: Int)
-}
\ No newline at end of file
diff --git a/app/src/main/java/app/marcdev/earworm/mainscreen/MainFragmentPresenterImpl.kt b/app/src/main/java/app/marcdev/earworm/mainscreen/MainFragmentPresenterImpl.kt
deleted file mode 100644
index b8cd93c..0000000
--- a/app/src/main/java/app/marcdev/earworm/mainscreen/MainFragmentPresenterImpl.kt
+++ /dev/null
@@ -1,109 +0,0 @@
-package app.marcdev.earworm.mainscreen
-
-import android.content.Context
-import app.marcdev.earworm.database.FavouriteItem
-import app.marcdev.earworm.utils.*
-import timber.log.Timber
-
-class MainFragmentPresenterImpl(val view: MainFragmentView, val context: Context) : MainFragmentPresenter {
- private var model = MainFragmentModelImpl(this, context)
-
- override fun getAllItems() {
- Timber.d("Log: getAllItems: Started")
- model.getAllItemsAsync()
- }
-
- override fun getAllItems(filter: ItemFilter) {
- Timber.d("Log: getAllItems with Filter: Started")
- Timber.i("Log: getAllItems: Input Filter = $filter")
- if(filter != DEFAULT_FILTER.copy()) {
- view.activateFilterIcon(true)
- } else {
- view.activateFilterIcon(false)
- }
- model.getAllItemsAsync(filter)
- }
-
- override fun getAllItemsCallback(items: MutableList) {
- Timber.d("Log: getAllItemsCallback: Started")
-
- val sortedItems = sortByDateDescending(items)
- val itemsWithHeaders = addListHeaders(sortedItems)
-
- view.updateRecycler(itemsWithHeaders)
- view.displayProgress(false)
-
- if(itemsWithHeaders.isEmpty()) {
- view.displayNoEntriesWarning(true)
- } else {
- view.displayNoEntriesWarning(false)
- }
- }
-
- override fun getAllItemsCallback(items: MutableList, filter: ItemFilter) {
- Timber.d("Log: getAllItemsCallback: Started with filter = $filter")
-
- val sortedItems = applyFilter(items, filter)
- val itemsWithHeaders = addListHeaders(sortedItems)
-
- view.updateRecycler(itemsWithHeaders)
- view.displayProgress(false)
-
- if(itemsWithHeaders.isEmpty()) {
- view.displayNoFilteredResultsWarning(true)
- } else {
- view.displayNoFilteredResultsWarning(false)
- }
- }
-
- override fun deleteItem(item: FavouriteItem) {
- Timber.d("Log: deleteItem: Started")
- if(item.imageName != "") {
- Timber.d("Log: deleteItem: Item has an image, checking if used elsewhere")
- model.countUsesOfImage(item)
- } else {
- Timber.d("Log: deleteItem: Item does not have an image, deleting item")
- model.deleteItemAsync(item)
- }
- }
-
- override fun deleteItemCallback() {
- Timber.d("Log: deleteItemCallback: Started")
- if(view.getActiveFilter() != DEFAULT_FILTER.copy()) {
- getAllItems(view.getActiveFilter())
- } else {
- getAllItems()
- }
- }
-
- override fun countUsesOfImageCallback(item: FavouriteItem, uses: Int) {
- Timber.d("Log: countUsesOfImageCallback: Started with imageName = ${item.imageName} and uses = $uses")
-
- if(uses <= 1) {
- val filePath = getArtworkDirectory(context) + item.imageName
- model.deleteImage(filePath)
- }
-
- model.deleteItemAsync(item)
- }
-
- override fun editItemClick(itemId: Int) {
- Timber.d("Log: editItemClick: Started")
- view.displayEditItemSheet(itemId)
- }
-
- override fun search(input: String) {
- Timber.d("Log: search: Started with input = $input")
- if(input.isBlank()) {
- val inputFilter = view.getActiveFilter()
- inputFilter.searchTerm = ""
- model.getAllItemsAsync(inputFilter)
- view.changeSearchIcon(true)
- } else {
- val inputFilter = view.getActiveFilter()
- inputFilter.searchTerm = input.trim()
- model.getAllItemsAsync(inputFilter)
- view.changeSearchIcon(false)
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/app/marcdev/earworm/mainscreen/MainFragmentView.kt b/app/src/main/java/app/marcdev/earworm/mainscreen/MainFragmentView.kt
deleted file mode 100644
index 86660e2..0000000
--- a/app/src/main/java/app/marcdev/earworm/mainscreen/MainFragmentView.kt
+++ /dev/null
@@ -1,27 +0,0 @@
-package app.marcdev.earworm.mainscreen
-
-import app.marcdev.earworm.database.FavouriteItem
-import app.marcdev.earworm.utils.ItemFilter
-
-interface MainFragmentView {
-
- fun displayNoEntriesWarning(display: Boolean)
-
- fun displayNoFilteredResultsWarning(display: Boolean)
-
- fun displayAddedToast()
-
- fun displayItemDeletedToast()
-
- fun updateRecycler(items: List)
-
- fun displayProgress(isVisible: Boolean)
-
- fun displayEditItemSheet(itemId: Int)
-
- fun getActiveFilter(): ItemFilter
-
- fun changeSearchIcon(isSearch: Boolean)
-
- fun activateFilterIcon(isActive: Boolean)
-}
\ No newline at end of file
diff --git a/app/src/main/java/app/marcdev/earworm/mainscreen/MainFragmentViewModel.kt b/app/src/main/java/app/marcdev/earworm/mainscreen/MainFragmentViewModel.kt
new file mode 100644
index 0000000..54026ad
--- /dev/null
+++ b/app/src/main/java/app/marcdev/earworm/mainscreen/MainFragmentViewModel.kt
@@ -0,0 +1,241 @@
+package app.marcdev.earworm.mainscreen
+
+import androidx.lifecycle.*
+import app.marcdev.earworm.data.database.FavouriteItem
+import app.marcdev.earworm.data.repository.FavouriteItemRepository
+import app.marcdev.earworm.internal.*
+import app.marcdev.earworm.utils.FileUtils
+import app.marcdev.earworm.utils.ItemFilter
+import kotlinx.coroutines.launch
+
+class MainFragmentViewModel(private val repository: FavouriteItemRepository,
+ private val fileUtils: FileUtils)
+ : ViewModel() {
+
+ private val activeFilter = MutableLiveData()
+ private val allItems = repository.allItems
+
+ private val _displayData = MediatorLiveData>()
+ val displayData: LiveData>
+ get() = _displayData
+
+ private val _displayLoading = MutableLiveData()
+ val displayLoading: LiveData
+ get() = _displayLoading
+
+ private val _displayNoEntries = MutableLiveData()
+ val displayNoEntries: LiveData
+ get() = _displayNoEntries
+
+ private val _displayNoFilteredResults = MutableLiveData()
+ val displayNoFilteredResults: LiveData
+ get() = _displayNoFilteredResults
+
+ private val _colorFilterIcon = MutableLiveData()
+ val colorFilterIcon: LiveData
+ get() = _colorFilterIcon
+
+ private val _displaySearchIcon = MutableLiveData()
+ val displaySearchIcon: LiveData
+ get() = _displaySearchIcon
+
+ init {
+ _displayNoEntries.value = false
+ _displayNoFilteredResults.value = false
+ _displayData.addSource(activeFilter) { filter ->
+ workOutDisplayData(allItems.value, filter)
+ }
+ _displayData.addSource(allItems) { items ->
+ workOutDisplayData(items, activeFilter.value)
+ }
+ }
+
+ private fun workOutDisplayData(items: List?, filter: ItemFilter?) {
+ _displayLoading.value = true
+ displaySearchIconIfNeeded(filter)
+ var finalList: List? = null
+
+ items?.let { fullList ->
+ _displayNoEntries.value = fullList.isEmpty()
+
+ val filteredList = if(filter != null) {
+ filterResults(fullList, filter)
+ } else {
+ fullList
+ }
+ finalList = addListHeaders(filteredList)
+ }
+
+ _displayData.value = finalList?.toList()
+ _displayLoading.value = false
+ }
+
+ fun deleteItem(item: FavouriteItem) {
+ viewModelScope.launch {
+ repository.deleteItem(item.id)
+ deleteImageIfNecessary(item.imageName)
+ }
+ }
+
+ fun search(searchTermArg: String) {
+ val newFilter: ItemFilter? = if(activeFilter.value == null)
+ DEFAULT_FILTER.copy()
+ else
+ activeFilter.value
+
+ newFilter?.searchTerm = searchTermArg
+ activeFilter.value = newFilter
+ }
+
+ fun filter(filter: ItemFilter) {
+ val newFilter: ItemFilter? = if(activeFilter.value == null)
+ DEFAULT_FILTER.copy()
+ else
+ activeFilter.value
+ if(newFilter != null) {
+ newFilter.endDay = filter.endDay
+ newFilter.endMonth = filter.endMonth
+ newFilter.endYear = filter.endYear
+ newFilter.startDay = filter.startDay
+ newFilter.startMonth = filter.startMonth
+ newFilter.startYear = filter.startYear
+ newFilter.includeAlbums = filter.includeAlbums
+ newFilter.includeArtists = filter.includeArtists
+ newFilter.includeSongs = filter.includeSongs
+ }
+ activeFilter.value = newFilter
+ }
+
+ private suspend fun deleteImageIfNecessary(imageName: String) {
+ if(imageName.isNotBlank()) {
+ val uses = repository.countUsesOfImage(imageName)
+ if(uses == 0) {
+ fileUtils.deleteImage(imageName)
+ }
+ }
+ }
+
+ private fun displaySearchIconIfNeeded(filter: ItemFilter?) {
+ if(filter == null) {
+ _displaySearchIcon.value = true
+ } else {
+ _displaySearchIcon.value = filter.searchTerm.isBlank()
+ }
+ }
+
+ private fun filterResults(allItems: List, filter: ItemFilter): List {
+ val filteredByDate = filterByDate(allItems, filter)
+ val filteredByType = filterByType(filteredByDate, filter)
+ val filteredByText = filterByText(filteredByType, filter)
+
+ _displayNoFilteredResults.value = filteredByText.isEmpty()
+ colorFilterIconIfNecessary(filter)
+ return filteredByText
+ }
+
+ private fun filterByDate(items: List, filter: ItemFilter): List {
+ val filteredItems = mutableListOf()
+
+ val filterCompleteDateStart = getCompleteDate(filter.startDay, filter.startMonth, filter.startYear)
+ val filterCompleteDateEnd: Int = getCompleteDate(filter.endDay, filter.endMonth, filter.endYear)
+
+ for(item in items) {
+ val itemCompleteDate = getCompleteDate(item.day, item.month, item.year)
+ if(itemCompleteDate in filterCompleteDateStart..filterCompleteDateEnd) {
+ filteredItems.add(item)
+ }
+ }
+
+ return filteredItems
+ }
+
+ private fun filterByType(items: List, filter: ItemFilter): List {
+ val filteredItems = mutableListOf()
+
+ for(item in items) {
+ if(item.type == SONG && filter.includeSongs)
+ filteredItems.add(item)
+
+ if(item.type == ALBUM && filter.includeAlbums)
+ filteredItems.add(item)
+
+ if(item.type == ARTIST && filter.includeArtists)
+ filteredItems.add(item)
+ }
+
+ return filteredItems
+ }
+
+ private fun filterByText(items: List, filter: ItemFilter): List {
+ val filteredItems = mutableListOf()
+
+ for(item in items) {
+ if((item.albumName.contains(filter.searchTerm, true)
+ || (item.artistName.contains(filter.searchTerm, true)
+ || (item.songName.contains(filter.searchTerm, true)
+ || (item.genre.contains(filter.searchTerm, true)))))
+ ) {
+ filteredItems.add(item)
+ }
+ }
+ return filteredItems
+ }
+
+ private fun getCompleteDate(day: Int, month: Int, year: Int): Int {
+ val dayTwoDigit = if(day < 10)
+ "0$day"
+ else
+ "$day"
+
+ val monthTwoDigit = if(month < 10)
+ "0$month"
+ else
+ "$month"
+
+ return "$year$monthTwoDigit$dayTwoDigit".toInt()
+ }
+
+ private fun addListHeaders(items: List): List {
+ val listWithHeaders = items.toMutableList()
+
+ var lastMonth = 12
+ var lastYear = 9999
+ if(items.isNotEmpty()) {
+ lastMonth = items.first().month + 1
+ lastYear = items.first().year
+ }
+
+ val headersToAdd = mutableListOf>()
+
+ for(x in 0 until items.size) {
+ if(((items[x].month < lastMonth) && (items[x].year == lastYear))
+ || (items[x].month > lastMonth) && (items[x].year < lastYear)
+ || (items[x].year < lastYear)
+ ) {
+ val header = FavouriteItem("", "", "", "", 0, items[x].month, items[x].year, HEADER, "")
+ lastMonth = items[x].month
+ lastYear = items[x].year
+ headersToAdd.add(Pair(x, header))
+ }
+ }
+
+ for((add, x) in (0 until headersToAdd.size).withIndex()) {
+ listWithHeaders.add(headersToAdd[x].first + add, headersToAdd[x].second)
+ }
+
+ return listWithHeaders
+ }
+
+ private fun colorFilterIconIfNecessary(filter: ItemFilter) {
+ val isDefaultFilter = filter.startDay == DEFAULT_FILTER.startDay
+ && filter.startMonth == DEFAULT_FILTER.startMonth
+ && filter.startYear == DEFAULT_FILTER.startYear
+ && filter.endDay == DEFAULT_FILTER.endDay
+ && filter.endMonth == DEFAULT_FILTER.endMonth
+ && filter.endYear == DEFAULT_FILTER.endYear
+ && filter.includeSongs == DEFAULT_FILTER.includeSongs
+ && filter.includeAlbums == DEFAULT_FILTER.includeAlbums
+ && filter.includeArtists == DEFAULT_FILTER.includeArtists
+ _colorFilterIcon.value = !isDefaultFilter
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/app/marcdev/earworm/mainscreen/MainFragmentViewModelFactory.kt b/app/src/main/java/app/marcdev/earworm/mainscreen/MainFragmentViewModelFactory.kt
new file mode 100644
index 0000000..4330abf
--- /dev/null
+++ b/app/src/main/java/app/marcdev/earworm/mainscreen/MainFragmentViewModelFactory.kt
@@ -0,0 +1,15 @@
+package app.marcdev.earworm.mainscreen
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import app.marcdev.earworm.data.repository.FavouriteItemRepository
+import app.marcdev.earworm.utils.FileUtils
+
+class MainFragmentViewModelFactory(private val favouriteItemRepository: FavouriteItemRepository, private val fileUtils: FileUtils)
+ : ViewModelProvider.NewInstanceFactory() {
+
+ @Suppress("UNCHECKED_CAST")
+ override fun create(modelClass: Class): T {
+ return MainFragmentViewModel(favouriteItemRepository, fileUtils) as T
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/app/marcdev/earworm/mainscreen/mainrecycler/MainRecyclerAdapter.kt b/app/src/main/java/app/marcdev/earworm/mainscreen/mainrecycler/MainRecyclerAdapter.kt
index 7c6bce7..ef3463c 100644
--- a/app/src/main/java/app/marcdev/earworm/mainscreen/mainrecycler/MainRecyclerAdapter.kt
+++ b/app/src/main/java/app/marcdev/earworm/mainscreen/mainrecycler/MainRecyclerAdapter.kt
@@ -5,54 +5,47 @@ import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import app.marcdev.earworm.R
-import app.marcdev.earworm.database.FavouriteItem
-import app.marcdev.earworm.mainscreen.MainFragmentPresenter
-import app.marcdev.earworm.utils.*
-import timber.log.Timber
+import app.marcdev.earworm.data.database.FavouriteItem
+import app.marcdev.earworm.internal.*
-class MainRecyclerAdapter(context: Context?, private val presenter: MainFragmentPresenter) : RecyclerView.Adapter(), MainRecyclerView {
+class MainRecyclerAdapter(context: Context,
+ private val itemClick: () -> Unit,
+ private val itemLongClick: (FavouriteItem) -> Unit)
+ : RecyclerView.Adapter() {
private var items: List = mutableListOf()
private var inflater: LayoutInflater = LayoutInflater.from(context)
override fun getItemViewType(position: Int): Int {
- Timber.v("Log: getItemViewType: Started")
return items[position].type
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MainRecyclerViewHolder {
- Timber.v("Log: onCreateViewHolder: Started")
-
lateinit var viewHolder: MainRecyclerViewHolder
when(viewType) {
HEADER -> {
- Timber.v("Log: onCreateViewHolder: Type == Header")
val view = inflater.inflate(R.layout.item_header, parent, false)
viewHolder = MainRecyclerViewHolderHeader(view)
}
SONG -> {
- Timber.v("Log: onCreateViewHolder: Type == Song")
val view = inflater.inflate(R.layout.item_mainrecycler_song, parent, false)
- viewHolder = MainRecyclerViewHolderSong(view)
+ viewHolder = MainRecyclerViewHolderSong(view, itemClick, itemLongClick)
}
ALBUM -> {
- Timber.v("Log: onCreateViewHolder: Type == Album")
val view = inflater.inflate(R.layout.item_mainrecycler_album, parent, false)
- viewHolder = MainRecyclerViewHolderAlbum(view)
+ viewHolder = MainRecyclerViewHolderAlbum(view, itemClick, itemLongClick)
}
ARTIST -> {
- Timber.v("Log: onCreateViewHolder: Type == Artist")
val view = inflater.inflate(R.layout.item_mainrecycler_artist, parent, false)
- viewHolder = MainRecyclerViewHolderArtist(view)
+ viewHolder = MainRecyclerViewHolderArtist(view, itemClick, itemLongClick)
}
GENRE -> {
- Timber.v("Log: onCreateViewHolder: Type == Genre")
val view = inflater.inflate(R.layout.item_mainrecycler_genre, parent, false)
- viewHolder = MainRecyclerViewHolderGenre(view)
+ viewHolder = MainRecyclerViewHolderGenre(view, itemClick, itemLongClick)
}
}
@@ -60,18 +53,14 @@ class MainRecyclerAdapter(context: Context?, private val presenter: MainFragment
}
override fun onBindViewHolder(holder: MainRecyclerViewHolder, position: Int) {
- Timber.v("Log: onBindViewHolder: $position")
holder.display(items[position])
- holder.bindPresenterAndItem(presenter, items[position])
}
override fun getItemCount(): Int {
- Timber.v("Log: getItemCount: ${items.size}")
return items.size
}
- override fun updateItems(items: List) {
- Timber.d("Log: updateItems: Started")
+ fun updateItems(items: List) {
this.items = items
notifyDataSetChanged()
}
diff --git a/app/src/main/java/app/marcdev/earworm/mainscreen/mainrecycler/MainRecyclerView.kt b/app/src/main/java/app/marcdev/earworm/mainscreen/mainrecycler/MainRecyclerView.kt
deleted file mode 100644
index 27a2735..0000000
--- a/app/src/main/java/app/marcdev/earworm/mainscreen/mainrecycler/MainRecyclerView.kt
+++ /dev/null
@@ -1,7 +0,0 @@
-package app.marcdev.earworm.mainscreen.mainrecycler
-
-import app.marcdev.earworm.database.FavouriteItem
-
-interface MainRecyclerView {
- fun updateItems(items: List)
-}
\ No newline at end of file
diff --git a/app/src/main/java/app/marcdev/earworm/mainscreen/mainrecycler/MainRecyclerViewHolder.kt b/app/src/main/java/app/marcdev/earworm/mainscreen/mainrecycler/MainRecyclerViewHolder.kt
index ae2a229..88c96c8 100644
--- a/app/src/main/java/app/marcdev/earworm/mainscreen/mainrecycler/MainRecyclerViewHolder.kt
+++ b/app/src/main/java/app/marcdev/earworm/mainscreen/mainrecycler/MainRecyclerViewHolder.kt
@@ -1,78 +1,29 @@
package app.marcdev.earworm.mainscreen.mainrecycler
-import android.app.Dialog
-import android.graphics.Color
-import android.graphics.drawable.ColorDrawable
-import android.preference.PreferenceManager
import android.view.View
import androidx.recyclerview.widget.RecyclerView
-import app.marcdev.earworm.R
-import app.marcdev.earworm.database.FavouriteItem
-import app.marcdev.earworm.mainscreen.MainFragmentPresenter
-import com.google.android.material.button.MaterialButton
-import com.google.android.material.snackbar.Snackbar
-import timber.log.Timber
+import app.marcdev.earworm.data.database.FavouriteItem
-open class MainRecyclerViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
+open class MainRecyclerViewHolder(itemView: View,
+ private val itemClick: () -> Unit,
+ private val itemLongClick: (FavouriteItem) -> Unit)
+ : RecyclerView.ViewHolder(itemView) {
- private lateinit var displayedItem: FavouriteItem
- private lateinit var presenter: MainFragmentPresenter
- private lateinit var editDialog: Dialog
- private val prefs = PreferenceManager.getDefaultSharedPreferences(itemView.context)
-
- private val snackbarActionListener = View.OnClickListener {
- Timber.d("Log: snackbarActionListener: Clicked")
- prefs.edit().putBoolean("pref_show_tips", false).apply()
- }
-
- private val itemClickListener = View.OnClickListener {
- if(prefs.getBoolean("pref_show_tips", true)) {
- val snackbar = Snackbar.make(it, itemView.resources.getString(R.string.long_click_hint), Snackbar.LENGTH_SHORT)
- snackbar.setAction(itemView.resources.getString(R.string.dont_show), snackbarActionListener)
- snackbar.show()
- }
- }
-
- private val itemLongClickListener = View.OnLongClickListener {
- editDialog.show()
- return@OnLongClickListener true
- }
-
- private val editOnClickListener = View.OnClickListener {
- Timber.d("Log: editOnClickListener: Clicked")
- editDialog.dismiss()
- presenter.editItemClick(displayedItem.id!!)
- }
-
- private val deleteOnClickListener = View.OnClickListener {
- Timber.d("Log: deleteOnClickListener: Clicked")
- presenter.deleteItem(displayedItem)
- editDialog.dismiss()
- }
+ protected var displayedItem: FavouriteItem? = null
init {
- initEditDialog()
- itemView.setOnClickListener(itemClickListener)
- itemView.setOnLongClickListener(itemLongClickListener)
+ itemView.setOnClickListener {
+ itemClick
+ }
+ itemView.setOnLongClickListener {
+ displayedItem?.let { item ->
+ itemLongClick(item)
+ }
+ true
+ }
}
open fun display(favouriteItemToDisplay: FavouriteItem) {
// TO BE OVERRIDDEN
}
-
- fun bindPresenterAndItem(presenter: MainFragmentPresenter, item: FavouriteItem) {
- this.presenter = presenter
- this.displayedItem = item
- }
-
- private fun initEditDialog() {
- this.editDialog = Dialog(itemView.context)
- editDialog.setContentView(R.layout.dialog_edit_or_delete)
- editDialog.window!!.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
-
- val editButton = editDialog.findViewById(R.id.btn_add_or_delete_edit)
- editButton.setOnClickListener(editOnClickListener)
- val deleteButton = editDialog.findViewById(R.id.btn_add_or_delete_delete)
- deleteButton.setOnClickListener(deleteOnClickListener)
- }
}
\ No newline at end of file
diff --git a/app/src/main/java/app/marcdev/earworm/mainscreen/mainrecycler/MainRecyclerViewHolderAlbum.kt b/app/src/main/java/app/marcdev/earworm/mainscreen/mainrecycler/MainRecyclerViewHolderAlbum.kt
index b883c48..61a247e 100644
--- a/app/src/main/java/app/marcdev/earworm/mainscreen/mainrecycler/MainRecyclerViewHolderAlbum.kt
+++ b/app/src/main/java/app/marcdev/earworm/mainscreen/mainrecycler/MainRecyclerViewHolderAlbum.kt
@@ -4,14 +4,22 @@ import android.view.View
import android.widget.ImageView
import android.widget.TextView
import app.marcdev.earworm.R
-import app.marcdev.earworm.database.FavouriteItem
+import app.marcdev.earworm.data.database.FavouriteItem
+import app.marcdev.earworm.utils.FileUtils
import app.marcdev.earworm.utils.formatDateForDisplay
-import app.marcdev.earworm.utils.getArtworkDirectory
import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions
-import timber.log.Timber
+import org.kodein.di.Kodein
+import org.kodein.di.KodeinAware
+import org.kodein.di.android.closestKodein
+import org.kodein.di.generic.instance
-class MainRecyclerViewHolderAlbum(itemView: View) : MainRecyclerViewHolder(itemView) {
+class MainRecyclerViewHolderAlbum(itemView: View,
+ itemClick: () -> Unit,
+ itemLongClick: (FavouriteItem) -> Unit)
+ : MainRecyclerViewHolder(itemView, itemClick, itemLongClick), KodeinAware {
+ override val kodein: Kodein by closestKodein(itemView.context)
+ private val fileUtils: FileUtils by instance()
private val albumNameDisplay: TextView = itemView.findViewById(R.id.txt_albumName)
private val albumDateDisplay: TextView = itemView.findViewById(R.id.txt_albumDate)
@@ -19,23 +27,19 @@ class MainRecyclerViewHolderAlbum(itemView: View) : MainRecyclerViewHolder(itemV
private var albumImageDisplay: ImageView = itemView.findViewById(R.id.img_album_icon)
override fun display(favouriteItemToDisplay: FavouriteItem) {
- Timber.d("Log: display: $favouriteItemToDisplay")
+ displayedItem = favouriteItemToDisplay
albumNameDisplay.text = favouriteItemToDisplay.albumName
albumArtistDisplay.text = favouriteItemToDisplay.artistName
val date = formatDateForDisplay(favouriteItemToDisplay.day, favouriteItemToDisplay.month, favouriteItemToDisplay.year)
albumDateDisplay.text = date
if(favouriteItemToDisplay.imageName.isNotBlank()) {
- Timber.d("Log: display: ${favouriteItemToDisplay.imageName}")
-
Glide.with(itemView)
- .load(getArtworkDirectory(itemView.context) + favouriteItemToDisplay.imageName)
+ .load(fileUtils.artworkDirectory + favouriteItemToDisplay.imageName)
.apply(RequestOptions().centerCrop())
.apply(RequestOptions().error(itemView.resources.getDrawable(R.drawable.ic_error_24px, null)))
.into(albumImageDisplay)
} else {
- Timber.d("Log: display: No image to display")
-
Glide.with(itemView)
.load(itemView.resources.getDrawable(R.drawable.ic_album_24px, null))
.apply(RequestOptions().centerCrop())
diff --git a/app/src/main/java/app/marcdev/earworm/mainscreen/mainrecycler/MainRecyclerViewHolderArtist.kt b/app/src/main/java/app/marcdev/earworm/mainscreen/mainrecycler/MainRecyclerViewHolderArtist.kt
index d733aee..f617ceb 100644
--- a/app/src/main/java/app/marcdev/earworm/mainscreen/mainrecycler/MainRecyclerViewHolderArtist.kt
+++ b/app/src/main/java/app/marcdev/earworm/mainscreen/mainrecycler/MainRecyclerViewHolderArtist.kt
@@ -4,14 +4,22 @@ import android.view.View
import android.widget.ImageView
import android.widget.TextView
import app.marcdev.earworm.R
-import app.marcdev.earworm.database.FavouriteItem
+import app.marcdev.earworm.data.database.FavouriteItem
+import app.marcdev.earworm.utils.FileUtils
import app.marcdev.earworm.utils.formatDateForDisplay
-import app.marcdev.earworm.utils.getArtworkDirectory
import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions
-import timber.log.Timber
+import org.kodein.di.Kodein
+import org.kodein.di.KodeinAware
+import org.kodein.di.android.closestKodein
+import org.kodein.di.generic.instance
-class MainRecyclerViewHolderArtist(itemView: View) : MainRecyclerViewHolder(itemView) {
+class MainRecyclerViewHolderArtist(itemView: View,
+ itemClick: () -> Unit,
+ itemLongClick: (FavouriteItem) -> Unit)
+ : MainRecyclerViewHolder(itemView, itemClick, itemLongClick), KodeinAware {
+ override val kodein: Kodein by closestKodein(itemView.context)
+ private val fileUtils: FileUtils by instance()
private val artistNameDisplay: TextView = itemView.findViewById(R.id.txt_artistName)
private val artistGenreDisplay: TextView = itemView.findViewById(R.id.txt_artistGenre)
@@ -19,23 +27,19 @@ class MainRecyclerViewHolderArtist(itemView: View) : MainRecyclerViewHolder(item
private val artistImageDisplay: ImageView = itemView.findViewById(R.id.img_artist_icon)
override fun display(favouriteItemToDisplay: FavouriteItem) {
- Timber.d("Log: display: $favouriteItemToDisplay")
+ displayedItem = favouriteItemToDisplay
artistNameDisplay.text = favouriteItemToDisplay.artistName
artistGenreDisplay.text = favouriteItemToDisplay.genre
val date = formatDateForDisplay(favouriteItemToDisplay.day, favouriteItemToDisplay.month, favouriteItemToDisplay.year)
artistDateDisplay.text = date
if(favouriteItemToDisplay.imageName.isNotBlank()) {
- Timber.d("Log: display: imageName = ${favouriteItemToDisplay.imageName}")
-
Glide.with(itemView)
- .load(getArtworkDirectory(itemView.context) + favouriteItemToDisplay.imageName)
+ .load(fileUtils.artworkDirectory + favouriteItemToDisplay.imageName)
.apply(RequestOptions().centerCrop())
.apply(RequestOptions().error(itemView.resources.getDrawable(R.drawable.ic_error_24px, null)))
.into(artistImageDisplay)
} else {
- Timber.d("Log: display: No image to display")
-
Glide.with(itemView)
.load(itemView.resources.getDrawable(R.drawable.ic_person_24px, null))
.apply(RequestOptions().centerCrop())
diff --git a/app/src/main/java/app/marcdev/earworm/mainscreen/mainrecycler/MainRecyclerViewHolderGenre.kt b/app/src/main/java/app/marcdev/earworm/mainscreen/mainrecycler/MainRecyclerViewHolderGenre.kt
index b51afc8..9763f6a 100644
--- a/app/src/main/java/app/marcdev/earworm/mainscreen/mainrecycler/MainRecyclerViewHolderGenre.kt
+++ b/app/src/main/java/app/marcdev/earworm/mainscreen/mainrecycler/MainRecyclerViewHolderGenre.kt
@@ -3,17 +3,23 @@ package app.marcdev.earworm.mainscreen.mainrecycler
import android.view.View
import android.widget.TextView
import app.marcdev.earworm.R
-import app.marcdev.earworm.database.FavouriteItem
+import app.marcdev.earworm.data.database.FavouriteItem
import app.marcdev.earworm.utils.formatDateForDisplay
-import timber.log.Timber
+import org.kodein.di.Kodein
+import org.kodein.di.KodeinAware
+import org.kodein.di.android.closestKodein
-class MainRecyclerViewHolderGenre(itemView: View) : MainRecyclerViewHolder(itemView) {
+class MainRecyclerViewHolderGenre(itemView: View,
+ itemClick: () -> Unit,
+ itemLongClick: (FavouriteItem) -> Unit)
+ : MainRecyclerViewHolder(itemView, itemClick, itemLongClick), KodeinAware {
+ override val kodein: Kodein by closestKodein(itemView.context)
private var genreNameDisplay: TextView = itemView.findViewById(R.id.txt_genreName)
private var genreDateDisplay: TextView = itemView.findViewById(R.id.txt_genreDate)
override fun display(favouriteItemToDisplay: FavouriteItem) {
- Timber.d("Log: display: $favouriteItemToDisplay")
+ displayedItem = favouriteItemToDisplay
genreNameDisplay.text = favouriteItemToDisplay.genre
val date = formatDateForDisplay(favouriteItemToDisplay.day, favouriteItemToDisplay.month, favouriteItemToDisplay.year)
genreDateDisplay.text = date
diff --git a/app/src/main/java/app/marcdev/earworm/mainscreen/mainrecycler/MainRecyclerViewHolderHeader.kt b/app/src/main/java/app/marcdev/earworm/mainscreen/mainrecycler/MainRecyclerViewHolderHeader.kt
index 19420dc..ee1d48b 100644
--- a/app/src/main/java/app/marcdev/earworm/mainscreen/mainrecycler/MainRecyclerViewHolderHeader.kt
+++ b/app/src/main/java/app/marcdev/earworm/mainscreen/mainrecycler/MainRecyclerViewHolderHeader.kt
@@ -3,11 +3,10 @@ package app.marcdev.earworm.mainscreen.mainrecycler
import android.view.View
import android.widget.TextView
import app.marcdev.earworm.R
-import app.marcdev.earworm.database.FavouriteItem
-import app.marcdev.earworm.utils.getMonthName
-import timber.log.Timber
+import app.marcdev.earworm.data.database.FavouriteItem
+import app.marcdev.earworm.utils.formatDateForHeaderDisplay
-open class MainRecyclerViewHolderHeader(itemView: View) : MainRecyclerViewHolder(itemView) {
+open class MainRecyclerViewHolderHeader(itemView: View) : MainRecyclerViewHolder(itemView, {}, {}) {
private var dateDisplay: TextView = itemView.findViewById(R.id.txt_header_title)
@@ -26,8 +25,6 @@ open class MainRecyclerViewHolderHeader(itemView: View) : MainRecyclerViewHolder
}
override fun display(favouriteItemToDisplay: FavouriteItem) {
- Timber.d("Log: display: $favouriteItemToDisplay")
-
- dateDisplay.text = ("${getMonthName(favouriteItemToDisplay.month, itemView.context)} ${favouriteItemToDisplay.year}")
+ dateDisplay.text = formatDateForHeaderDisplay(favouriteItemToDisplay.month, favouriteItemToDisplay.year)
}
}
diff --git a/app/src/main/java/app/marcdev/earworm/mainscreen/mainrecycler/MainRecyclerViewHolderSong.kt b/app/src/main/java/app/marcdev/earworm/mainscreen/mainrecycler/MainRecyclerViewHolderSong.kt
index 0611378..565b741 100644
--- a/app/src/main/java/app/marcdev/earworm/mainscreen/mainrecycler/MainRecyclerViewHolderSong.kt
+++ b/app/src/main/java/app/marcdev/earworm/mainscreen/mainrecycler/MainRecyclerViewHolderSong.kt
@@ -4,14 +4,22 @@ import android.view.View
import android.widget.ImageView
import android.widget.TextView
import app.marcdev.earworm.R
-import app.marcdev.earworm.database.FavouriteItem
+import app.marcdev.earworm.data.database.FavouriteItem
+import app.marcdev.earworm.utils.FileUtils
import app.marcdev.earworm.utils.formatDateForDisplay
-import app.marcdev.earworm.utils.getArtworkDirectory
import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions
-import timber.log.Timber
+import org.kodein.di.Kodein
+import org.kodein.di.KodeinAware
+import org.kodein.di.android.closestKodein
+import org.kodein.di.generic.instance
-class MainRecyclerViewHolderSong(itemView: View) : MainRecyclerViewHolder(itemView) {
+class MainRecyclerViewHolderSong(itemView: View,
+ itemClick: () -> Unit,
+ itemLongClick: (FavouriteItem) -> Unit)
+ : MainRecyclerViewHolder(itemView, itemClick, itemLongClick), KodeinAware {
+ override val kodein: Kodein by closestKodein(itemView.context)
+ private val fileUtils: FileUtils by instance()
private val songNameDisplay: TextView = itemView.findViewById(R.id.txt_songName)
private val songDateDisplay: TextView = itemView.findViewById(R.id.txt_songDate)
@@ -19,22 +27,19 @@ class MainRecyclerViewHolderSong(itemView: View) : MainRecyclerViewHolder(itemVi
private val songImageDisplay: ImageView = itemView.findViewById(R.id.img_song_icon)
override fun display(favouriteItemToDisplay: FavouriteItem) {
- Timber.d("Log: display: $favouriteItemToDisplay")
+ displayedItem = favouriteItemToDisplay
songNameDisplay.text = favouriteItemToDisplay.songName
songArtistDisplay.text = favouriteItemToDisplay.artistName
val date = formatDateForDisplay(favouriteItemToDisplay.day, favouriteItemToDisplay.month, favouriteItemToDisplay.year)
songDateDisplay.text = date
if(favouriteItemToDisplay.imageName.isNotBlank()) {
- Timber.d("Log: display: ${favouriteItemToDisplay.imageName}")
Glide.with(itemView)
- .load(getArtworkDirectory(itemView.context) + favouriteItemToDisplay.imageName)
+ .load(fileUtils.artworkDirectory + favouriteItemToDisplay.imageName)
.apply(RequestOptions().centerCrop())
.apply(RequestOptions().error(itemView.resources.getDrawable(R.drawable.ic_error_24px, null)))
.into(songImageDisplay)
} else {
- Timber.d("Log: display: No image to display")
-
Glide.with(itemView)
.load(itemView.resources.getDrawable(R.drawable.ic_music_note_24px, null))
.apply(RequestOptions().centerCrop())
diff --git a/app/src/main/java/app/marcdev/earworm/repository/FavouriteItemRepository.kt b/app/src/main/java/app/marcdev/earworm/repository/FavouriteItemRepository.kt
deleted file mode 100644
index 1b9cebe..0000000
--- a/app/src/main/java/app/marcdev/earworm/repository/FavouriteItemRepository.kt
+++ /dev/null
@@ -1,16 +0,0 @@
-package app.marcdev.earworm.repository
-
-import app.marcdev.earworm.database.FavouriteItem
-
-interface FavouriteItemRepository {
-
- suspend fun insertOrUpdateItem(item: FavouriteItem)
-
- suspend fun getAllItems(): MutableList
-
- suspend fun getItem(id: Int): MutableList
-
- suspend fun deleteItem(id: Int)
-
- suspend fun countUsesOfImage(imageName: String): Int
-}
\ No newline at end of file
diff --git a/app/src/main/java/app/marcdev/earworm/repository/FavouriteItemRepositoryImpl.kt b/app/src/main/java/app/marcdev/earworm/repository/FavouriteItemRepositoryImpl.kt
deleted file mode 100644
index 80407c4..0000000
--- a/app/src/main/java/app/marcdev/earworm/repository/FavouriteItemRepositoryImpl.kt
+++ /dev/null
@@ -1,27 +0,0 @@
-package app.marcdev.earworm.repository
-
-import app.marcdev.earworm.database.DAO
-import app.marcdev.earworm.database.FavouriteItem
-
-class FavouriteItemRepositoryImpl(private val dao: DAO) : FavouriteItemRepository {
-
- override suspend fun insertOrUpdateItem(item: FavouriteItem) {
- return dao.insertOrUpdateItem(item)
- }
-
- override suspend fun getAllItems(): MutableList {
- return dao.getAllItems()
- }
-
- override suspend fun getItem(id: Int): MutableList {
- return dao.getItemById(id)
- }
-
- override suspend fun deleteItem(id: Int) {
- return dao.deleteItemById(id)
- }
-
- override suspend fun countUsesOfImage(imageName: String): Int {
- return dao.getNumberOfEntriesUsingImage(imageName)
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/app/marcdev/earworm/settingsscreen/LicensesActivity.kt b/app/src/main/java/app/marcdev/earworm/settingsscreen/LicensesActivity.kt
index d910e87..8fada0a 100644
--- a/app/src/main/java/app/marcdev/earworm/settingsscreen/LicensesActivity.kt
+++ b/app/src/main/java/app/marcdev/earworm/settingsscreen/LicensesActivity.kt
@@ -4,33 +4,28 @@ import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.view.View
-import android.widget.ImageButton
+import android.widget.ImageView
import android.widget.TextView
-import androidx.appcompat.app.AppCompatActivity
import androidx.cardview.widget.CardView
import app.marcdev.earworm.R
-import app.marcdev.earworm.utils.DARK_THEME
-import app.marcdev.earworm.utils.changeColorOfImageButtonDrawable
+import app.marcdev.earworm.internal.DARK_THEME
+import app.marcdev.earworm.internal.base.EarwormActivity
+import app.marcdev.earworm.utils.changeColorOfImageViewDrawable
import app.marcdev.earworm.utils.getTheme
-import timber.log.Timber
-class LicensesActivity : AppCompatActivity() {
+class LicensesActivity : EarwormActivity() {
private var isDarkMode: Boolean = false
override fun onCreate(savedInstanceState: Bundle?) {
- Timber.d("Log: onCreate: Started")
-
/* Theme changes must be done before super.onCreate otherwise it will be overridden with the value
in the manifest */
- if(getTheme(applicationContext) == DARK_THEME) {
- Timber.v("Log: onCreate: Is dark mode")
+ isDarkMode = if(getTheme(applicationContext) == DARK_THEME) {
setTheme(R.style.Earworm_DarkTheme)
- isDarkMode = true
+ true
} else {
- Timber.v("Log: onCreate: Is not dark mode")
setTheme(R.style.Earworm_LightTheme)
- isDarkMode = false
+ false
}
super.onCreate(savedInstanceState)
@@ -40,11 +35,10 @@ class LicensesActivity : AppCompatActivity() {
}
private fun bindViews() {
- Timber.v("Log: bindViews: Started")
- val backButton = findViewById(R.id.img_backFromSettings)
+ val backButton = findViewById(R.id.img_backFromSettings)
backButton.setOnClickListener(backOnClickListener)
if(isDarkMode) {
- changeColorOfImageButtonDrawable(applicationContext, backButton, false)
+ changeColorOfImageViewDrawable(applicationContext, backButton, false)
}
val toolbarTitle = findViewById(R.id.txt_settingsToolbarTitle)
@@ -68,12 +62,10 @@ class LicensesActivity : AppCompatActivity() {
}
private val backOnClickListener = View.OnClickListener {
- Timber.d("Log: backClick: Started")
finish()
}
private val glideOnClickListener = View.OnClickListener {
- Timber.d("Log: glideClick: Started")
val uriUrl = Uri.parse("https://github.com/bumptech/glide")
val launchBrowser = Intent(Intent.ACTION_VIEW)
launchBrowser.data = uriUrl
@@ -81,7 +73,6 @@ class LicensesActivity : AppCompatActivity() {
}
private val timberOnClickListener = View.OnClickListener {
- Timber.d("Log: timberClick: Started")
val uriUrl = Uri.parse("https://github.com/JakeWharton/timber")
val launchBrowser = Intent(Intent.ACTION_VIEW)
launchBrowser.data = uriUrl
@@ -89,7 +80,6 @@ class LicensesActivity : AppCompatActivity() {
}
private val materialIconsOnClickListener = View.OnClickListener {
- Timber.d("Log: materialIconsClick: Started")
val uriUrl = Uri.parse("https://github.com/google/material-design-icons")
val launchBrowser = Intent(Intent.ACTION_VIEW)
launchBrowser.data = uriUrl
@@ -97,7 +87,6 @@ class LicensesActivity : AppCompatActivity() {
}
private val materialComponentsOnClickListener = View.OnClickListener {
- Timber.d("Log: materialComponentsClick: Started")
val uriUrl = Uri.parse("https://github.com/material-components/material-components-android")
val launchBrowser = Intent(Intent.ACTION_VIEW)
launchBrowser.data = uriUrl
@@ -105,7 +94,6 @@ class LicensesActivity : AppCompatActivity() {
}
private val filePickerOnClickListener = View.OnClickListener {
- Timber.d("Log: filePickerClick: Started")
val uriUrl = Uri.parse("https://github.com/DroidNinja/Android-FilePicker")
val launchBrowser = Intent(Intent.ACTION_VIEW)
launchBrowser.data = uriUrl
diff --git a/app/src/main/java/app/marcdev/earworm/settingsscreen/SettingsActivity.kt b/app/src/main/java/app/marcdev/earworm/settingsscreen/SettingsActivity.kt
index d39ae06..2472d6b 100644
--- a/app/src/main/java/app/marcdev/earworm/settingsscreen/SettingsActivity.kt
+++ b/app/src/main/java/app/marcdev/earworm/settingsscreen/SettingsActivity.kt
@@ -2,25 +2,19 @@ package app.marcdev.earworm.settingsscreen
import android.os.Bundle
import android.view.View
-import android.widget.ImageButton
-import androidx.appcompat.app.AppCompatActivity
+import android.widget.ImageView
import app.marcdev.earworm.R
-import app.marcdev.earworm.utils.DARK_THEME
-import app.marcdev.earworm.utils.changeColorOfImageButtonDrawable
+import app.marcdev.earworm.internal.DARK_THEME
+import app.marcdev.earworm.internal.base.EarwormActivity
+import app.marcdev.earworm.utils.changeColorOfImageViewDrawable
import app.marcdev.earworm.utils.getTheme
-import app.marcdev.earworm.utils.setFragment
-import timber.log.Timber
-class SettingsActivity : AppCompatActivity() {
+class SettingsActivity : EarwormActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
- Timber.v("Log: onCreate: Started")
-
if(getTheme(applicationContext) == DARK_THEME) {
- Timber.v("Log: onCreate: Is dark mode")
setTheme(R.style.Earworm_DarkTheme)
} else {
- Timber.v("Log: onCreate: Is not dark mode")
setTheme(R.style.Earworm_LightTheme)
}
@@ -32,18 +26,15 @@ class SettingsActivity : AppCompatActivity() {
}
private fun bindViews() {
- Timber.v("Log: bindViews: Started")
- val backButton = findViewById(R.id.img_backFromSettings)
+ val backButton = findViewById(R.id.img_backFromSettings)
backButton.setOnClickListener(backOnClickListener)
if(getTheme(applicationContext) == DARK_THEME) {
- Timber.v("Log: bindViews: Converting to dark mode")
- changeColorOfImageButtonDrawable(applicationContext, backButton, false)
+ changeColorOfImageViewDrawable(applicationContext, backButton, false)
}
}
private val backOnClickListener = View.OnClickListener {
- Timber.d("Log: BackClick: Started")
this.finish()
}
}
\ No newline at end of file
diff --git a/app/src/main/java/app/marcdev/earworm/settingsscreen/SettingsFragment.kt b/app/src/main/java/app/marcdev/earworm/settingsscreen/SettingsFragment.kt
index ecc7014..a98306a 100644
--- a/app/src/main/java/app/marcdev/earworm/settingsscreen/SettingsFragment.kt
+++ b/app/src/main/java/app/marcdev/earworm/settingsscreen/SettingsFragment.kt
@@ -11,7 +11,8 @@ import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import app.marcdev.earworm.BuildConfig
import app.marcdev.earworm.R
-import app.marcdev.earworm.utils.*
+import app.marcdev.earworm.internal.*
+import app.marcdev.earworm.utils.changeColorOfDrawable
import timber.log.Timber
class SettingsFragment : PreferenceFragmentCompat() {
@@ -52,41 +53,35 @@ class SettingsFragment : PreferenceFragmentCompat() {
}
private val themeChangeListener = Preference.OnPreferenceChangeListener { preference, newValue ->
- Timber.d("Log: themeChangeListener: Theme changed to $newValue")
requireActivity().recreate()
matchSummaryToSelection(preference, newValue.toString())
true
}
private val clearInputsChangeListener = Preference.OnPreferenceChangeListener { preference, newValue ->
- Timber.d("Log: clearInputsChangeListener: Value changed to $newValue")
matchSummaryToSelection(preference, newValue.toString())
true
}
private val resetTipsListener = Preference.OnPreferenceClickListener {
- Timber.d("Log: ResetTipsClick: Clicked")
prefs.edit().putBoolean(PREF_SHOW_TIPS, true).apply()
Toast.makeText(requireContext(), resources.getString(R.string.reset_tips_confirmation), Toast.LENGTH_LONG).show()
return@OnPreferenceClickListener true
}
private val versionClickListener = Preference.OnPreferenceClickListener {
- Timber.d("Log: versionClick: Started")
val versionCodeString = resources.getString(R.string.build_code)
Toast.makeText(requireContext(), "$versionCodeString: ${BuildConfig.VERSION_CODE}", Toast.LENGTH_SHORT).show()
true
}
private val licensesOnClickListener = Preference.OnPreferenceClickListener {
- Timber.d("Log: licensesClick: Started")
val intent = Intent(requireContext(), LicensesActivity::class.java)
startActivity(intent)
true
}
private val githubOnClickListener = Preference.OnPreferenceClickListener {
- Timber.d("Log: githubClick: Started")
val uriUrl = Uri.parse("https://github.com/MarcDonald/Earworm")
val launchBrowser = Intent(Intent.ACTION_VIEW)
launchBrowser.data = uriUrl
@@ -95,15 +90,11 @@ class SettingsFragment : PreferenceFragmentCompat() {
}
private fun matchSummaryToSelection(preference: Preference, value: String) {
- Timber.d("Log: themeOnChangeListener: Started")
- Timber.d("Log: themeOnChangeListener: Value = $value")
-
if(preference is ListPreference) {
val index = preference.findIndexOfValue(value)
preference.setSummary(
if(index >= 0) {
- Timber.d("Log: BindPreferenceSummaryToValue: Setting summary to ${preference.entries[index]}")
preference.entries[index]
} else {
Timber.w("Log: BindPreferenceSummaryToValue: Index < 0")
@@ -111,7 +102,6 @@ class SettingsFragment : PreferenceFragmentCompat() {
})
} else {
- Timber.d("Log: BindPreferenceSummaryToValue: Setting summary to $value")
preference.summary = value
}
}
diff --git a/app/src/main/java/app/marcdev/earworm/uicomponents/AddItemDatePickerDialog.kt b/app/src/main/java/app/marcdev/earworm/uicomponents/AddItemDatePickerDialog.kt
new file mode 100644
index 0000000..a8fd6e7
--- /dev/null
+++ b/app/src/main/java/app/marcdev/earworm/uicomponents/AddItemDatePickerDialog.kt
@@ -0,0 +1,52 @@
+package app.marcdev.earworm.uicomponents
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.DatePicker
+import app.marcdev.earworm.R
+import app.marcdev.earworm.internal.base.EarwormDialogFragment
+import com.google.android.material.button.MaterialButton
+import java.util.*
+
+class AddItemDatePickerDialog(private val calendarArg: Calendar?,
+ private val okClick: (Int, Int, Int) -> Unit)
+ : EarwormDialogFragment() {
+
+ //
+ private lateinit var datePicker: DatePicker
+ private lateinit var cancelButton: MaterialButton
+ private lateinit var okButton: MaterialButton
+ //
+
+ private val day: Int
+ get() = datePicker.dayOfMonth
+ private val month: Int
+ get() = datePicker.month
+ private val year: Int
+ get() = datePicker.year
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ val view = inflater.inflate(R.layout.dialog_datepicker, container, false)
+ bindViews(view)
+ setDate()
+ return view
+ }
+
+ private fun bindViews(view: View) {
+ datePicker = view.findViewById(R.id.datepicker)
+ cancelButton = view.findViewById(R.id.btn_datepicker_cancel)
+ cancelButton.setOnClickListener { dismiss() }
+ okButton = view.findViewById(R.id.btn_datepicker_ok)
+ okButton.setOnClickListener {
+ okClick(day, month, year)
+ }
+ }
+
+ private fun setDate() {
+ calendarArg?.let { calendar ->
+ datePicker.updateDate(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), calendar.get(Calendar.DAY_OF_MONTH))
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/app/marcdev/earworm/uicomponents/BinaryOptionDialog.kt b/app/src/main/java/app/marcdev/earworm/uicomponents/BinaryOptionDialog.kt
new file mode 100644
index 0000000..049b655
--- /dev/null
+++ b/app/src/main/java/app/marcdev/earworm/uicomponents/BinaryOptionDialog.kt
@@ -0,0 +1,193 @@
+package app.marcdev.earworm.uicomponents
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import app.marcdev.earworm.R
+import app.marcdev.earworm.internal.base.EarwormDialogFragment
+import com.google.android.material.button.MaterialButton
+
+class BinaryOptionDialog : EarwormDialogFragment() {
+
+ // UI Components
+ private lateinit var negativeButton: MaterialButton
+ private lateinit var positiveButton: MaterialButton
+ private lateinit var titleDisplay: TextView
+ private lateinit var messageDisplay: TextView
+
+ // To set
+ private var negativeButtonText = ""
+ private var positiveButtonText = ""
+ private var titleText = ""
+ private var messageText = ""
+ private var negativeButtonOnClick: () -> Unit = {}
+ private var dismissAfterNegativeClick = true
+ private var positiveButtonOnClick: () -> Unit = {}
+ private var dismissAfterPositiveClick = true
+ private var isTitleVisible = true
+ private var isMessageVisible = true
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ val view = inflater.inflate(R.layout.dialog_binary_option, container, false)
+ bindViews(view)
+ setContent()
+ return view
+ }
+
+ private fun bindViews(view: View) {
+ negativeButton = view.findViewById(R.id.btn_binary_dialog_negative)
+ positiveButton = view.findViewById(R.id.btn_binary_dialog_positive)
+
+ titleDisplay = view.findViewById(R.id.txt_binary_dialog_title)
+ messageDisplay = view.findViewById(R.id.txt_binary_dialog_message)
+ }
+
+ private fun setContent() {
+ if(titleText.isNotBlank()) {
+ titleDisplay.text = titleText
+ }
+
+ if(messageText.isNotBlank()) {
+ messageDisplay.text = messageText
+ }
+
+ if(negativeButtonText.isNotBlank()) {
+ negativeButton.text = negativeButtonText
+ }
+
+ if(positiveButtonText.isNotBlank()) {
+ positiveButton.text = positiveButtonText
+ }
+
+ negativeButton.setOnClickListener {
+ negativeButtonOnClick()
+ if(dismissAfterNegativeClick)
+ dismiss()
+ }
+
+ positiveButton.setOnClickListener {
+ positiveButtonOnClick()
+ if(dismissAfterPositiveClick)
+ dismiss()
+ }
+
+ if(isTitleVisible)
+ titleDisplay.visibility = View.VISIBLE
+ else
+ titleDisplay.visibility = View.GONE
+
+ if(isMessageVisible)
+ messageDisplay.visibility = View.VISIBLE
+ else
+ messageDisplay.visibility = View.GONE
+ }
+
+ private fun setNegativeButton(text: String, onClick: () -> Unit, dismissAfter: Boolean) {
+ negativeButtonText = text
+ negativeButtonOnClick = onClick
+ dismissAfterNegativeClick = dismissAfter
+ }
+
+ private fun setPositiveButton(text: String, onClick: () -> Unit, dismissAfter: Boolean) {
+ positiveButtonText = text
+ positiveButtonOnClick = onClick
+ dismissAfterPositiveClick = dismissAfter
+ }
+
+ private fun setTitle(text: String) {
+ if(view != null) {
+ titleDisplay.text = text
+ } else {
+ titleText = text
+ }
+ }
+
+ private fun setMessage(text: String) {
+ if(view != null) {
+ messageDisplay.text = text
+ } else {
+ messageText = text
+ }
+ }
+
+ private fun setTitleVisibility(isVisible: Boolean) {
+ isTitleVisible = isVisible
+ }
+
+ private fun setMessageVisibility(isVisible: Boolean) {
+ isMessageVisible = isVisible
+ }
+
+ class Builder {
+ // To set
+ private var negativeButtonText = ""
+ private var positiveButtonText = ""
+ private var titleText = ""
+ private var messageText = ""
+ private var negativeButtonOnClick: () -> Unit = {}
+ private var dismissAfterNegativeClick = true
+ private var positiveButtonOnClick: () -> Unit = {}
+ private var dismissAfterPositiveClick = true
+ private var isTitleVisible = true
+ private var isMessageVisible = true
+
+ fun setTitle(text: String): Builder {
+ titleText = text
+ return this
+ }
+
+ fun setMessage(text: String): Builder {
+ messageText = text
+ return this
+ }
+
+ fun setNegativeButton(text: String, onClick: () -> Unit): Builder {
+ negativeButtonText = text
+ negativeButtonOnClick = onClick
+ return this
+ }
+
+ fun setPositiveButton(text: String, onClick: () -> Unit): Builder {
+ positiveButtonText = text
+ positiveButtonOnClick = onClick
+ return this
+ }
+
+ fun setNegativeButton(text: String, onClick: () -> Unit, dismissAfter: Boolean): Builder {
+ negativeButtonText = text
+ negativeButtonOnClick = onClick
+ dismissAfterNegativeClick = dismissAfter
+ return this
+ }
+
+ fun setPositiveButton(text: String, onClick: () -> Unit, dismissAfter: Boolean): Builder {
+ positiveButtonText = text
+ positiveButtonOnClick = onClick
+ dismissAfterPositiveClick = dismissAfter
+ return this
+ }
+
+ fun setTitleVisible(isVisible: Boolean): Builder {
+ isTitleVisible = isVisible
+ return this
+ }
+
+ fun setMessageVisible(isVisible: Boolean): Builder {
+ isMessageVisible = isVisible
+ return this
+ }
+
+ fun build(): BinaryOptionDialog {
+ val dialog = BinaryOptionDialog()
+ dialog.setTitle(titleText)
+ dialog.setMessage(messageText)
+ dialog.setNegativeButton(negativeButtonText, negativeButtonOnClick, dismissAfterNegativeClick)
+ dialog.setPositiveButton(positiveButtonText, positiveButtonOnClick, dismissAfterPositiveClick)
+ dialog.setTitleVisibility(isTitleVisible)
+ dialog.setMessageVisibility(isMessageVisible)
+ return dialog
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/app/marcdev/earworm/uicomponents/FilterDatePickerDialog.kt b/app/src/main/java/app/marcdev/earworm/uicomponents/FilterDatePickerDialog.kt
new file mode 100644
index 0000000..9867c79
--- /dev/null
+++ b/app/src/main/java/app/marcdev/earworm/uicomponents/FilterDatePickerDialog.kt
@@ -0,0 +1,74 @@
+package app.marcdev.earworm.uicomponents
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.DatePicker
+import app.marcdev.earworm.R
+import app.marcdev.earworm.internal.DEFAULT_FILTER
+import app.marcdev.earworm.internal.base.EarwormDialogFragment
+import com.google.android.material.button.MaterialButton
+import java.util.*
+
+class FilterDatePickerDialog(private val okClick: (Int, Int, Int) -> Unit,
+ private val isStart: Boolean,
+ private val calendar: Calendar?)
+ : EarwormDialogFragment() {
+
+ //
+ private lateinit var datePicker: DatePicker
+ private lateinit var cancelButton: MaterialButton
+ private lateinit var okButton: MaterialButton
+ private lateinit var startEndButton: MaterialButton
+ //
+
+ private val day: Int
+ get() = datePicker.dayOfMonth
+ private val month: Int
+ get() = datePicker.month
+ private val year: Int
+ get() = datePicker.year
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ val view = inflater.inflate(R.layout.dialog_datepicker_filter, container, false)
+ bindViews(view)
+ setDate()
+ return view
+ }
+
+ private fun bindViews(view: View) {
+ datePicker = view.findViewById(R.id.datepicker_filter)
+ cancelButton = view.findViewById(R.id.btn_datepicker_filter_cancel)
+ cancelButton.setOnClickListener { dismiss() }
+ okButton = view.findViewById(R.id.btn_datepicker_filter_ok)
+ okButton.setOnClickListener {
+ okClick(day, month, year)
+ }
+ startEndButton = view.findViewById(R.id.btn_datepicker_filter_start_end)
+ startEndButton.setOnClickListener {
+ if(isStart) {
+ okClick(DEFAULT_FILTER.startDay, DEFAULT_FILTER.startMonth, DEFAULT_FILTER.startYear)
+ } else {
+ okClick(DEFAULT_FILTER.endDay, DEFAULT_FILTER.endMonth, DEFAULT_FILTER.endYear)
+ }
+ resetDate()
+ }
+
+ if(isStart)
+ startEndButton.text = resources.getString(R.string.start)
+ else
+ startEndButton.text = resources.getString(R.string.end)
+ }
+
+ private fun resetDate() {
+ val today = Calendar.getInstance()
+ datePicker.updateDate(today.get(Calendar.YEAR), today.get(Calendar.MONTH), today.get(Calendar.DAY_OF_MONTH))
+ }
+
+ private fun setDate() {
+ calendar?.let { calendar ->
+ datePicker.updateDate(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), calendar.get(Calendar.DAY_OF_MONTH))
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/app/marcdev/earworm/uicomponents/FilterDialog.kt b/app/src/main/java/app/marcdev/earworm/uicomponents/FilterDialog.kt
index f1c2394..c17863f 100644
--- a/app/src/main/java/app/marcdev/earworm/uicomponents/FilterDialog.kt
+++ b/app/src/main/java/app/marcdev/earworm/uicomponents/FilterDialog.kt
@@ -1,161 +1,127 @@
package app.marcdev.earworm.uicomponents
-import android.app.Dialog
-import android.content.Context
-import android.graphics.Color
-import android.graphics.drawable.ColorDrawable
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
import android.widget.CheckBox
import android.widget.CompoundButton
-import android.widget.DatePicker
import app.marcdev.earworm.R
-import app.marcdev.earworm.mainscreen.MainFragmentPresenter
-import app.marcdev.earworm.utils.DEFAULT_FILTER
+import app.marcdev.earworm.internal.DEFAULT_FILTER
+import app.marcdev.earworm.internal.base.EarwormDialogFragment
import app.marcdev.earworm.utils.ItemFilter
import app.marcdev.earworm.utils.formatDateForDisplay
import com.google.android.material.button.MaterialButton
import com.google.android.material.chip.Chip
-import timber.log.Timber
import java.util.*
-class FilterDialog(context: Context, private val presenter: MainFragmentPresenter) : Dialog(context) {
+class FilterDialog(private val okClick: (ItemFilter) -> Unit) : EarwormDialogFragment() {
private lateinit var displaySongCheckbox: CheckBox
private lateinit var displayAlbumCheckbox: CheckBox
private lateinit var displayArtistCheckbox: CheckBox
private lateinit var startDateDisplay: Chip
private lateinit var endDateDisplay: Chip
- private lateinit var startDatePickerDialog: Dialog
- private lateinit var endDatePickerDialog: Dialog
- var activeFilter: ItemFilter = DEFAULT_FILTER.copy()
-
- init {
- Timber.d("Log: FilterDialog Init: Started")
- setContentView(R.layout.dialog_filter)
- window!!.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
- bindViews()
+ private lateinit var startDatePickerDialog: FilterDatePickerDialog
+ private lateinit var endDatePickerDialog: FilterDatePickerDialog
+ private var activeFilter: ItemFilter = DEFAULT_FILTER.copy()
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ val view = inflater.inflate(R.layout.dialog_filter, container, false)
+ bindViews(view)
initCheckboxes()
+ return view
}
- private fun bindViews() {
- Timber.d("Log: bindViews: Started")
-
- initStartDatePickerDialog()
- initEndDatePickerDialog()
-
- this.startDateDisplay = findViewById(R.id.chip_filter_start)
+ private fun bindViews(view: View) {
+ startDateDisplay = view.findViewById(R.id.chip_filter_start)
startDateDisplay.setOnClickListener {
- Timber.d("Log: startDateClickListener: Started")
- startDatePickerDialog.show()
+ startDatePickerDialog.show(requireFragmentManager(), "Start Date Picker")
}
- this.endDateDisplay = findViewById(R.id.chip_filter_end)
+ this.endDateDisplay = view.findViewById(R.id.chip_filter_end)
endDateDisplay.setOnClickListener {
- Timber.d("Log: endDateClickListener: Started")
- endDatePickerDialog.show()
+ endDatePickerDialog.show(requireFragmentManager(), "End Date Picker")
}
- this.displaySongCheckbox = findViewById(R.id.chk_filter_song)
+ this.displaySongCheckbox = view.findViewById(R.id.chk_filter_song)
displaySongCheckbox.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
activeFilter.includeSongs = isChecked
}
- this.displayAlbumCheckbox = findViewById(R.id.chk_filter_album)
+ this.displayAlbumCheckbox = view.findViewById(R.id.chk_filter_album)
displayAlbumCheckbox.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
activeFilter.includeAlbums = isChecked
}
- this.displayArtistCheckbox = findViewById(R.id.chk_filter_artist)
+ this.displayArtistCheckbox = view.findViewById(R.id.chk_filter_artist)
displayArtistCheckbox.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
activeFilter.includeArtists = isChecked
}
- val submitButton: MaterialButton = findViewById(R.id.btn_filter_ok)
+ val submitButton: MaterialButton = view.findViewById(R.id.btn_filter_ok)
submitButton.setOnClickListener {
- Timber.d("Log: submitButtonOnClickListener: Started")
- presenter.getAllItems(activeFilter)
+ okClick(activeFilter)
dismiss()
}
+
+ initStartDatePickerDialog()
+ initEndDatePickerDialog()
}
private fun initStartDatePickerDialog() {
- Timber.d("Log: initStartDatePickerDialog: Started")
-
- this.startDatePickerDialog = Dialog(context)
- startDatePickerDialog.setContentView(R.layout.dialog_datepicker_filter)
- startDatePickerDialog.window!!.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
-
- val datePicker: DatePicker = startDatePickerDialog.findViewById(R.id.datepicker_filter)
-
- val cancelButton: MaterialButton = startDatePickerDialog.findViewById(R.id.btn_datepicker_filter_cancel)
- cancelButton.setOnClickListener {
- Timber.d("Log: cancelButtonOnClickListener: Started")
- startDatePickerDialog.dismiss()
- }
-
- val okButton: MaterialButton = startDatePickerDialog.findViewById(R.id.btn_datepicker_filter_ok)
- okButton.setOnClickListener {
- Timber.d("Log: okButtonOnClickListener: Started")
- activeFilter.startDay = datePicker.dayOfMonth
- activeFilter.startMonth = datePicker.month
- activeFilter.startYear = datePicker.year
- startDateDisplay.text = formatDateForDisplay(datePicker.dayOfMonth, datePicker.month, datePicker.year)
- startDatePickerDialog.dismiss()
- }
-
- val startButton: MaterialButton = startDatePickerDialog.findViewById(R.id.btn_datepicker_filter_start_end)
- startButton.setOnClickListener {
- Timber.d("Log: startButtonOnClickListener: Started")
- activeFilter.startDay = DEFAULT_FILTER.startDay
- activeFilter.startMonth = DEFAULT_FILTER.startMonth
- activeFilter.startYear = DEFAULT_FILTER.startYear
- startDateDisplay.text = context.resources.getString(R.string.start)
- val todayCalendar = Calendar.getInstance()
- datePicker.updateDate(todayCalendar.get(Calendar.YEAR), todayCalendar.get(Calendar.MONTH), todayCalendar.get(Calendar.DAY_OF_MONTH))
- startDatePickerDialog.dismiss()
+ if(activeFilter.startDay == DEFAULT_FILTER.startDay && activeFilter.startMonth == DEFAULT_FILTER.startMonth && activeFilter.startYear == DEFAULT_FILTER.startYear) {
+ startDateDisplay.text = resources.getString(R.string.start)
+ this.startDatePickerDialog = FilterDatePickerDialog(::startDatePickerOkClick, true, null)
+ } else {
+ startDateDisplay.text = formatDateForDisplay(activeFilter.startDay, activeFilter.startMonth, activeFilter.startYear)
+ val calendar = Calendar.getInstance()
+ calendar.set(Calendar.DAY_OF_MONTH, activeFilter.startDay)
+ calendar.set(Calendar.MONTH, activeFilter.startMonth)
+ calendar.set(Calendar.YEAR, activeFilter.startYear)
+ this.startDatePickerDialog = FilterDatePickerDialog(::startDatePickerOkClick, true, calendar)
}
}
private fun initEndDatePickerDialog() {
- Timber.d("Log: initEndDatePickerDialog: Started")
-
- this.endDatePickerDialog = Dialog(context)
- endDatePickerDialog.setContentView(R.layout.dialog_datepicker_filter)
- endDatePickerDialog.window!!.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
-
- val datePicker: DatePicker = endDatePickerDialog.findViewById(R.id.datepicker_filter)
-
- val cancelButton: MaterialButton = endDatePickerDialog.findViewById(R.id.btn_datepicker_filter_cancel)
- cancelButton.setOnClickListener {
- Timber.d("Log: cancelButtonOnClickListener: Started")
- endDatePickerDialog.dismiss()
+ if(activeFilter.endDay == DEFAULT_FILTER.endDay && activeFilter.endMonth == DEFAULT_FILTER.endMonth && activeFilter.endYear == DEFAULT_FILTER.endYear) {
+ endDateDisplay.text = resources.getString(R.string.end)
+ this.endDatePickerDialog = FilterDatePickerDialog(::endDatePickerOkClick, false, null)
+ } else {
+ endDateDisplay.text = formatDateForDisplay(activeFilter.endDay, activeFilter.endMonth, activeFilter.endYear)
+ val calendar = Calendar.getInstance()
+ calendar.set(Calendar.DAY_OF_MONTH, activeFilter.endDay)
+ calendar.set(Calendar.MONTH, activeFilter.endMonth)
+ calendar.set(Calendar.YEAR, activeFilter.endYear)
+ this.endDatePickerDialog = FilterDatePickerDialog(::endDatePickerOkClick, false, calendar)
}
+ }
- val okButton: MaterialButton = endDatePickerDialog.findViewById(R.id.btn_datepicker_filter_ok)
- okButton.setOnClickListener {
- Timber.d("Log: okButtonOnClickListener: Started")
- activeFilter.endDay = datePicker.dayOfMonth
- activeFilter.endMonth = datePicker.month
- activeFilter.endYear = datePicker.year
- endDateDisplay.text = formatDateForDisplay(datePicker.dayOfMonth, datePicker.month, datePicker.year)
- endDatePickerDialog.dismiss()
+ private fun startDatePickerOkClick(day: Int, month: Int, year: Int) {
+ activeFilter.startDay = day
+ activeFilter.startMonth = month
+ activeFilter.startYear = year
+ if(day == DEFAULT_FILTER.startDay && month == DEFAULT_FILTER.startMonth && year == DEFAULT_FILTER.startYear) {
+ startDateDisplay.text = resources.getString(R.string.start)
+ } else {
+ startDateDisplay.text = formatDateForDisplay(activeFilter.startDay, activeFilter.startMonth, activeFilter.startYear)
}
+ startDatePickerDialog.dismiss()
+ }
- val endButton: MaterialButton = endDatePickerDialog.findViewById(R.id.btn_datepicker_filter_start_end)
- endButton.text = context.resources.getString(R.string.end)
- endButton.setOnClickListener {
- Timber.d("Log: startButtonOnClickListener: Started")
- activeFilter.endDay = DEFAULT_FILTER.endDay
- activeFilter.endMonth = DEFAULT_FILTER.endMonth
- activeFilter.endYear = DEFAULT_FILTER.endYear
- endDateDisplay.text = context.resources.getString(R.string.end)
- val todayCalendar = Calendar.getInstance()
- datePicker.updateDate(todayCalendar.get(Calendar.YEAR), todayCalendar.get(Calendar.MONTH), todayCalendar.get(Calendar.DAY_OF_MONTH))
- endDatePickerDialog.dismiss()
+ private fun endDatePickerOkClick(day: Int, month: Int, year: Int) {
+ activeFilter.endDay = day
+ activeFilter.endMonth = month
+ activeFilter.endYear = year
+ if(day == DEFAULT_FILTER.endDay && month == DEFAULT_FILTER.endMonth && year == DEFAULT_FILTER.endYear) {
+ endDateDisplay.text = resources.getString(R.string.end)
+ } else {
+ endDateDisplay.text = formatDateForDisplay(activeFilter.endDay, activeFilter.endMonth, activeFilter.endYear)
}
+ endDatePickerDialog.dismiss()
}
private fun initCheckboxes() {
- Timber.d("Log: initCheckboxes: Started")
displaySongCheckbox.isChecked = activeFilter.includeSongs
displayAlbumCheckbox.isChecked = activeFilter.includeAlbums
displayArtistCheckbox.isChecked = activeFilter.includeArtists
diff --git a/app/src/main/java/app/marcdev/earworm/utils/DateTimeUtils.kt b/app/src/main/java/app/marcdev/earworm/utils/DateTimeUtils.kt
new file mode 100644
index 0000000..81da9ca
--- /dev/null
+++ b/app/src/main/java/app/marcdev/earworm/utils/DateTimeUtils.kt
@@ -0,0 +1,43 @@
+package app.marcdev.earworm.utils
+
+import android.text.format.DateFormat
+import java.util.*
+
+/**
+ * Converts date to a format suitable for display
+ * @param day The day
+ * @param month The month (indexed at 0 the same as the Java calendar, so January is 0)
+ * @param year The year
+ */
+fun formatDateForDisplay(day: Int, month: Int, year: Int): String {
+ val calendar = Calendar.getInstance()
+ calendar.set(Calendar.DAY_OF_MONTH, day)
+ calendar.set(Calendar.MONTH, month)
+ calendar.set(Calendar.YEAR, year)
+ return formatDateForDisplay(calendar)
+}
+
+/**
+ * Converts date to a format suitable for display
+ * @param calendar Date to convert
+ */
+fun formatDateForDisplay(calendar: Calendar): String {
+ val datePattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), "ddMMyyyy")
+ val formattedDate = DateFormat.format(datePattern, calendar)
+ return formattedDate.toString()
+}
+
+/**
+ * Formats the date for displaying on a header
+ * @param month Integer value of the month, indexed at 0
+ * @param year Integer value of the year
+ * @return Full name of the month in string format
+ */
+fun formatDateForHeaderDisplay(month: Int, year: Int): String {
+ val calendar = Calendar.getInstance()
+ calendar.set(Calendar.MONTH, month)
+ calendar.set(Calendar.YEAR, year)
+ val datePattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), "MMMMyyyy")
+ val formattedDate = DateFormat.format(datePattern, calendar)
+ return formattedDate.toString()
+}
\ No newline at end of file
diff --git a/app/src/main/java/app/marcdev/earworm/utils/EarwormUtils.kt b/app/src/main/java/app/marcdev/earworm/utils/EarwormUtils.kt
index 9078c1f..f6f0f73 100644
--- a/app/src/main/java/app/marcdev/earworm/utils/EarwormUtils.kt
+++ b/app/src/main/java/app/marcdev/earworm/utils/EarwormUtils.kt
@@ -4,58 +4,13 @@ import android.content.Context
import android.graphics.PorterDuff
import android.graphics.drawable.Drawable
import android.preference.PreferenceManager
-import android.widget.ImageButton
-import androidx.fragment.app.Fragment
-import androidx.fragment.app.FragmentManager
+import android.widget.ImageView
import app.marcdev.earworm.R
-import timber.log.Timber
-
-// Song types
-const val SONG = 0
-const val ALBUM = 1
-const val ARTIST = 2
-const val GENRE = 3
-const val HEADER = 4
-
-val DEFAULT_FILTER = ItemFilter(1, 0, 1900, 31, 11, 2099, true, true, true, "")
-
-// Theme IDs
-const val LIGHT_THEME = 0
-const val DARK_THEME = 1
-
-// Preference keys
-const val PREF_THEME = "pref_theme"
-const val PREF_SHOW_TIPS = "pref_show_tips"
-const val PREF_BUILD_NUMBER = "pref_build_number"
-const val PREF_LICENSES = "pref_licenses"
-const val PREF_GITHUB = "pref_github"
-const val PREF_CLEAR_INPUTS = "pref_clear_inputs_on_type_change"
-
-/**
- * Replaces a fragment in a frame with another fragment
- * @param fragment The fragment to display
- * @param fragmentManager The Fragment Manager
- * @param frameId The ID of the frame to display the new fragment in
- */
-fun setFragment(fragment: Fragment, fragmentManager: FragmentManager, frameId: Int) {
- Timber.d("Log: setFragment: Replacing frame $frameId with fragment $fragment")
- val fragmentTransaction = fragmentManager.beginTransaction()
- fragmentTransaction.replace(frameId, fragment)
- fragmentTransaction.commit()
-}
-
-/**
- * Converts date to a format suitable for display
- * @param day The day
- * @param month The month (indexed at 0 the same as the Java calendar, so January is 0)
- * @param year The year
- */
-fun formatDateForDisplay(day: Int, month: Int, year: Int): String {
- Timber.d("Log: formatDateForDisplay: Started with day = $day, month = $month, year = $year")
- // Add 1 to month to make it non-zero indexed (January will now be 1 rather than 0)
- return "$day/${month + 1}/$year"
-}
+import app.marcdev.earworm.internal.DARK_THEME
+import app.marcdev.earworm.internal.LIGHT_THEME
+import app.marcdev.earworm.internal.PREF_THEME
+//
/**
* Changes the color of a drawable in an ImageView to indicate whether it is activated or not.
* Deactivated will change the color to either black or 70% white depending on the theme
@@ -63,9 +18,7 @@ fun formatDateForDisplay(day: Int, month: Int, year: Int): String {
* @param button The button to change the color of
* @param isActivated Whether or not the button should be put into the activated state
*/
-fun changeColorOfImageButtonDrawable(context: Context, button: ImageButton, isActivated: Boolean) {
- Timber.v("Log: changeColorOfImageButtonDrawable: Started")
-
+fun changeColorOfImageViewDrawable(context: Context, button: ImageView, isActivated: Boolean) {
when {
isActivated -> button.setColorFilter(context.getColor(R.color.colorAccent))
(getTheme(context) == DARK_THEME && !isActivated) -> button.setColorFilter(context.getColor(R.color.white60))
@@ -81,8 +34,6 @@ fun changeColorOfImageButtonDrawable(context: Context, button: ImageButton, isAc
* @param isActivated Whether or not the button should be put into the activated state
*/
fun changeColorOfDrawable(context: Context, drawable: Drawable, isActivated: Boolean) {
- Timber.v("Log: changeColorOfDrawable: Started")
-
when {
isActivated -> drawable.setColorFilter(context.getColor(R.color.colorAccent), PorterDuff.Mode.SRC_IN)
(getTheme(context) == DARK_THEME && !isActivated) -> drawable.setColorFilter((context.getColor(R.color.white60)), PorterDuff.Mode.SRC_IN)
@@ -90,39 +41,17 @@ fun changeColorOfDrawable(context: Context, drawable: Drawable, isActivated: Boo
}
}
-/**
- * Gets the full name of a month based on it's 0 indexed number
- * @param month Integer value of the month, indexed at 0
- * @param context Context
- * @return Full name of the month in string format
- */
-fun getMonthName(month: Int, context: Context): String {
- val monthArray = context.resources.getStringArray(R.array.months)
- return monthArray[month]
-}
-
-/**
- * Gets the path of the application's image storage
- * @param context Context
- */
-fun getArtworkDirectory(context: Context): String {
- Timber.d("Log: getArtworkDirectory: Started")
- val returnValue = context.filesDir.path + "/artwork/"
- Timber.d("Log: getArtworkDirectory: Returning $returnValue")
- return returnValue
-}
-
/**
* Checks the shared preferences to see if the user has selected dark mode
* @param context Context
*/
fun getTheme(context: Context): Int {
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
- val theme = prefs.getString("pref_theme", context.resources.getString(R.string.light))
- return when(theme) {
+ return when(prefs.getString(PREF_THEME, context.resources.getString(R.string.light))) {
context.resources.getString(R.string.light) -> LIGHT_THEME
context.resources.getString(R.string.dark) -> DARK_THEME
else -> -1
}
-}
\ No newline at end of file
+}
+//
\ No newline at end of file
diff --git a/app/src/main/java/app/marcdev/earworm/utils/FileUtils.kt b/app/src/main/java/app/marcdev/earworm/utils/FileUtils.kt
new file mode 100644
index 0000000..caed905
--- /dev/null
+++ b/app/src/main/java/app/marcdev/earworm/utils/FileUtils.kt
@@ -0,0 +1,11 @@
+package app.marcdev.earworm.utils
+
+import java.io.File
+
+interface FileUtils {
+ fun deleteImage(imageName: String)
+
+ fun saveImage(file: File)
+
+ val artworkDirectory: String
+}
\ No newline at end of file
diff --git a/app/src/main/java/app/marcdev/earworm/utils/FileUtilsImpl.kt b/app/src/main/java/app/marcdev/earworm/utils/FileUtilsImpl.kt
new file mode 100644
index 0000000..7395a2e
--- /dev/null
+++ b/app/src/main/java/app/marcdev/earworm/utils/FileUtilsImpl.kt
@@ -0,0 +1,35 @@
+package app.marcdev.earworm.utils
+
+import android.content.Context
+import timber.log.Timber
+import java.io.File
+
+class FileUtilsImpl(private val context: Context) : FileUtils {
+
+ override fun deleteImage(imageName: String) {
+ val filePath = artworkDirectory + imageName
+ val file = File(filePath)
+ if(file.exists()) {
+ file.delete()
+ } else {
+ Timber.w("Log: deleteImage: File doesn't exist")
+ }
+ }
+
+ override fun saveImage(file: File) {
+ val toPath = artworkDirectory + file.name
+ val toFile = File(toPath)
+ if(toFile.compareTo(file) != 0) {
+ try {
+ file.copyTo(toFile, true)
+ } catch(e: NoSuchFileException) {
+ Timber.e("Log: saveImage: $e")
+ }
+ } else {
+ Timber.d("Log: saveImage: No need to save as file already exists in storage")
+ }
+ }
+
+ override val artworkDirectory: String
+ get() = context.filesDir.path + "/artwork/"
+}
\ No newline at end of file
diff --git a/app/src/main/java/app/marcdev/earworm/utils/ListUtils.kt b/app/src/main/java/app/marcdev/earworm/utils/ListUtils.kt
deleted file mode 100644
index 563e2d4..0000000
--- a/app/src/main/java/app/marcdev/earworm/utils/ListUtils.kt
+++ /dev/null
@@ -1,140 +0,0 @@
-package app.marcdev.earworm.utils
-
-import app.marcdev.earworm.database.FavouriteItem
-import timber.log.Timber
-
-/**
- * Filters a list based on a filter input by the user
- * @param allItems The complete list of items from the database
- * @param filter The filter to apply
- * @return Filtered list
- */
-fun applyFilter(allItems: MutableList, filter: ItemFilter): MutableList {
- val filteredItems = mutableListOf()
- filteredItems.addAll(allItems)
-
- val startDayTwoDigit = if(filter.startDay < 10)
- "0${filter.startDay}"
- else {
- "${filter.startDay}"
- }
-
- val startMonthTwoDigit = if(filter.startMonth < 10)
- "0${filter.startMonth}"
- else {
- "${filter.startMonth}"
- }
-
- val filterCompleteDateStart: Int = "${filter.startYear}$startMonthTwoDigit$startDayTwoDigit".toInt()
-
- val endDayTwoDigit = if(filter.endDay < 10)
- "0${filter.endDay}"
- else {
- "${filter.endDay}"
- }
-
- val endMonthTwoDigit = if(filter.endMonth < 10)
- "0${filter.endMonth}"
- else {
- "${filter.endMonth}"
- }
-
- val filterCompleteDateEnd: Int = "${filter.endYear}$endMonthTwoDigit$endDayTwoDigit".toInt()
-
- for(x in 0 until allItems.size) {
- if(allItems[x].type == SONG && !filter.includeSongs) {
- filteredItems.remove(allItems[x])
- }
-
- if(allItems[x].type == ALBUM && !filter.includeAlbums) {
- filteredItems.remove(allItems[x])
- }
-
- if(allItems[x].type == ARTIST && !filter.includeArtists) {
- filteredItems.remove(allItems[x])
- }
-
- val dayTwoDigit = if(allItems[x].day < 10)
- "0${allItems[x].day}"
- else {
- "${allItems[x].day}"
- }
-
- val monthTwoDigit = if(allItems[x].month < 10)
- "0${allItems[x].month}"
- else {
- "${allItems[x].month}"
- }
-
- val completeDate = "${allItems[x].year}$monthTwoDigit$dayTwoDigit"
- val completeDateI: Int = completeDate.toInt()
-
- if(completeDateI < filterCompleteDateStart || completeDateI > filterCompleteDateEnd) {
- filteredItems.remove(allItems[x])
- }
-
- if(!(allItems[x].albumName.contains(filter.searchTerm, true))
- && !(allItems[x].artistName.contains(filter.searchTerm, true))
- && !(allItems[x].songName.contains(filter.searchTerm, true))
- && !(allItems[x].genre.contains(filter.searchTerm, true))
- ) {
- filteredItems.remove(allItems[x])
- }
- }
-
- return sortByDateDescending(filteredItems)
-}
-
-/**
- * Sorts a list by date in descending order
- * @param items List to sort
- * @return Sorted list of FavouriteItems
- */
-fun sortByDateDescending(items: MutableList): MutableList {
- val filteredItems = items.sortedWith(
- compareBy(
- { -it.year },
- { -it.month },
- { -it.day },
- { -it.id!! }))
-
- return filteredItems.toMutableList()
-}
-
-/**
- * Adds header items to a list on a monthly basis
- * @param allItems List to add headers to (sorted by date descending)
- * @return List with headers every new month
- */
-fun addListHeaders(allItems: MutableList): List {
- val listWithHeaders = mutableListOf()
- listWithHeaders.addAll(allItems)
-
- var lastMonth = 12
- var lastYear = 9999
- if(allItems.isNotEmpty()) {
- lastMonth = allItems.first().month + 1
- lastYear = allItems.first().year
- }
-
- val headersToAdd = mutableListOf>()
-
- for(x in 0 until allItems.size) {
- if(((allItems[x].month < lastMonth) && (allItems[x].year == lastYear))
- || (allItems[x].month > lastMonth) && (allItems[x].year < lastYear)
- || (allItems[x].year < lastYear)
- ) {
- Timber.v("Log: addListHeaders: x = $x")
- val header = FavouriteItem("", "", "", "", 0, allItems[x].month, allItems[x].year, HEADER, "")
- lastMonth = allItems[x].month
- lastYear = allItems[x].year
- headersToAdd.add(Pair(x, header))
- }
- }
-
- for((add, x) in (0 until headersToAdd.size).withIndex()) {
- listWithHeaders.add(headersToAdd[x].first + add, headersToAdd[x].second)
- }
-
- return listWithHeaders
-}
diff --git a/app/src/main/res/drawable/rounded_dialog_background.xml b/app/src/main/res/drawable/rounded_dialog_background.xml
index 1f1014e..4f69b3d 100644
--- a/app/src/main/res/drawable/rounded_dialog_background.xml
+++ b/app/src/main/res/drawable/rounded_dialog_background.xml
@@ -1,11 +1,7 @@
-
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_datepicker.xml b/app/src/main/res/layout/dialog_datepicker.xml
index b91904d..e908d2b 100644
--- a/app/src/main/res/layout/dialog_datepicker.xml
+++ b/app/src/main/res/layout/dialog_datepicker.xml
@@ -2,8 +2,7 @@
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+ xmlns:app="http://schemas.android.com/apk/res-auto">
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_edit_or_delete.xml b/app/src/main/res/layout/dialog_edit_or_delete.xml
deleted file mode 100644
index 47ec010..0000000
--- a/app/src/main/res/layout/dialog_edit_or_delete.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_filter.xml b/app/src/main/res/layout/dialog_filter.xml
index cd957db..12148d0 100644
--- a/app/src/main/res/layout/dialog_filter.xml
+++ b/app/src/main/res/layout/dialog_filter.xml
@@ -1,7 +1,6 @@
@@ -187,7 +186,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/marginStandard"
- android:backgroundTint="@color/positive"
+ style="@style/PositiveButton"
android:text="@string/filter"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
diff --git a/app/src/main/res/layout/toolbar_filter.xml b/app/src/main/res/layout/toolbar_filter.xml
index 8900531..b939f3e 100644
--- a/app/src/main/res/layout/toolbar_filter.xml
+++ b/app/src/main/res/layout/toolbar_filter.xml
@@ -16,17 +16,17 @@
android:focusable="true"
android:focusableInTouchMode="true">
-
@@ -40,26 +40,26 @@
android:layout_marginTop="@dimen/toolbarIconMargin"
android:layout_marginEnd="@dimen/toolbarIconMarginSmall"
android:layout_marginBottom="@dimen/toolbarIconMargin"
- android:inputType="text"
- android:hint="@string/search"
android:autofillHints="@string/search"
+ android:hint="@string/search"
+ android:inputType="text"
android:singleLine="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/img_search"
app:layout_constraintStart_toEndOf="@id/img_filter"
app:layout_constraintTop_toTopOf="parent" />
-
diff --git a/app/src/main/res/layout/toolbar_main.xml b/app/src/main/res/layout/toolbar_main.xml
index c776ea8..4cb736e 100644
--- a/app/src/main/res/layout/toolbar_main.xml
+++ b/app/src/main/res/layout/toolbar_main.xml
@@ -1,44 +1,44 @@
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/toolbar_main"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/appBarHeight"
+ android:background="@color/colorPrimary"
+ app:contentInsetEnd="0dp"
+ app:contentInsetLeft="0dp"
+ app:contentInsetRight="0dp"
+ app:contentInsetStart="0dp"
+ app:layout_scrollFlags="scroll|snap|enterAlways">
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent" />
-
diff --git a/app/src/main/res/layout/toolbar_settings.xml b/app/src/main/res/layout/toolbar_settings.xml
index eb00be5..3419cb7 100644
--- a/app/src/main/res/layout/toolbar_settings.xml
+++ b/app/src/main/res/layout/toolbar_settings.xml
@@ -28,15 +28,15 @@
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
-
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index f71d525..944e43e 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -4,9 +4,10 @@
4dp
16dp
56dp
- 48dp
+ 36dp
16dp
8dp
+ 6dp
16dp
8dp
8dp
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 0b1a775..c9f255e 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -25,6 +25,8 @@
Shortcut Disabled
Yes
No
+ Title
+ Content
Song name
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index ab964e1..2894833 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -17,6 +17,8 @@
- @color/lightThemeColorPrimary
- @style/Earworm.LightToolbar
- @color/lightThemeColorPrimaryDark
+ - @color/positive
+ - @color/negative
@@ -39,6 +41,8 @@
- @color/darkThemeColorPrimaryDark
- @color/darkThemeColorPrimary
- ?attr/navigationBarColor
+ - @color/positive
+ - @color/negative
@@ -74,8 +78,20 @@
- ?attr/transparentButtonBackground
+
+
+
+
-
-