diff --git a/app/build.gradle b/app/build.gradle
index ab2f937..30216a3 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -57,18 +57,19 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.core:core-ktx:1.3.2'
- implementation 'androidx.constraintlayout:constraintlayout:2.0.2'
+ implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation "androidx.coordinatorlayout:coordinatorlayout:1.1.0"
implementation 'com.github.doyaaaaaken:kotlin-csv-jvm:0.11.1'
+ implementation 'com.github.wrdlbrnft:sorted-list-adapter:0.3.0.27'
implementation "androidx.cardview:cardview:1.0.0"
- implementation 'androidx.navigation:navigation-fragment-ktx:2.3.0'
- implementation 'androidx.navigation:navigation-ui-ktx:2.3.0'
+ implementation 'androidx.navigation:navigation-fragment-ktx:2.3.1'
+ implementation 'androidx.navigation:navigation-ui-ktx:2.3.1'
implementation 'androidx.preference:preference-ktx:1.1.1'
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.recyclerview:recyclerview:1.1.0'
- testImplementation 'junit:junit:4.12'
+ testImplementation 'junit:junit:4.13'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
implementation 'com.google.android.material:material:1.2.1'
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 76a077a..550722f 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -18,18 +18,12 @@
-
+ android:configChanges="screenSize|screenLayout|keyboardHidden|orientation">
-
\ No newline at end of file
diff --git a/app/src/main/java/com/illusionware/npsbrowser/AppData.kt b/app/src/main/java/com/illusionware/npsbrowser/AppData.kt
index a35dcf7..c2070ba 100644
--- a/app/src/main/java/com/illusionware/npsbrowser/AppData.kt
+++ b/app/src/main/java/com/illusionware/npsbrowser/AppData.kt
@@ -1,5 +1,59 @@
package com.illusionware.npsbrowser
-data class AppData(val titleID: String?, val region: String?, val title : String?, val link : String?,
- val license : String?, val contentID : String?, val lastDateTime: String?,
- val fileSize : Number?, val sha256 : String?, val minFW : String?)
\ No newline at end of file
+import com.github.wrdlbrnft.sortedlistadapter.SortedListAdapter
+
+data class AppData(
+ val titleID: String?, val region: String?, val title: String?, val link: String?,
+ val license: String?, val contentID: String?, val lastDateTime: String?,
+ val fileSize: Number?, val sha256: String?, val minFW: String?
+) : SortedListAdapter.ViewModel {
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (javaClass != other?.javaClass) return false
+
+ other as AppData
+
+ if (titleID != other.titleID) return false
+ if (region != other.region) return false
+ if (title != other.title) return false
+ if (link != other.link) return false
+ if (license != other.license) return false
+ if (contentID != other.contentID) return false
+ if (lastDateTime != other.lastDateTime) return false
+ if (fileSize != other.fileSize) return false
+ if (sha256 != other.sha256) return false
+ if (minFW != other.minFW) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = titleID?.hashCode() ?: 0
+ result = 31 * result + (region?.hashCode() ?: 0)
+ result = 31 * result + (title?.hashCode() ?: 0)
+ result = 31 * result + (link?.hashCode() ?: 0)
+ result = 31 * result + (license?.hashCode() ?: 0)
+ result = 31 * result + (contentID?.hashCode() ?: 0)
+ result = 31 * result + (lastDateTime?.hashCode() ?: 0)
+ result = 31 * result + (fileSize?.hashCode() ?: 0)
+ result = 31 * result + (sha256?.hashCode() ?: 0)
+ result = 31 * result + (minFW?.hashCode() ?: 0)
+ return result
+ }
+
+ override fun isSameModelAs(model: T): Boolean {
+ if (model is AppData) {
+ val other: AppData = model as AppData
+ return "${other.titleID} ${other.title}" == "$titleID $title"
+ }
+ return false
+ }
+
+ override fun isContentTheSameAs(model: T): Boolean {
+ if (model is AppData) {
+ val other: AppData = model as AppData
+ return title?.equals(other.title) ?: (other.title == null)
+ }
+ return false
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/illusionware/npsbrowser/activities/MainActivity.kt b/app/src/main/java/com/illusionware/npsbrowser/activities/MainActivity.kt
index dfbb7ca..7f293e9 100644
--- a/app/src/main/java/com/illusionware/npsbrowser/activities/MainActivity.kt
+++ b/app/src/main/java/com/illusionware/npsbrowser/activities/MainActivity.kt
@@ -2,18 +2,26 @@ package com.illusionware.npsbrowser.activities
import android.os.Bundle
import android.view.MenuItem
+import androidx.fragment.app.FragmentManager
import com.illusionware.npsbrowser.R
import com.illusionware.npsbrowser.fragments.mainactivity.MainActivityFragment
-class MainActivity : BaseActivity() {
+class MainActivity : BaseActivity(), FragmentManager.OnBackStackChangedListener {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setTheme(R.style.NPSTheme)
setContentView(R.layout.activity_main)
+ setSupportActionBar(findViewById(R.id.toolbar))
+ supportFragmentManager.addOnBackStackChangedListener(this);
+
+ if (supportFragmentManager.backStackEntryCount > 0) {
+ showUpButton()
+ }
+
if (savedInstanceState == null) {
supportFragmentManager.beginTransaction()
- .replace(R.id.container, MainActivityFragment.newInstance())
+ .replace(R.id.container, MainActivityFragment())
.commitNow()
}
}
@@ -28,12 +36,20 @@ class MainActivity : BaseActivity() {
return super.onOptionsItemSelected(item)
}
- fun showUpButton() {
+ override fun onBackStackChanged() {
+ // enable Up button only if there are entries on the backstack
+ if (supportFragmentManager.backStackEntryCount < 1) {
+ hideUpButton()
+ } else {
+ showUpButton()
+ }
+ }
+
+ private fun showUpButton() {
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
}
- fun hideUpButton() {
+ private fun hideUpButton() {
supportActionBar!!.setDisplayHomeAsUpEnabled(false)
}
-
}
diff --git a/app/src/main/java/com/illusionware/npsbrowser/activities/SingleAppActivity.kt b/app/src/main/java/com/illusionware/npsbrowser/activities/SingleAppActivity.kt
deleted file mode 100644
index d4d5b92..0000000
--- a/app/src/main/java/com/illusionware/npsbrowser/activities/SingleAppActivity.kt
+++ /dev/null
@@ -1,104 +0,0 @@
-package com.illusionware.npsbrowser.activities
-
-import android.app.DownloadManager
-import android.content.ClipData
-import android.content.ClipboardManager
-import android.net.Uri
-import android.os.Bundle
-import android.os.Environment
-import android.util.Log
-import android.view.View
-import android.widget.Toast
-import com.illusionware.npsbrowser.AppData
-import com.illusionware.npsbrowser.databinding.ActivitySingleAppBinding
-import java.lang.Exception
-
-class SingleAppActivity: BaseActivity() {
- companion object {
- lateinit var app : AppData
- }
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- val binding = ActivitySingleAppBinding.inflate(layoutInflater)
- setContentView(binding.root)
-
- var downloadManager = getSystemService(DOWNLOAD_SERVICE) as DownloadManager
-
- binding.singleAppTitle.text = "[${app.titleID}] ${app.title}"
- if (app.fileSize != null) {
- binding.singleAppSize.text = prettifySize()
- } else {
- binding.singleAppSizeTitle.visibility = View.INVISIBLE
- }
- binding.singleAppContentId.text = app.contentID
- binding.singleAppFwVer.text = app.minFW
- // TODO: get and set app icon
- if (app.minFW.isNullOrBlank()) {
- binding.singleAppFwVerTitle.visibility = View.INVISIBLE
- }
- if (app.license == "MISSING") {
- binding.singleAppError.text = "Warning: No license was provided for this app."
- }
- if (app.link == "CART ONLY") {
- binding.singleAppError.text = "CART ONLY GAME"
- binding.singleAppDownloadButton.isEnabled = false
- } else if (app.link == "MISSING") {
- binding.singleAppError.text = "Warning: No download link was provided for this app."
- binding.singleAppDownloadButton.isEnabled = false
- }
-
- binding.singleAppContentId.setOnLongClickListener {
- val clipboardManager = getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
- val clipData = ClipData.newPlainText("Content ID", binding.singleAppContentId.text)
- clipboardManager.setPrimaryClip(clipData)
- Toast.makeText(this, "Content ID Copied to Clipboard!", Toast.LENGTH_SHORT).show()
- true
- }
-
- binding.singleAppDownloadButton.setOnClickListener {
- try {
- var request = DownloadManager.Request(Uri.parse(app.link))
- request.setTitle(app.title)
- request.setDescription("Downloading...")
- request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
- request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, app.title + ".pkg") // TODO: set up preference for this
- downloadManager.enqueue(request)
- } catch (e: Exception) {
- Toast.makeText(this, "An error occurred while downloading. please try again", Toast.LENGTH_SHORT).show()
- }
- }
-
- binding.singleAppDownloadButton.setOnLongClickListener {
- val clipboardManager = getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
- val clipData = ClipData.newPlainText("PKG URL", app.link)
- clipboardManager.setPrimaryClip(clipData)
- Toast.makeText(this, "PKG URL Copied to Clipboard", Toast.LENGTH_SHORT).show()
- true
- }
- }
-
- private fun prettifySize(): String {
- if (app.fileSize!!.toFloat() / (1024 * 1024 * 1024) > 1) {
- return "%.1f GB".format(toGB(app.fileSize!!))
- }
- if (app.fileSize!!.toFloat() / (1024 * 1024) > 1) {
- return "%.1f MB".format(toMB(app.fileSize!!))
- }
- if (app.fileSize!!.toFloat() / 1024 > 1) {
- return "%.1f KB".format(toKB(app.fileSize!!))
- }
- return "${app.fileSize!!} Bytes";
- }
-
- private fun toKB(size: Number): Number {
- return size.toFloat() / 1024
- }
-
- private fun toMB(size: Number): Number {
- return size.toFloat() / (1024 * 1024)
- }
-
- private fun toGB(size: Number): Number {
- return size.toFloat() / (1024 * 1024 * 1024)
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/illusionware/npsbrowser/adapters/AppAdapter.kt b/app/src/main/java/com/illusionware/npsbrowser/adapters/AppAdapter.kt
index c00ea9c..fe45bf1 100644
--- a/app/src/main/java/com/illusionware/npsbrowser/adapters/AppAdapter.kt
+++ b/app/src/main/java/com/illusionware/npsbrowser/adapters/AppAdapter.kt
@@ -1,33 +1,73 @@
package com.illusionware.npsbrowser.adapters
import android.content.Context
+import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.core.content.res.ResourcesCompat
-import androidx.recyclerview.widget.RecyclerView
+import com.github.wrdlbrnft.modularadapter.ModularAdapter
+import com.github.wrdlbrnft.sortedlistadapter.SortedListAdapter
import com.illusionware.npsbrowser.AppData
import com.illusionware.npsbrowser.R
-import kotlin.collections.ArrayList
+import com.illusionware.npsbrowser.activities.MainActivity
+import com.illusionware.npsbrowser.fragments.SingleAppFragment
+import com.illusionware.npsbrowser.fragments.mainactivity.MainActivityFragment
+import java.util.Comparator
-class AppAdapter(private val appsList: ArrayList, val context : Context) : RecyclerView.Adapter() {
- private final val LIST_ITEM = 0
- private final val GRID_ITEM = 1
- var isSwitchView = true
+private val comparator: Comparator = SortedListAdapter.ComparatorBuilder()
+ .setOrderForModel(AppData::class.java) { a, b -> a.title?.compareTo(b.title!!)!! }
+ .build()
- inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
- var appTitle: TextView = view.findViewById(R.id.appTitle)
- var appRegion: TextView = view.findViewById(R.id.appRegion)
- var appMinFW: TextView = view.findViewById(R.id.appMinFW)
- var appLastDate: TextView = view.findViewById(R.id.appLastDate)
+class AppAdapter(val context: Context) : SortedListAdapter(context, AppData::class.java, comparator) {
+ private val LIST_ITEM = 0
+ private val GRID_ITEM = 1
+ private var isSwitchView = true
+
+ inner class AppViewHolder(itemView: View) :
+ SortedListAdapter.ViewHolder(itemView) {
+ var appTitle: TextView = itemView.findViewById(R.id.appTitle)
+ var appRegion: TextView = itemView.findViewById(R.id.appRegion)
+ var appMinFW: TextView = itemView.findViewById(R.id.appMinFW)
+ var appLastDate: TextView = itemView.findViewById(R.id.appLastDate)
+
+ override fun performBind(item: AppData) {
+ appTitle.text = item.title
+ appTitle.isSelected = true
+ appRegion.text = item.region
+ appMinFW.text = item.minFW
+ appMinFW.background = null;
+
+ if (appMinFW.text.isNotBlank()) {
+ val drawable = ResourcesCompat.getDrawable(
+ context.resources,
+ R.drawable.rect_red,
+ null
+ )
+ appMinFW.background = drawable;
+ }
+ appLastDate.text = item.lastDateTime
+ }
}
- override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
+ override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup, viewType: Int): AppViewHolder {
return if (viewType == LIST_ITEM) {
- ViewHolder( LayoutInflater.from(parent.context).inflate(R.layout.list_item, parent, false))
+ AppViewHolder(
+ inflater.inflate(
+ R.layout.recycler_item_list,
+ parent,
+ false
+ )
+ )
} else {
- ViewHolder( LayoutInflater.from(parent.context).inflate(R.layout.grid_item, parent, false))
+ AppViewHolder(
+ inflater.inflate(
+ R.layout.recycler_item_grid,
+ parent,
+ false
+ )
+ )
}
}
@@ -39,34 +79,8 @@ class AppAdapter(private val appsList: ArrayList, val context : Context
}
}
- public fun toggleItemViewType(type: Int): Boolean {
+ fun toggleItemViewType(type: Int): Boolean {
isSwitchView = type == 0
return isSwitchView
}
-
- override fun onBindViewHolder(holder: ViewHolder, position: Int) {
- holder.appTitle.text = appsList[position].title
- holder.appTitle.isSelected = true
- holder.appRegion.text = appsList[position].region
- holder.appMinFW.text = appsList[position].minFW
- holder.appMinFW.background = null;
-
- if (holder.appMinFW.text.isNotBlank()) {
- val drawable = ResourcesCompat.getDrawable(context.resources, R.drawable.rect_red, null)
- holder.appMinFW.background = drawable;
- }
- holder.appLastDate.text = appsList[position].lastDateTime
- }
-
- override fun getItemCount(): Int {
- return appsList.size
- }
-
- fun updateData(apps: ArrayList) {
- appsList.clear()
- notifyDataSetChanged()
- appsList.addAll(apps)
- notifyDataSetChanged()
-
- }
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/com/illusionware/npsbrowser/fragments/SettingsFragment.kt b/app/src/main/java/com/illusionware/npsbrowser/fragments/SettingsFragment.kt
index ae17dc0..8630afe 100644
--- a/app/src/main/java/com/illusionware/npsbrowser/fragments/SettingsFragment.kt
+++ b/app/src/main/java/com/illusionware/npsbrowser/fragments/SettingsFragment.kt
@@ -1,33 +1,23 @@
package com.illusionware.npsbrowser.fragments
import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
+import android.view.*
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentTransaction.TRANSIT_FRAGMENT_OPEN
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceManager
import com.illusionware.npsbrowser.R
-import com.illusionware.npsbrowser.activities.BaseActivity
-import com.illusionware.npsbrowser.activities.MainActivity
-import com.illusionware.npsbrowser.fragments.mainactivity.MainActivityFragment
-import kotlinx.android.synthetic.main.appbar.*
class SettingsFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- if (activity!!.supportFragmentManager.backStackEntryCount < 1) {
- (activity as MainActivity?)?.showUpButton()
- MainActivityFragment().recyclerView?.adapter = MainActivityFragment().viewAdapter
- }
PreferenceManager.setDefaultValues(context, R.xml.root_preferences, false);
activity?.supportFragmentManager
?.beginTransaction()
?.replace(
R.id.prefs,
- SettingsFragment()
+ PrefsFragment()
)
?.setTransition(TRANSIT_FRAGMENT_OPEN)
?.commit()
@@ -42,24 +32,11 @@ class SettingsFragment : Fragment() {
return inflater.inflate(R.layout.settings_fragment, container, false)
}
- override fun onActivityCreated(savedInstanceState: Bundle?) {
- super.onActivityCreated(savedInstanceState)
- (activity as BaseActivity?)!!.setSupportActionBar(toolbar)
- (activity as BaseActivity?)!!.supportActionBar?.setDisplayHomeAsUpEnabled(true)
- //viewModel = ViewModelProviders.of(this).get(MainActivity2ViewModel::class.java)
- // TODO: Use the ViewModel
- }
-
- class SettingsFragment : PreferenceFragmentCompat() {
+ class PrefsFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.root_preferences, rootKey)
PreferenceManager.setDefaultValues(context, R.xml.root_preferences, false);
}
-
- override fun onActivityCreated(savedInstanceState: Bundle?) {
- //(activity as BaseActivity?)!!.setSupportActionBar(toolbar)
- super.onActivityCreated(savedInstanceState)
- }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/illusionware/npsbrowser/fragments/SingleAppFragment.kt b/app/src/main/java/com/illusionware/npsbrowser/fragments/SingleAppFragment.kt
new file mode 100644
index 0000000..e6d081b
--- /dev/null
+++ b/app/src/main/java/com/illusionware/npsbrowser/fragments/SingleAppFragment.kt
@@ -0,0 +1,115 @@
+package com.illusionware.npsbrowser.fragments
+
+import android.app.DownloadManager
+import android.content.ClipData
+import android.content.ClipboardManager
+import android.content.Context.CLIPBOARD_SERVICE
+import android.content.Context.DOWNLOAD_SERVICE
+import android.net.Uri
+import android.os.Bundle
+import android.os.Environment
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.Button
+import android.widget.TextView
+import android.widget.Toast
+import androidx.fragment.app.Fragment
+import com.illusionware.npsbrowser.AppData
+import com.illusionware.npsbrowser.R
+import java.lang.Exception
+
+class SingleAppFragment : Fragment() {
+ companion object {
+ lateinit var app : AppData
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ super.onCreateView(inflater, container, savedInstanceState)
+ activity?.title = app.title
+ val view = inflater.inflate(R.layout.single_app_fragment, container, false)
+ val downloadManager = requireActivity().getSystemService(DOWNLOAD_SERVICE) as DownloadManager
+
+ view?.findViewById(R.id.singleAppTitle)?.text = "[${app.titleID}] ${app.title}"
+ if (app.fileSize != null) {
+ view?.findViewById(R.id.singleAppSize)?.text = prettifySize()
+ } else {
+ view?.findViewById(R.id.singleAppSizeTitle)?.visibility = View.INVISIBLE
+ }
+ view?.findViewById(R.id.singleAppContentId)?.text = app.contentID
+ view?.findViewById(R.id.singleAppFwVer)?.text = app.minFW
+ // TODO: get and set app icon
+ if (app.minFW.isNullOrBlank()) {
+ view?.findViewById(R.id.singleAppFwVerTitle)?.visibility = View.INVISIBLE
+ }
+ if (app.license == "MISSING") {
+ view?.findViewById(R.id.singleAppError)?.text = "Warning: No license was provided for this app."
+ }
+ if (app.link == "CART ONLY") {
+ view?.findViewById(R.id.singleAppError)?.text = "CART ONLY GAME"
+ view?.findViewById