ui: refactor: Use ViewPager2 instead of an ad-hoc spinner for slot selection
All checks were successful
/ build-debug (push) Successful in 4m32s
All checks were successful
/ build-debug (push) Successful in 4m32s
This commit is contained in:
parent
d78985bd72
commit
3ffd847af7
5 changed files with 71 additions and 75 deletions
|
@ -12,28 +12,43 @@ import android.util.Log
|
|||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.widget.AdapterView
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.Spinner
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.commitNow
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import com.google.android.material.tabs.TabLayoutMediator
|
||||
import im.angry.openeuicc.common.R
|
||||
import im.angry.openeuicc.util.*
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
open class MainActivity : BaseEuiccAccessActivity(), OpenEuiccContextMarker {
|
||||
companion object {
|
||||
const val TAG = "MainActivity"
|
||||
}
|
||||
|
||||
private lateinit var spinnerAdapter: ArrayAdapter<String>
|
||||
private lateinit var spinnerItem: MenuItem
|
||||
private lateinit var spinner: Spinner
|
||||
private lateinit var loadingProgress: ProgressBar
|
||||
private lateinit var tabs: TabLayout
|
||||
private lateinit var viewPager: ViewPager2
|
||||
|
||||
private data class Page(
|
||||
val title: String,
|
||||
val createFragment: () -> Fragment
|
||||
)
|
||||
|
||||
private val pages: MutableList<Page> = mutableListOf()
|
||||
|
||||
private val pagerAdapter by lazy {
|
||||
object : FragmentStateAdapter(this) {
|
||||
override fun getItemCount() = pages.size
|
||||
|
||||
override fun createFragment(position: Int): Fragment = pages[position].createFragment()
|
||||
}
|
||||
}
|
||||
|
||||
var loading: Boolean
|
||||
get() = loadingProgress.visibility == View.VISIBLE
|
||||
|
@ -45,8 +60,6 @@ open class MainActivity : BaseEuiccAccessActivity(), OpenEuiccContextMarker {
|
|||
}
|
||||
}
|
||||
|
||||
private val fragments = arrayListOf<Fragment>()
|
||||
|
||||
protected lateinit var tm: TelephonyManager
|
||||
|
||||
private val usbReceiver = object : BroadcastReceiver() {
|
||||
|
@ -63,11 +76,16 @@ open class MainActivity : BaseEuiccAccessActivity(), OpenEuiccContextMarker {
|
|||
setContentView(R.layout.activity_main)
|
||||
setSupportActionBar(requireViewById(R.id.toolbar))
|
||||
loadingProgress = requireViewById(R.id.loading)
|
||||
tabs = requireViewById(R.id.main_tabs)
|
||||
viewPager = requireViewById(R.id.view_pager)
|
||||
|
||||
viewPager.adapter = pagerAdapter
|
||||
TabLayoutMediator(tabs, viewPager) { tab, pos ->
|
||||
tab.text = pages[pos].title
|
||||
}.attach()
|
||||
|
||||
tm = telephonyManager
|
||||
|
||||
spinnerAdapter = ArrayAdapter<String>(this, R.layout.spinner_item)
|
||||
|
||||
registerReceiver(usbReceiver, IntentFilter().apply {
|
||||
addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED)
|
||||
addAction(UsbManager.ACTION_USB_DEVICE_DETACHED)
|
||||
|
@ -81,37 +99,6 @@ open class MainActivity : BaseEuiccAccessActivity(), OpenEuiccContextMarker {
|
|||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
menuInflater.inflate(R.menu.activity_main, menu)
|
||||
|
||||
if (!this::spinner.isInitialized) {
|
||||
spinnerItem = menu.findItem(R.id.spinner)
|
||||
spinner = spinnerItem.actionView as Spinner
|
||||
if (spinnerAdapter.isEmpty) {
|
||||
spinnerItem.isVisible = false
|
||||
}
|
||||
spinner.adapter = spinnerAdapter
|
||||
spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
||||
override fun onItemSelected(
|
||||
parent: AdapterView<*>?,
|
||||
view: View?,
|
||||
position: Int,
|
||||
id: Long
|
||||
) {
|
||||
if (position < fragments.size) {
|
||||
supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.fragment_root, fragments[position]).commit()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNothingSelected(parent: AdapterView<*>?) {
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
// Fragments may cause this menu to be inflated multiple times.
|
||||
// Simply reuse the action view in that case
|
||||
menu.findItem(R.id.spinner).actionView = spinner
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -136,6 +123,8 @@ open class MainActivity : BaseEuiccAccessActivity(), OpenEuiccContextMarker {
|
|||
|
||||
private suspend fun init() {
|
||||
loading = true
|
||||
viewPager.visibility = View.GONE
|
||||
tabs.visibility = View.GONE
|
||||
|
||||
val knownChannels = withContext(Dispatchers.IO) {
|
||||
euiccChannelManager.enumerateEuiccChannels().onEach {
|
||||
|
@ -156,28 +145,25 @@ open class MainActivity : BaseEuiccAccessActivity(), OpenEuiccContextMarker {
|
|||
loading = false
|
||||
|
||||
knownChannels.sortedBy { it.logicalSlotId }.forEach { channel ->
|
||||
spinnerAdapter.add(getString(R.string.channel_name_format, channel.logicalSlotId))
|
||||
fragments.add(appContainer.uiComponentFactory.createEuiccManagementFragment(channel))
|
||||
pages.add(Page(
|
||||
getString(R.string.channel_name_format, channel.logicalSlotId)
|
||||
) { appContainer.uiComponentFactory.createEuiccManagementFragment(channel) })
|
||||
}
|
||||
|
||||
// If USB readers exist, add them at the very last
|
||||
// We use a wrapper fragment to handle logic specific to USB readers
|
||||
usbDevice?.let {
|
||||
spinnerAdapter.add(it.productName)
|
||||
fragments.add(UsbCcidReaderFragment())
|
||||
//spinnerAdapter.add(it.productName)
|
||||
pages.add(Page(it.productName ?: "USB") { UsbCcidReaderFragment() })
|
||||
}
|
||||
pagerAdapter.notifyDataSetChanged()
|
||||
viewPager.visibility = View.VISIBLE
|
||||
|
||||
if (fragments.isNotEmpty()) {
|
||||
if (this@MainActivity::spinner.isInitialized) {
|
||||
spinnerItem.isVisible = true
|
||||
}
|
||||
supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.fragment_root, fragments.first()).commit()
|
||||
} else {
|
||||
supportFragmentManager.beginTransaction().replace(
|
||||
R.id.fragment_root,
|
||||
appContainer.uiComponentFactory.createNoEuiccPlaceholderFragment()
|
||||
).commit()
|
||||
if (pages.size > 1) {
|
||||
tabs.visibility = View.VISIBLE
|
||||
} else if (pages.isEmpty()) {
|
||||
pages.add(Page("") { appContainer.uiComponentFactory.createNoEuiccPlaceholderFragment() })
|
||||
pagerAdapter.notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -185,14 +171,11 @@ open class MainActivity : BaseEuiccAccessActivity(), OpenEuiccContextMarker {
|
|||
private fun refresh() {
|
||||
lifecycleScope.launch {
|
||||
loading = true
|
||||
viewPager.visibility = View.GONE
|
||||
tabs.visibility = View.GONE
|
||||
|
||||
supportFragmentManager.commitNow {
|
||||
fragments.forEach {
|
||||
remove(it)
|
||||
}
|
||||
}
|
||||
fragments.clear()
|
||||
spinnerAdapter.clear()
|
||||
pages.clear()
|
||||
pagerAdapter.notifyDataSetChanged()
|
||||
|
||||
init()
|
||||
}
|
||||
|
|
5
app-common/src/main/res/drawable/ic_refresh_black.xml
Normal file
5
app-common/src/main/res/drawable/ic_refresh_black.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<vector android:height="24dp" android:tint="?attr/colorControlNormal"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M17.65,6.35C16.2,4.9 14.21,4 12,4c-4.42,0 -7.99,3.58 -7.99,8s3.57,8 7.99,8c3.73,0 6.84,-2.55 7.73,-6h-2.08c-0.82,2.33 -3.04,4 -5.65,4 -3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6c1.66,0 3.14,0.69 4.22,1.78L13,11h7V4l-2.35,2.35z"/>
|
||||
</vector>
|
|
@ -14,6 +14,17 @@
|
|||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintWidth_percent="1" />
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/main_tabs"
|
||||
android:background="?attr/colorSurfaceVariant"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
app:tabTextColor="?attr/colorOnSurfaceVariant"
|
||||
app:tabSelectedTextColor="?attr/colorOnSurfaceVariant"
|
||||
app:layout_constraintTop_toBottomOf="@id/toolbar"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/loading"
|
||||
android:layout_width="wrap_content"
|
||||
|
@ -21,16 +32,17 @@
|
|||
android:indeterminate="true"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/toolbar"
|
||||
app:layout_constraintTop_toBottomOf="@id/main_tabs"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/fragment_root"
|
||||
<androidx.viewpager2.widget.ViewPager2
|
||||
android:id="@+id/view_pager"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/toolbar"/>
|
||||
app:layout_constraintTop_toBottomOf="@id/main_tabs"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -1,16 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item
|
||||
android:id="@+id/spinner"
|
||||
android:title=""
|
||||
app:actionViewClass="android.widget.Spinner"
|
||||
app:showAsAction="always" />
|
||||
|
||||
<item
|
||||
android:id="@+id/reload"
|
||||
android:title="@string/reload"
|
||||
app:showAsAction="never" />
|
||||
android:icon="@drawable/ic_refresh_black"
|
||||
app:showAsAction="ifRoom" />
|
||||
|
||||
<item
|
||||
android:id="@+id/settings"
|
||||
|
|
|
@ -50,6 +50,7 @@ dependencies {
|
|||
api("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2")
|
||||
api("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
|
||||
api("androidx.cardview:cardview:1.0.0")
|
||||
api("androidx.viewpager2:viewpager2:1.1.0")
|
||||
api("androidx.datastore:datastore-preferences:1.0.0")
|
||||
api("com.journeyapps:zxing-android-embedded:4.3.0")
|
||||
testImplementation("junit:junit:4.13.2")
|
||||
|
|
Loading…
Reference in a new issue