diff --git a/app/build.gradle b/app/build.gradle index 697573c..7fea453 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,5 +1,6 @@ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' +apply plugin: 'com.mikepenz.aboutlibraries.plugin' android { @@ -24,7 +25,6 @@ android { viewBinding true } - namespace 'de.msdevs.einschlafhilfe' compileOptions { @@ -50,6 +50,8 @@ dependencies { implementation("ru.gildor.coroutines:kotlin-coroutines-okhttp:1.0") implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.10" implementation 'com.github.AppIntro:AppIntro:6.3.1' + implementation "com.mikepenz:aboutlibraries-core:10.9.1" + implementation "com.mikepenz:aboutlibraries:10.9.1" } repositories { mavenCentral() diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e95e854..f760253 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -42,7 +42,12 @@ - + diff --git a/app/src/main/java/de/msdevs/einschlafhilfe/AboutActivity.kt b/app/src/main/java/de/msdevs/einschlafhilfe/AboutActivity.kt index 0b91b15..bfa90a9 100644 --- a/app/src/main/java/de/msdevs/einschlafhilfe/AboutActivity.kt +++ b/app/src/main/java/de/msdevs/einschlafhilfe/AboutActivity.kt @@ -52,6 +52,8 @@ class AboutActivity : BaseActivity() { opennUrl("https://github.com/MarvinStelter/DieDreiFragezeichenEinschlafhilfe") } btnLicenses.setOnClickListener{ + startActivity(Intent(this@AboutActivity, AboutLibrariesActivity::class.java)) + if(expandableLayout.isExpanded){ expandableLayout.collapse() }else{ diff --git a/app/src/main/java/de/msdevs/einschlafhilfe/AboutLibrariesActivity.kt b/app/src/main/java/de/msdevs/einschlafhilfe/AboutLibrariesActivity.kt new file mode 100644 index 0000000..560039a --- /dev/null +++ b/app/src/main/java/de/msdevs/einschlafhilfe/AboutLibrariesActivity.kt @@ -0,0 +1,18 @@ +package de.msdevs.einschlafhilfe +import android.os.Bundle +import com.mikepenz.aboutlibraries.LibsBuilder +import com.mikepenz.aboutlibraries.ui.LibsActivity + + +class AboutLibrariesActivity : LibsActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + intent = LibsBuilder() + .withEdgeToEdge(true) + .withAboutMinimalDesign(true) + .withActivityTitle(getString(R.string.about_libs)) + .withSearchEnabled(true) + .intent(this) + super.onCreate(savedInstanceState) + } + +} \ No newline at end of file diff --git a/app/src/main/java/de/msdevs/einschlafhilfe/FilterActivity.kt b/app/src/main/java/de/msdevs/einschlafhilfe/FilterActivity.kt index f6ca4d7..37ad12c 100644 --- a/app/src/main/java/de/msdevs/einschlafhilfe/FilterActivity.kt +++ b/app/src/main/java/de/msdevs/einschlafhilfe/FilterActivity.kt @@ -1,57 +1,79 @@ package de.msdevs.einschlafhilfe +import android.database.sqlite.SQLiteDatabase import android.os.Bundle import android.util.Log +import android.view.ScaleGestureDetector import android.view.View -import android.widget.AdapterView import android.widget.ArrayAdapter -import android.widget.AutoCompleteTextView +import android.widget.FrameLayout +import android.widget.HorizontalScrollView +import android.widget.ScrollView +import android.widget.TextView +import android.widget.Toast import androidx.lifecycle.lifecycleScope +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView import com.google.android.material.textfield.MaterialAutoCompleteTextView +import de.msdevs.einschlafhilfe.adapter.FilterListeAdapter +import de.msdevs.einschlafhilfe.database.DatabaseHelper import de.msdevs.einschlafhilfe.databinding.ActivityFilterBinding import de.msdevs.einschlafhilfe.models.JsonResponse import de.msdevs.einschlafhilfe.utils.NetworkUtils import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import okhttp3.OkHttpClient -import okhttp3.Request import org.json.JSONObject -import ru.gildor.coroutines.okhttp.await - - -class FilterActivity : BaseActivity(){ +import java.io.BufferedReader +class FilterActivity : BaseActivity(), FilterListeAdapter.OnDeleteItemListener{ + private lateinit var rvFilterListe : RecyclerView + private lateinit var tvNothingAdded : TextView private lateinit var binding : ActivityFilterBinding + private lateinit var folgen_database: SQLiteDatabase + private lateinit var databaseHelper: DatabaseHelper private lateinit var tvInput : MaterialAutoCompleteTextView private val episodeList = ArrayList() + private var filterList = ArrayList() + private var jsonIndexSeperator = HashMap() + private var apiCallCounter = 0 private lateinit var networkUtils: NetworkUtils + private lateinit var folgenListe : String + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityFilterBinding.inflate(layoutInflater) val view = binding.root setContentView(view) iniViews() + refreshList() + supportActionBar?.setDisplayHomeAsUpEnabled(true) lifecycleScope.launch { withContext(Dispatchers.IO) { - apiCall() + apiCall("ddf") + apiCall("d3") + apiCall("kids") + apiCall("sonderfolgen") } } + } - private suspend fun apiCall() { - val urlExtraParameter = "folgen.json" - episodeList.clear() + private suspend fun apiCall(extraParameter : String) { try { if(networkUtils.isConnected(this)){ - val client = OkHttpClient.Builder().build() - val request = - Request.Builder().url(getString(R.string.base_url) + urlExtraParameter) - .build() - val folgenListe = client.newCall(request).await().body()?.string().toString() + if(extraParameter == "ddf"){ + folgenListe = assets.open("offline_list.txt").bufferedReader().use(BufferedReader::readText) + }else if (extraParameter == "d3"){ + folgenListe = assets.open("offline_list_dd.txt").bufferedReader().use(BufferedReader::readText) + }else if(extraParameter == "kids"){ + folgenListe = assets.open("offline_list_kids.txt").bufferedReader().use(BufferedReader::readText) + }else{ + folgenListe = assets.open("offline_list_sonderfolgen_ddf.txt").bufferedReader().use(BufferedReader::readText) + } val jsonObject = JSONObject(folgenListe) val jsonArray = jsonObject.optJSONArray("folgen") if (jsonArray != null) { @@ -61,12 +83,18 @@ class FilterActivity : BaseActivity(){ JsonResponse( name = jsonObject.optString("name"), beschreibung = jsonObject.optString("beschreibung"), - spotify = jsonObject.optString("spotify") + spotify = jsonObject.optString("spotify"), + nummer = jsonObject.optInt("nummer"), + type = extraParameter ) ) } - runOnUiThread { - iniAutoComplete() + jsonIndexSeperator[extraParameter] = jsonArray.length() + apiCallCounter++ + if(apiCallCounter == 4){ + runOnUiThread { + iniAutoComplete() + } } } @@ -85,11 +113,59 @@ class FilterActivity : BaseActivity(){ textView.setOnItemClickListener { parent, view, position, id -> val selectedName = parent.getItemAtPosition(position) as String + databaseHelper.insertFilterFolge(folgen_database,selectedName,getEpisodeNumber(selectedName),getEpisodeType(selectedName)) + textView.text = null + refreshList() + } + } + private fun refreshList(){ + filterList = databaseHelper.getFilterList(folgen_database) + val adapter = FilterListeAdapter(filterList,this) + val layoutManger = LinearLayoutManager(this) + if(filterList.isNotEmpty()){ + tvNothingAdded.visibility = View.GONE + }else{ + tvNothingAdded.visibility = View.VISIBLE } + rvFilterListe.layoutManager = layoutManger + rvFilterListe.adapter = adapter + } private fun iniViews(){ + folgen_database = openOrCreateDatabase("app_list",MODE_PRIVATE,null) + databaseHelper = DatabaseHelper(this) + databaseHelper.createTables(folgen_database) + networkUtils = NetworkUtils() tvInput = binding.tvFolge + rvFilterListe = binding.rvFilterListe + tvNothingAdded = binding.tvNoEpisodesAdded + } + + private fun getEpisodeNumber(nameToFind: String): String? { + for (jsonResponse in episodeList) { + if (jsonResponse.name == nameToFind) { + return jsonResponse.nummer.toString() + } + } + return null + } + private fun getEpisodeType(nameToFind: String): String? { + for (jsonResponse in episodeList) { + if (jsonResponse.name == nameToFind) { + return jsonResponse.type + } + } + return null + } + override fun onSupportNavigateUp(): Boolean { + onBackPressed() + return true + } + + override fun onDeleteItem(name: String) { + databaseHelper.removeFromList(folgen_database,name) + refreshList() } } \ No newline at end of file diff --git a/app/src/main/java/de/msdevs/einschlafhilfe/MainActivity.kt b/app/src/main/java/de/msdevs/einschlafhilfe/MainActivity.kt index 740e89c..a81340a 100644 --- a/app/src/main/java/de/msdevs/einschlafhilfe/MainActivity.kt +++ b/app/src/main/java/de/msdevs/einschlafhilfe/MainActivity.kt @@ -271,7 +271,7 @@ class MainActivity : BaseActivity() { if (jsonArray != null) { for (i in 0 until jsonArray.length()) { val jsonObject = jsonArray.getJSONObject(i) - episodeList.add(JsonResponse(name = jsonObject.optString("name"), beschreibung = jsonObject.optString("beschreibung"), spotify = jsonObject.optString("spotify"))) + episodeList.add(JsonResponse(name = jsonObject.optString("name"), beschreibung = jsonObject.optString("beschreibung"), spotify = jsonObject.optString("spotify"), nummer = jsonObject.optInt("nummer"), type = "")) } } } diff --git a/app/src/main/java/de/msdevs/einschlafhilfe/adapter/AutoCompleteTextViewAdapter.kt b/app/src/main/java/de/msdevs/einschlafhilfe/adapter/AutoCompleteTextViewAdapter.kt deleted file mode 100644 index b123ff6..0000000 --- a/app/src/main/java/de/msdevs/einschlafhilfe/adapter/AutoCompleteTextViewAdapter.kt +++ /dev/null @@ -1,29 +0,0 @@ -package de.msdevs.einschlafhilfe.adapter - -import android.content.Context -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.ArrayAdapter -import android.widget.TextView -import de.msdevs.einschlafhilfe.models.JsonResponse -import de.msdevs.einschlafhilfe.R - -class AutoCompleteTextViewAdapter(context: Context, resource: Int, objects: ArrayList) : - ArrayAdapter(context, resource, objects) { - - override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { - val item = getItem(position) - val view = convertView ?: LayoutInflater.from(context) - .inflate(R.layout.layout_autocomplete, parent, false) - - val textView: TextView = view.findViewById(R.id.tv_folgen_name) - textView.text = item?.name - - return view - } - - override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View { - return getView(position, convertView, parent) - } -} \ No newline at end of file diff --git a/app/src/main/java/de/msdevs/einschlafhilfe/adapter/FilterListeAdapter.kt b/app/src/main/java/de/msdevs/einschlafhilfe/adapter/FilterListeAdapter.kt new file mode 100644 index 0000000..54bb0c9 --- /dev/null +++ b/app/src/main/java/de/msdevs/einschlafhilfe/adapter/FilterListeAdapter.kt @@ -0,0 +1,79 @@ +package de.msdevs.einschlafhilfe.adapter + +import android.content.Context +import android.util.Log +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.ImageView +import androidx.recyclerview.widget.RecyclerView +import com.bumptech.glide.Glide +import com.bumptech.glide.load.engine.DiskCacheStrategy +import de.msdevs.einschlafhilfe.databinding.ItemFolgeBinding +import de.msdevs.einschlafhilfe.models.JsonResponse +import de.msdevs.einschlafhilfe.utils.NetworkUtils +import de.msdevs.einschlafhilfe.R + +class FilterListeAdapter(private val folgeList: List, private val onDeleteItemListener: OnDeleteItemListener? = null) : + RecyclerView.Adapter() { + private var networkUtils = NetworkUtils() + + inner class FolgeViewHolder(private val binding: ItemFolgeBinding) : + RecyclerView.ViewHolder(binding.root) { + + fun bind(folgeTitle: String, nummer : String, type : String) { + binding.tvFolgenName.text = folgeTitle + binding.tvFolgenNummer.text = binding.tvFolgenNummer.context.getString(R.string.nummer, nummer) + loadCover(nummer,type, binding.ivCover) + + binding.ivDelete.setOnClickListener{ + onDeleteItemListener?.onDeleteItem(folgeTitle) + } + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FolgeViewHolder { + val binding = + ItemFolgeBinding.inflate(LayoutInflater.from(parent.context), parent, false) + return FolgeViewHolder(binding) + } + + override fun onBindViewHolder(holder: FolgeViewHolder, position: Int) { + val item = folgeList[position] + holder.bind(item.name,item.nummer.toString(), item.type) + } + + override fun getItemCount(): Int { + return folgeList.size + } + + private fun loadCover(nummer : String, type : String, iv : ImageView){ + val context : Context = iv.context + var prefix = "" + if(type == "ddf"){ + prefix = "" + }else if(type == "d3"){ + prefix = "dd" + }else if(type == "kids"){ + prefix = "k" + }else if(type == "sonderfolgen"){ + prefix = "x" + }else{ + prefix = "s" + } + val url : String = if(prefix == "dd"){ + context.getString(R.string.cover_citroncode_dd_url) + (nummer) + ".png" + }else{ + context.getString(R.string.cover_citroncode_url) + prefix + (nummer) + ".png" + } + Log.e("FilterListeAdapter", "Cover URL: $url") + if(networkUtils.isConnected(context)){ + Glide.with(context) + .load(url) + .diskCacheStrategy(DiskCacheStrategy.ALL) + .into(iv) + } + } + interface OnDeleteItemListener { + fun onDeleteItem(name: String) + } +} \ No newline at end of file diff --git a/app/src/main/java/de/msdevs/einschlafhilfe/database/DatabaseHelper.kt b/app/src/main/java/de/msdevs/einschlafhilfe/database/DatabaseHelper.kt new file mode 100644 index 0000000..cc00456 --- /dev/null +++ b/app/src/main/java/de/msdevs/einschlafhilfe/database/DatabaseHelper.kt @@ -0,0 +1,57 @@ +package de.msdevs.einschlafhilfe.database + +import android.content.ContentValues +import android.content.Context +import android.database.Cursor +import android.database.sqlite.SQLiteDatabase +import android.util.Log +import de.msdevs.einschlafhilfe.models.JsonResponse + + +class DatabaseHelper(private val c: Context) { + fun insertFilterFolge( + database: SQLiteDatabase, + name: String?, + nummer: String?, + type: String? + ) { + val initialValues = ContentValues() + initialValues.put("name", name) + initialValues.put("nummer", nummer) + initialValues.put("type", type) + database.insert("filter", null, initialValues) + } + + fun createTables(database: SQLiteDatabase) { + database.execSQL("CREATE TABLE IF NOT EXISTS filter(name VARCHAR, nummer VARCHAR, type VARCHAR);") + } + + + fun removeFromList(database: SQLiteDatabase, name: String) { + database.delete("filter", "name=?", arrayOf(name)) + } + + fun getFilterList(database: SQLiteDatabase): ArrayList { + val app_names = ArrayList() + var cursor: Cursor? = null + try { + cursor = database.rawQuery("Select * from filter", null) + if (cursor.moveToFirst()) { + do { + try { + app_names.add(JsonResponse(cursor.getString(0),"N/A","N/A",Integer.parseInt(cursor.getString(1)), cursor.getString(2))) + } catch (e: Exception) { + Log.e("DatabaseHelper", "Error: " + e.message) + } + } while (cursor.moveToNext()) + } + } catch (e: Exception) { + Log.e("DatabaseHelper", "Error: " + e.message) + } finally { + cursor?.close() + } + return app_names + } + + +} \ No newline at end of file diff --git a/app/src/main/java/de/msdevs/einschlafhilfe/models/JsonResponse.kt b/app/src/main/java/de/msdevs/einschlafhilfe/models/JsonResponse.kt index 73872c2..7964ce5 100644 --- a/app/src/main/java/de/msdevs/einschlafhilfe/models/JsonResponse.kt +++ b/app/src/main/java/de/msdevs/einschlafhilfe/models/JsonResponse.kt @@ -3,5 +3,7 @@ package de.msdevs.einschlafhilfe.models class JsonResponse( val name: String, val beschreibung: String, - val spotify: String + val spotify: String, + val nummer: Int, + val type : String ) \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_delete.xml b/app/src/main/res/drawable/ic_delete.xml new file mode 100644 index 0000000..60a0fd7 --- /dev/null +++ b/app/src/main/res/drawable/ic_delete.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/layout/activity_filter.xml b/app/src/main/res/layout/activity_filter.xml index 762f67c..0ce85cd 100644 --- a/app/src/main/res/layout/activity_filter.xml +++ b/app/src/main/res/layout/activity_filter.xml @@ -3,31 +3,40 @@ android:layout_width="match_parent" android:background="@color/black" android:layout_height="match_parent"> - - - - - + + + - + android:layout_margin="10dp" + android:layout_height="wrap_content"> + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_folge.xml b/app/src/main/res/layout/item_folge.xml new file mode 100644 index 0000000..d55f94a --- /dev/null +++ b/app/src/main/res/layout/item_folge.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_autocomplete.xml b/app/src/main/res/layout/layout_autocomplete.xml deleted file mode 100644 index a66924a..0000000 --- a/app/src/main/res/layout/layout_autocomplete.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0c92bfd..fadbafb 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -68,6 +68,7 @@ Bob Justus Peter + Drittanbieter Bibliotheken theme Die drei ??? Sonderfolgen Die drei ??? Kids Sonderfolgen @@ -76,4 +77,6 @@ Klick auf den Button unten, um Folgen auszublenden, die du z. B. nicht im Besitz hast oder nicht gerne hörst. Folgen auswählen Es wurden noch keine Folgen hinzugefügt + Nummer: %1$s + Name der Folge diff --git a/build.gradle b/build.gradle index 0d39da9..0a84534 100644 --- a/build.gradle +++ b/build.gradle @@ -8,6 +8,7 @@ buildscript { dependencies { classpath 'com.android.tools.build:gradle:8.3.0-rc01' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.10" + } }