Skip to content

Commit

Permalink
Merge pull request #7 from alexmercerind/feat/history-search
Browse files Browse the repository at this point in the history
feat: search in `HistoryFragment`
  • Loading branch information
alexmercerind authored Nov 28, 2023
2 parents 4b7e84b + 078b851 commit 32967b1
Show file tree
Hide file tree
Showing 22 changed files with 378 additions and 102 deletions.
17 changes: 17 additions & 0 deletions .idea/deploymentTargetDropDown.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 8 additions & 5 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ android {
applicationId = "com.alexmercerind.audire"
minSdk = 21
targetSdk = 34
versionCode = 1
versionName = "1.0"
versionCode = 2
versionName = "1.1"
}

buildFeatures {
Expand All @@ -22,12 +22,14 @@ android {

buildTypes {
release {
isDebuggable = false
isMinifyEnabled = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"
)
signingConfig = signingConfigs.getByName("debug")
}
debug {
isDebuggable = true
}
}
compileOptions {
Expand All @@ -50,6 +52,7 @@ dependencies {
implementation("androidx.navigation:navigation-ui-ktx:$navVersion")

val roomVersion = "2.6.0"
implementation("androidx.room:room-ktx:$roomVersion")
implementation("androidx.room:room-runtime:$roomVersion")
annotationProcessor("androidx.room:room-compiler:$roomVersion")
ksp("androidx.room:room-compiler:$roomVersion")
Expand Down
Binary file added app/release/app-release.aab
Binary file not shown.
Binary file added app/release/app-release.apk
Binary file not shown.
20 changes: 20 additions & 0 deletions app/release/output-metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"version": 3,
"artifactType": {
"type": "APK",
"kind": "Directory"
},
"applicationId": "com.alexmercerind.audire",
"variantName": "release",
"elements": [
{
"type": "SINGLE",
"filters": [],
"attributes": [],
"versionCode": 1,
"versionName": "1.0",
"outputFile": "app-release.apk"
}
],
"elementType": "File"
}
3 changes: 2 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
android:exported="false" />
<activity
android:name=".ui.MainActivity"
android:exported="true">
android:exported="true"
android:windowSoftInputMode="adjustNothing">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@ import com.alexmercerind.audire.models.HistoryItem
import com.alexmercerind.audire.ui.HistoryViewModel
import com.alexmercerind.audire.ui.MusicActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch

@OptIn(DelicateCoroutinesApi::class)
class HistoryItemAdapter(
private val items: List<HistoryItem>, private val historyViewModel: HistoryViewModel
val items: List<HistoryItem>, private val historyViewModel: HistoryViewModel
) : RecyclerView.Adapter<HistoryItemAdapter.HistoryItemViewHolder>() {

inner class HistoryItemViewHolder(val binding: HistoryItemBinding) :
Expand Down Expand Up @@ -56,15 +58,15 @@ class HistoryItemAdapter(
MaterialAlertDialogBuilder(
root.context, R.style.Base_Theme_Audire_MaterialAlertDialog
).setTitle(R.string.remove_history_item_title).setMessage(
context.getString(
R.string.remove_history_item_message, items[position].title
)
).setPositiveButton(R.string.yes) { dialog, _ ->
dialog.dismiss()
GlobalScope.launch(Dispatchers.IO) { historyViewModel.delete(items[position]) }
}.setNegativeButton(R.string.no) { dialog, _ ->
dialog.dismiss()
}.show()
context.getString(
R.string.remove_history_item_message, items[position].title
)
).setPositiveButton(R.string.yes) { dialog, _ ->
dialog.dismiss()
GlobalScope.launch(Dispatchers.IO) { historyViewModel.delete(items[position]) }
}.setNegativeButton(R.string.no) { dialog, _ ->
dialog.dismiss()
}.show()
true
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package com.alexmercerind.audire.db

import androidx.lifecycle.LiveData
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import com.alexmercerind.audire.models.HistoryItem
import kotlinx.coroutines.flow.Flow

@Dao
interface HistoryItemDao {
Expand All @@ -17,5 +17,8 @@ interface HistoryItemDao {
fun delete(historyItem: HistoryItem)

@Query("SELECT * FROM history_item ORDER BY timestamp DESC")
fun getAll(): LiveData<List<HistoryItem>>
fun getAll(): Flow<List<HistoryItem>>

@Query("SELECT * FROM history_item WHERE LOWER(title) LIKE '%' || :term || '%' ORDER BY timestamp DESC")
suspend fun search(term: String): List<HistoryItem>
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,13 @@ import com.alexmercerind.audire.models.HistoryItem

class HistoryRepository(private val application: Application) {
fun insert(historyItem: HistoryItem) =
HistoryItemDatabase(application)
.historyItemDao()
.insert(historyItem)
HistoryItemDatabase(application).historyItemDao().insert(historyItem)

fun delete(historyItem: HistoryItem) =
HistoryItemDatabase(application)
.historyItemDao()
.delete(historyItem)
HistoryItemDatabase(application).historyItemDao().delete(historyItem)

fun getAll() =
HistoryItemDatabase(application)
.historyItemDao()
.getAll()
fun getAll() = HistoryItemDatabase(application).historyItemDao().getAll()

suspend fun search(term: String) =
HistoryItemDatabase(application).historyItemDao().search(term)
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class AboutActivity : AppCompatActivity() {
companion object {
private const val GITHUB = "https://github.com/alexmercerind/audire"
private const val LICENSE = "https://github.com/alexmercerind/audire/blob/main/LICENSE"
private const val PRIVACY = "https://github.com/alexmercerind/audire/blob/main/PRIVACY"
private const val PRIVACY = "https://github.com/alexmercerind/audire/wiki/Privacy-Policy-%5BPlay-Store%5D"

private const val DEVELOPER_GITHUB = "https://github.com/alexmercerind"
private const val DEVELOPER_X = "https://x.com/alexmercerind"
Expand Down
106 changes: 86 additions & 20 deletions app/src/main/java/com/alexmercerind/audire/ui/HistoryFragment.kt
Original file line number Diff line number Diff line change
@@ -1,62 +1,128 @@
package com.alexmercerind.audire.ui

import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.recyclerview.widget.LinearLayoutManager
import com.alexmercerind.audire.R
import com.alexmercerind.audire.adapters.HistoryItemAdapter
import com.alexmercerind.audire.databinding.FragmentHistoryBinding
import com.google.android.material.appbar.MaterialToolbar
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.launch

class HistoryFragment : Fragment() {

private var _binding: FragmentHistoryBinding? = null
private val binding get() = _binding!!

private val historyViewModel: HistoryViewModel by viewModels()

private lateinit var imm: InputMethodManager

private val watcher = object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}

override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}

override fun afterTextChanged(s: Editable?) {
historyViewModel.term = s.toString()
}
}

override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
): View? {
): View {
_binding = FragmentHistoryBinding.inflate(inflater, container, false)

binding.searchLinearLayout.visibility = View.GONE
binding.historyLinearLayout.visibility = View.GONE
binding.historyRecyclerView.visibility = View.GONE

binding.historyRecyclerView.layoutManager = LinearLayoutManager(context)

historyViewModel.getAll().observe(viewLifecycleOwner) {
imm = context?.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager

binding.historyRecyclerView.adapter = HistoryItemAdapter(it, historyViewModel)
binding.searchTextInputLayout.visibility = View.GONE

if (it.isEmpty()) {
binding.historyLinearLayout.visibility = View.VISIBLE
binding.historyRecyclerView.visibility = View.GONE
} else {
binding.historyLinearLayout.visibility = View.GONE
binding.historyRecyclerView.visibility = View.VISIBLE
}
binding.searchTextInputLayout.setEndIconOnClickListener {
binding.searchTextInputEditText.text?.clear()
binding.primaryMaterialToolbar.visibility = View.VISIBLE
binding.searchTextInputLayout.visibility = View.GONE
binding.searchTextInputLayout.clearFocus()
imm.hideSoftInputFromWindow(binding.root.windowToken, 0)
}

binding.root.findViewById<MaterialToolbar>(R.id.primaryMaterialToolbar).setOnMenuItemClickListener {
val intent = when (it.itemId) {
R.id.settings -> Intent(context, SettingsActivity::class.java)
R.id.about -> Intent(context, AboutActivity::class.java)
else -> null
viewLifecycleOwner.lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
historyViewModel.adapter.filterNotNull().collect {
if (it.items.isEmpty()) {
if (historyViewModel.term.isEmpty()) {
// No HistoryItem(s) by default.
binding.historyLinearLayout.visibility = View.VISIBLE
binding.searchLinearLayout.visibility = View.GONE
} else {
// No HistoryItem(s) due to search.
binding.historyLinearLayout.visibility = View.GONE
binding.searchLinearLayout.visibility = View.VISIBLE
}
binding.historyRecyclerView.visibility = View.GONE
} else {
// HistoryItem(s) are present i.e. RecyclerView must be VISIBLE.
binding.historyLinearLayout.visibility = View.GONE
binding.searchLinearLayout.visibility = View.GONE
binding.historyRecyclerView.visibility = View.VISIBLE
binding.historyRecyclerView.adapter = it
}
}
}
if (intent != null) {
startActivity(intent)
}


binding.primaryMaterialToolbar.setOnMenuItemClickListener {
if (it.itemId == R.id.search) {
binding.primaryMaterialToolbar.visibility = View.GONE
binding.searchTextInputLayout.visibility = View.VISIBLE
binding.searchTextInputLayout.requestFocus()
imm.showSoftInput(binding.searchTextInputEditText, 0)
} else {
val intent = when (it.itemId) {
R.id.settings -> Intent(context, SettingsActivity::class.java)
R.id.about -> Intent(context, AboutActivity::class.java)
else -> null
}
if (intent != null) {
startActivity(intent)
}
}
true
}

return binding.root
}

override fun onStart() {
super.onStart()
binding.searchTextInputEditText.addTextChangedListener(watcher)
}

override fun onStop() {
super.onStop()
binding.searchTextInputEditText.removeTextChangedListener(watcher)
binding.searchTextInputEditText.text?.clear()

historyViewModel.term = ""
}

override fun onDestroyView() {
super.onDestroyView()
_binding = null
Expand Down
Loading

0 comments on commit 32967b1

Please sign in to comment.