mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-11-30 06:34:15 +01:00
Add filter and move search bar down
* Fix crashes
This commit is contained in:
parent
479209886b
commit
4db75022d2
@ -17,12 +17,15 @@ import androidx.appcompat.app.AppCompatDelegate
|
|||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.content.res.use
|
import androidx.core.content.res.use
|
||||||
import androidx.core.graphics.drawable.toBitmap
|
import androidx.core.graphics.drawable.toBitmap
|
||||||
|
import androidx.core.view.isInvisible
|
||||||
|
import androidx.core.view.isVisible
|
||||||
import androidx.core.view.size
|
import androidx.core.view.size
|
||||||
import androidx.lifecycle.observe
|
import androidx.lifecycle.observe
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import androidx.recyclerview.widget.DividerItemDecoration
|
import androidx.recyclerview.widget.DividerItemDecoration
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.google.android.material.chip.Chip
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import emu.skyline.adapter.AppViewItem
|
import emu.skyline.adapter.AppViewItem
|
||||||
@ -33,6 +36,7 @@ import emu.skyline.data.AppItem
|
|||||||
import emu.skyline.data.DataItem
|
import emu.skyline.data.DataItem
|
||||||
import emu.skyline.data.HeaderItem
|
import emu.skyline.data.HeaderItem
|
||||||
import emu.skyline.databinding.MainActivityBinding
|
import emu.skyline.databinding.MainActivityBinding
|
||||||
|
import emu.skyline.loader.AppEntry
|
||||||
import emu.skyline.loader.LoaderResult
|
import emu.skyline.loader.LoaderResult
|
||||||
import emu.skyline.loader.RomFormat
|
import emu.skyline.loader.RomFormat
|
||||||
import emu.skyline.utils.Settings
|
import emu.skyline.utils.Settings
|
||||||
@ -41,6 +45,10 @@ import kotlin.math.ceil
|
|||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class MainActivity : AppCompatActivity() {
|
class MainActivity : AppCompatActivity() {
|
||||||
|
companion object {
|
||||||
|
private val formatOrder = arrayOf(RomFormat.NSP, RomFormat.XCI, RomFormat.NRO, RomFormat.NSO, RomFormat.NCA)
|
||||||
|
}
|
||||||
|
|
||||||
private val binding by lazy { MainActivityBinding.inflate(layoutInflater) }
|
private val binding by lazy { MainActivityBinding.inflate(layoutInflater) }
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
@ -54,16 +62,32 @@ class MainActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
private val viewModel by viewModels<MainViewModel>()
|
private val viewModel by viewModels<MainViewModel>()
|
||||||
|
|
||||||
|
private var formatFilter : RomFormat? = null
|
||||||
|
private var appEntries : Map<RomFormat, List<AppEntry>>? = null
|
||||||
|
|
||||||
|
private var refreshIconVisible = false
|
||||||
|
set(visible) {
|
||||||
|
field = visible
|
||||||
|
binding.refreshIcon.apply {
|
||||||
|
if (visible != isVisible) {
|
||||||
|
binding.refreshIcon.alpha = if (visible) 0f else 1f
|
||||||
|
animate().alpha(if (visible) 1f else 0f).withStartAction { isVisible = true }.withEndAction { isInvisible = !visible }.apply { duration = 500 }.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun AppItem.toViewItem() = AppViewItem(layoutType, this, missingIcon, ::selectStartGame, ::selectShowGameDialog)
|
private fun AppItem.toViewItem() = AppViewItem(layoutType, this, missingIcon, ::selectStartGame, ::selectShowGameDialog)
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState : Bundle?) {
|
override fun onCreate(savedInstanceState : Bundle?) {
|
||||||
// Need to create new instance of settings, dependency injection happens
|
// Need to create new instance of settings, dependency injection happens
|
||||||
AppCompatDelegate.setDefaultNightMode(when ((Settings(this).appTheme.toInt())) {
|
AppCompatDelegate.setDefaultNightMode(
|
||||||
|
when ((Settings(this).appTheme.toInt())) {
|
||||||
0 -> AppCompatDelegate.MODE_NIGHT_NO
|
0 -> AppCompatDelegate.MODE_NIGHT_NO
|
||||||
1 -> AppCompatDelegate.MODE_NIGHT_YES
|
1 -> AppCompatDelegate.MODE_NIGHT_YES
|
||||||
2 -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
|
2 -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
|
||||||
else -> AppCompatDelegate.MODE_NIGHT_UNSPECIFIED
|
else -> AppCompatDelegate.MODE_NIGHT_UNSPECIFIED
|
||||||
})
|
}
|
||||||
|
)
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
@ -79,13 +103,26 @@ class MainActivity : AppCompatActivity() {
|
|||||||
setOnRefreshListener { loadRoms(false) }
|
setOnRefreshListener { loadRoms(false) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (format in formatOrder) {
|
||||||
|
binding.chipGroup.addView(Chip(this, null, R.attr.chipChoiceStyle).apply { text = format.name })
|
||||||
|
}
|
||||||
|
binding.chipGroup.setOnCheckedChangeListener { group, checkedId ->
|
||||||
|
for (i in 0 until group.childCount) {
|
||||||
|
if (group.getChildAt(i).id == checkedId) {
|
||||||
|
formatFilter = if (i == 0) null else formatOrder[i - 1]
|
||||||
|
populateAdapter()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
viewModel.stateData.observe(owner = this, onChanged = ::handleState)
|
viewModel.stateData.observe(owner = this, onChanged = ::handleState)
|
||||||
loadRoms(!settings.refreshRequired)
|
loadRoms(!settings.refreshRequired)
|
||||||
|
|
||||||
binding.searchBar.apply {
|
binding.searchBar.apply {
|
||||||
setLogIconListener { startActivity(Intent(context, LogActivity::class.java)) }
|
binding.logIcon.setOnClickListener { startActivity(Intent(context, LogActivity::class.java)) }
|
||||||
setSettingsIconListener { startActivityForResult(Intent(context, SettingsActivity::class.java), 3) }
|
binding.settingsIcon.setOnClickListener { startActivityForResult(Intent(context, SettingsActivity::class.java), 3) }
|
||||||
setRefreshIconListener { loadRoms(false) }
|
binding.refreshIcon.setOnClickListener { loadRoms(false) }
|
||||||
addTextChangedListener(afterTextChanged = { editable ->
|
addTextChangedListener(afterTextChanged = { editable ->
|
||||||
editable?.let { text -> adapter.filter.filter(text.toString()) }
|
editable?.let { text -> adapter.filter.filter(text.toString()) }
|
||||||
})
|
})
|
||||||
@ -95,7 +132,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
window.decorView.findViewById<View>(android.R.id.content).viewTreeObserver.addOnTouchModeChangeListener { isInTouchMode ->
|
window.decorView.findViewById<View>(android.R.id.content).viewTreeObserver.addOnTouchModeChangeListener { isInTouchMode ->
|
||||||
binding.searchBar.refreshIconVisible = !isInTouchMode
|
refreshIconVisible = !isInTouchMode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,32 +221,38 @@ class MainActivity : AppCompatActivity() {
|
|||||||
binding.appList.layoutManager = CustomLayoutManager(gridSpan)
|
binding.appList.layoutManager = CustomLayoutManager(gridSpan)
|
||||||
setAppListDecoration()
|
setAppListDecoration()
|
||||||
|
|
||||||
if (settings.searchLocation.isEmpty()) {
|
if (settings.searchLocation.isEmpty()) startActivityForResult(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).apply {
|
||||||
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
|
flags = Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION or
|
||||||
intent.flags = Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION or Intent.FLAG_GRANT_PREFIX_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION
|
Intent.FLAG_GRANT_PREFIX_URI_PERMISSION or
|
||||||
|
Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||||
|
}, 1)
|
||||||
|
}
|
||||||
|
|
||||||
startActivityForResult(intent, 1)
|
private fun getDataItems() = mutableListOf<DataItem>().apply {
|
||||||
|
appEntries?.let { entries ->
|
||||||
|
val formats = formatFilter?.let { arrayOf(it) } ?: formatOrder
|
||||||
|
for (format in formats) {
|
||||||
|
entries[format]?.let {
|
||||||
|
add(HeaderItem(format.name))
|
||||||
|
it.forEach { entry -> add(AppItem(entry)) }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleState(state : MainState) = when (state) {
|
private fun handleState(state : MainState) = when (state) {
|
||||||
MainState.Loading -> {
|
MainState.Loading -> {
|
||||||
binding.searchBar.animateRefreshIcon()
|
binding.refreshIcon.animate().rotationBy(-180f)
|
||||||
binding.swipeRefreshLayout.isRefreshing = true
|
binding.swipeRefreshLayout.isRefreshing = true
|
||||||
}
|
}
|
||||||
|
|
||||||
is MainState.Loaded -> {
|
is MainState.Loaded -> {
|
||||||
binding.swipeRefreshLayout.isRefreshing = false
|
binding.swipeRefreshLayout.isRefreshing = false
|
||||||
|
|
||||||
val formatOrder = arrayOf(RomFormat.NSP, RomFormat.NRO, RomFormat.NSO, RomFormat.NCA)
|
appEntries = state.items
|
||||||
val items = mutableListOf<DataItem>()
|
populateAdapter()
|
||||||
for (format in formatOrder) {
|
|
||||||
state.items[format]?.let {
|
|
||||||
items.add(HeaderItem(format.name))
|
|
||||||
it.forEach { entry -> items.add(AppItem(entry)) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
populateAdapter(items)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
is MainState.Error -> Snackbar.make(findViewById(android.R.id.content), getString(R.string.error) + ": ${state.ex.localizedMessage}", Snackbar.LENGTH_SHORT).show()
|
is MainState.Error -> Snackbar.make(findViewById(android.R.id.content), getString(R.string.error) + ": ${state.ex.localizedMessage}", Snackbar.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -233,7 +276,8 @@ class MainActivity : AppCompatActivity() {
|
|||||||
settings.refreshRequired = false
|
settings.refreshRequired = false
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun populateAdapter(items : List<DataItem>) {
|
private fun populateAdapter() {
|
||||||
|
val items = getDataItems()
|
||||||
adapter.setItems(items.map {
|
adapter.setItems(items.map {
|
||||||
when (it) {
|
when (it) {
|
||||||
is HeaderItem -> HeaderViewItem(it.title)
|
is HeaderItem -> HeaderViewItem(it.title)
|
||||||
|
@ -15,8 +15,9 @@ import emu.skyline.utils.toFile
|
|||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.util.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
import kotlin.collections.HashMap
|
||||||
|
|
||||||
sealed class MainState {
|
sealed class MainState {
|
||||||
object Loading : MainState()
|
object Loading : MainState()
|
||||||
@ -50,7 +51,7 @@ class MainViewModel @Inject constructor(private val romProvider : RomProvider) :
|
|||||||
val romsFile = File(context.filesDir.canonicalPath + "/roms.bin")
|
val romsFile = File(context.filesDir.canonicalPath + "/roms.bin")
|
||||||
|
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
if (loadFromFile) {
|
if (loadFromFile && romsFile.exists()) {
|
||||||
try {
|
try {
|
||||||
state = MainState.Loaded(fromFile(romsFile))
|
state = MainState.Loaded(fromFile(romsFile))
|
||||||
return@launch
|
return@launch
|
||||||
@ -59,14 +60,19 @@ class MainViewModel @Inject constructor(private val romProvider : RomProvider) :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val romElements = romProvider.loadRoms(searchLocation)
|
state = if (searchLocation.toString().isEmpty()) {
|
||||||
|
@Suppress("ReplaceWithEnumMap")
|
||||||
|
MainState.Loaded(HashMap())
|
||||||
|
} else {
|
||||||
try {
|
try {
|
||||||
|
val romElements = romProvider.loadRoms(searchLocation)
|
||||||
romElements.toFile(romsFile)
|
romElements.toFile(romsFile)
|
||||||
} catch (e : IOException) {
|
MainState.Loaded(romElements)
|
||||||
|
} catch (e : Exception) {
|
||||||
Log.w(TAG, "Ran into exception while saving: ${e.message}")
|
Log.w(TAG, "Ran into exception while saving: ${e.message}")
|
||||||
|
MainState.Error(e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
state = MainState.Loaded(romElements)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
|
|
||||||
package emu.skyline.adapter
|
package emu.skyline.adapter
|
||||||
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.Filter
|
import android.widget.Filter
|
||||||
import android.widget.Filterable
|
import android.widget.Filterable
|
||||||
@ -101,7 +100,7 @@ class GenericAdapter : RecyclerView.Adapter<GenericViewHolder<ViewBinding>>(), F
|
|||||||
val avgScore = topResults.sumByDouble { it.score } / topResults.size
|
val avgScore = topResults.sumByDouble { it.score } / topResults.size
|
||||||
|
|
||||||
for (result in topResults)
|
for (result in topResults)
|
||||||
if (result.score > avgScore) filterData.add(result.item)
|
if (result.score >= avgScore) filterData.add(result.item)
|
||||||
|
|
||||||
results.values = filterData
|
results.values = filterData
|
||||||
results.count = filterData.size
|
results.count = filterData.size
|
||||||
|
@ -4,15 +4,10 @@ import android.content.Context
|
|||||||
import android.text.Editable
|
import android.text.Editable
|
||||||
import android.text.TextWatcher
|
import android.text.TextWatcher
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.util.TypedValue
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.inputmethod.InputMethodManager
|
import android.view.inputmethod.InputMethodManager
|
||||||
import androidx.core.view.MarginLayoutParamsCompat
|
|
||||||
import androidx.core.view.isInvisible
|
|
||||||
import androidx.core.view.isVisible
|
|
||||||
import com.google.android.material.card.MaterialCardView
|
import com.google.android.material.card.MaterialCardView
|
||||||
import emu.skyline.databinding.ViewSearchBarBinding
|
import emu.skyline.databinding.ViewSearchBarBinding
|
||||||
import kotlin.math.roundToInt
|
|
||||||
|
|
||||||
class SearchBarView @JvmOverloads constructor(context : Context, attrs : AttributeSet? = null, defStyleAttr : Int = com.google.android.material.R.attr.materialCardViewStyle) : MaterialCardView(context, attrs, defStyleAttr) {
|
class SearchBarView @JvmOverloads constructor(context : Context, attrs : AttributeSet? = null, defStyleAttr : Int = com.google.android.material.R.attr.materialCardViewStyle) : MaterialCardView(context, attrs, defStyleAttr) {
|
||||||
private val binding = ViewSearchBarBinding.inflate(LayoutInflater.from(context), this)
|
private val binding = ViewSearchBarBinding.inflate(LayoutInflater.from(context), this)
|
||||||
@ -21,32 +16,6 @@ class SearchBarView @JvmOverloads constructor(context : Context, attrs : Attribu
|
|||||||
useCompatPadding = true
|
useCompatPadding = true
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onAttachedToWindow() {
|
|
||||||
super.onAttachedToWindow()
|
|
||||||
|
|
||||||
val margin = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 16f, context.resources.displayMetrics).roundToInt()
|
|
||||||
MarginLayoutParamsCompat.setMarginStart(layoutParams as MarginLayoutParams?, margin)
|
|
||||||
MarginLayoutParamsCompat.setMarginEnd(layoutParams as MarginLayoutParams?, margin)
|
|
||||||
|
|
||||||
radius = margin / 2f
|
|
||||||
cardElevation = radius / 2f
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setRefreshIconListener(listener : OnClickListener) = binding.refreshIcon.setOnClickListener(listener)
|
|
||||||
fun setLogIconListener(listener : OnClickListener) = binding.logIcon.setOnClickListener(listener)
|
|
||||||
fun setSettingsIconListener(listener : OnClickListener) = binding.settingsIcon.setOnClickListener(listener)
|
|
||||||
|
|
||||||
var refreshIconVisible = false
|
|
||||||
set(visible) {
|
|
||||||
field = visible
|
|
||||||
binding.refreshIcon.apply {
|
|
||||||
if (visible != isVisible) {
|
|
||||||
binding.refreshIcon.alpha = if (visible) 0f else 1f
|
|
||||||
animate().alpha(if (visible) 1f else 0f).withStartAction { isVisible = true }.withEndAction { isInvisible = !visible }.apply { duration = 500 }.start()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var text : CharSequence
|
var text : CharSequence
|
||||||
get() = binding.searchField.text
|
get() = binding.searchField.text
|
||||||
set(value) = binding.searchField.setText(value)
|
set(value) = binding.searchField.setText(value)
|
||||||
@ -65,10 +34,6 @@ class SearchBarView @JvmOverloads constructor(context : Context, attrs : Attribu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun animateRefreshIcon() {
|
|
||||||
binding.refreshIcon.animate().rotationBy(-180f)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun addTextChangedListener(
|
fun addTextChangedListener(
|
||||||
beforeTextChanged : (
|
beforeTextChanged : (
|
||||||
text : CharSequence?,
|
text : CharSequence?,
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
android:layout_height="150dp"
|
android:layout_height="150dp"
|
||||||
android:contentDescription="@string/icon"
|
android:contentDescription="@string/icon"
|
||||||
android:focusable="false"
|
android:focusable="false"
|
||||||
app:shapeAppearanceOverlay="@style/roundedAppImage"
|
app:shapeAppearanceOverlay="@style/RoundedAppImage"
|
||||||
tools:src="@drawable/default_icon" />
|
tools:src="@drawable/default_icon" />
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
@ -11,12 +11,12 @@
|
|||||||
android:id="@+id/item_click_layout"
|
android:id="@+id/item_click_layout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="12dp"
|
android:layout_marginStart="14dp"
|
||||||
android:layout_marginTop="12dp"
|
android:layout_marginTop="14dp"
|
||||||
android:layout_marginEnd="12dp"
|
android:layout_marginEnd="14dp"
|
||||||
android:layout_marginBottom="12dp"
|
android:layout_marginBottom="14dp"
|
||||||
app:cardCornerRadius="16dp"
|
app:cardCornerRadius="16dp"
|
||||||
app:cardElevation="4dp">
|
app:cardElevation="2dp">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/icon"
|
android:id="@+id/icon"
|
||||||
|
@ -65,7 +65,6 @@
|
|||||||
android:layout_marginBottom="10dp"
|
android:layout_marginBottom="10dp"
|
||||||
android:max="100"
|
android:max="100"
|
||||||
android:progress="25"
|
android:progress="25"
|
||||||
android:secondaryProgressTint="@color/colorPrimaryDark"
|
|
||||||
android:secondaryProgressTintMode="screen"
|
android:secondaryProgressTintMode="screen"
|
||||||
android:visibility="gone" />
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
@ -18,14 +18,88 @@
|
|||||||
|
|
||||||
<com.google.android.material.appbar.CollapsingToolbarLayout
|
<com.google.android.material.appbar.CollapsingToolbarLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="62dp"
|
android:layout_height="wrap_content"
|
||||||
android:fitsSystemWindows="true"
|
android:fitsSystemWindows="true"
|
||||||
app:layout_scrollFlags="scroll">
|
app:layout_scrollFlags="scroll">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/refresh_icon"
|
||||||
|
android:layout_width="30dp"
|
||||||
|
android:layout_height="30dp"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginEnd="4dp"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
|
android:contentDescription="@string/refresh"
|
||||||
|
android:padding="5dp"
|
||||||
|
android:visibility="invisible"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/log_icon"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:srcCompat="@drawable/ic_refresh"
|
||||||
|
app:tint="?android:attr/textColorSecondary"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/log_icon"
|
||||||
|
android:layout_width="30dp"
|
||||||
|
android:layout_height="30dp"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginEnd="4dp"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
|
android:contentDescription="@string/log"
|
||||||
|
android:padding="5dp"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/settings_icon"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:srcCompat="@drawable/ic_log"
|
||||||
|
app:tint="?android:attr/textColorSecondary" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/settings_icon"
|
||||||
|
android:layout_width="30dp"
|
||||||
|
android:layout_height="30dp"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
|
android:contentDescription="@string/settings"
|
||||||
|
android:padding="5dp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:srcCompat="@drawable/ic_settings"
|
||||||
|
app:tint="?android:attr/textColorSecondary" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/title_text"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="18dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginEnd="18dp"
|
||||||
|
android:fontFamily="sans-serif-medium"
|
||||||
|
android:text="@string/app_name"
|
||||||
|
android:textColor="?android:attr/textColorPrimary"
|
||||||
|
android:textSize="32sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/settings_icon" />
|
||||||
|
|
||||||
<emu.skyline.views.SearchBarView
|
<emu.skyline.views.SearchBarView
|
||||||
android:id="@+id/search_bar"
|
android:id="@+id/search_bar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent" />
|
android:layout_height="64dp"
|
||||||
|
android:layout_marginStart="12dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginEnd="12dp"
|
||||||
|
app:cardCornerRadius="16dp"
|
||||||
|
app:cardElevation="2dp"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/title_text" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
</com.google.android.material.appbar.CollapsingToolbarLayout>
|
</com.google.android.material.appbar.CollapsingToolbarLayout>
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
@ -36,11 +110,44 @@
|
|||||||
android:layout_marginTop="-4dp"
|
android:layout_marginTop="-4dp"
|
||||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<HorizontalScrollView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:scrollbars="none">
|
||||||
|
|
||||||
|
<com.google.android.material.chip.ChipGroup
|
||||||
|
android:id="@+id/chip_group"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="18dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginEnd="18dp"
|
||||||
|
app:checkedChip="@id/all_chip"
|
||||||
|
app:chipSpacingHorizontal="16dp"
|
||||||
|
app:selectionRequired="true"
|
||||||
|
app:singleLine="true"
|
||||||
|
app:singleSelection="true">
|
||||||
|
|
||||||
|
<com.google.android.material.chip.Chip
|
||||||
|
android:id="@+id/all_chip"
|
||||||
|
style="?attr/chipChoiceStyle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/all" />
|
||||||
|
</com.google.android.material.chip.ChipGroup>
|
||||||
|
</HorizontalScrollView>
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/app_list"
|
android:id="@+id/app_list"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:clipToPadding="false"
|
android:clipToPadding="false"
|
||||||
android:paddingTop="4dp" />
|
android:paddingTop="4dp" />
|
||||||
|
</LinearLayout>
|
||||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
|
@ -101,7 +101,6 @@
|
|||||||
android:layout_marginBottom="10dp"
|
android:layout_marginBottom="10dp"
|
||||||
android:max="100"
|
android:max="100"
|
||||||
android:progress="25"
|
android:progress="25"
|
||||||
android:secondaryProgressTint="@color/colorPrimaryDark"
|
|
||||||
android:secondaryProgressTintMode="screen"
|
android:secondaryProgressTintMode="screen"
|
||||||
android:visibility="gone" />
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
@ -42,59 +42,12 @@
|
|||||||
android:textSize="16sp"
|
android:textSize="16sp"
|
||||||
app:layout_constrainedWidth="true"
|
app:layout_constrainedWidth="true"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toStartOf="@id/refresh_icon"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintHorizontal_bias="0"
|
app:layout_constraintHorizontal_bias="0"
|
||||||
app:layout_constraintStart_toEndOf="@id/search_icon"
|
app:layout_constraintStart_toEndOf="@id/search_icon"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:ignore="LabelFor" />
|
tools:ignore="LabelFor" />
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/refresh_icon"
|
|
||||||
android:layout_width="30dp"
|
|
||||||
android:layout_height="30dp"
|
|
||||||
android:layout_gravity="center_vertical"
|
|
||||||
android:layout_marginEnd="4dp"
|
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
|
||||||
android:contentDescription="@string/refresh"
|
|
||||||
android:padding="5dp"
|
|
||||||
android:visibility="invisible"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toStartOf="@id/log_icon"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:srcCompat="@drawable/ic_refresh"
|
|
||||||
app:tint="?android:attr/textColorSecondary"
|
|
||||||
tools:visibility="visible" />
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/log_icon"
|
|
||||||
android:layout_width="30dp"
|
|
||||||
android:layout_height="30dp"
|
|
||||||
android:layout_gravity="center_vertical"
|
|
||||||
android:layout_marginEnd="4dp"
|
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
|
||||||
android:contentDescription="@string/log"
|
|
||||||
android:padding="5dp"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toStartOf="@id/settings_icon"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:srcCompat="@drawable/ic_log"
|
|
||||||
app:tint="?android:attr/textColorSecondary" />
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/settings_icon"
|
|
||||||
android:layout_width="30dp"
|
|
||||||
android:layout_height="30dp"
|
|
||||||
android:layout_gravity="center_vertical"
|
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
|
||||||
android:contentDescription="@string/settings"
|
|
||||||
android:padding="5dp"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:srcCompat="@drawable/ic_settings"
|
|
||||||
app:tint="?android:attr/textColorSecondary" />
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/skyline_text"
|
android:id="@+id/skyline_text"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources>
|
|
||||||
|
|
||||||
<style name="AppTheme" parent="BaseAppTheme">
|
|
||||||
<item name="android:windowLightNavigationBar">false</item>
|
|
||||||
<item name="android:navigationBarColor">@color/colorPrimaryDark</item>
|
|
||||||
</style>
|
|
||||||
</resources>
|
|
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<color name="colorPrimary">#FF424242</color>
|
<color name="colorPrimary">#FF424242</color>
|
||||||
<color name="colorPrimaryDark">#FF121212</color>
|
<color name="colorPrimaryVariant">#FF121212</color>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
<resources>
|
|
||||||
|
|
||||||
<style name="BaseAppTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar">
|
|
||||||
<item name="colorPrimary">@color/colorPrimary</item>
|
|
||||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
|
||||||
<item name="colorAccent">@color/colorAccent</item>
|
|
||||||
<item name="android:windowLightStatusBar">false</item>
|
|
||||||
</style>
|
|
||||||
</resources>
|
|
8
app/src/main/res/values-night/themes.xml
Normal file
8
app/src/main/res/values-night/themes.xml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<style name="AppTheme" parent="BaseAppTheme">
|
||||||
|
<item name="android:windowLightNavigationBar" tools:targetApi="o_mr1">false</item>
|
||||||
|
<item name="android:windowLightStatusBar">false</item>
|
||||||
|
</style>
|
||||||
|
</resources>
|
@ -1,8 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources>
|
|
||||||
|
|
||||||
<style name="AppTheme" parent="BaseAppTheme">
|
|
||||||
<item name="android:windowLightNavigationBar">true</item>
|
|
||||||
<item name="android:navigationBarColor">@color/colorPrimaryDark</item>
|
|
||||||
</style>
|
|
||||||
</resources>
|
|
@ -1,6 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<declare-styleable name="CustomEditTextPreference">
|
<declare-styleable name="CustomEditTextPreference">
|
||||||
<attr name="limit" format="integer"/>
|
<attr name="limit" format="integer" />
|
||||||
</declare-styleable>
|
</declare-styleable>
|
||||||
|
<attr name="chipChoiceStyle" format="reference" />
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<color name="colorPrimary">@color/cardview_light_background</color>
|
<color name="colorPrimary">@color/cardview_light_background</color>
|
||||||
<color name="colorPrimaryDark">@android:color/white</color>
|
<color name="colorPrimaryVariant">@android:color/white</color>
|
||||||
<color name="colorAccent">#FFFF0000</color>
|
<color name="colorOnPrimary">#FFFF0000</color>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
<string name="log">Logger</string>
|
<string name="log">Logger</string>
|
||||||
<string name="refresh">Refresh</string>
|
<string name="refresh">Refresh</string>
|
||||||
<!-- Main -->
|
<!-- Main -->
|
||||||
|
<string name="all">All</string>
|
||||||
<string name="metadata_missing">Metadata Missing</string>
|
<string name="metadata_missing">Metadata Missing</string>
|
||||||
<string name="icon">Icon</string>
|
<string name="icon">Icon</string>
|
||||||
<string name="no_rom">Cannot find any ROMs</string>
|
<string name="no_rom">Cannot find any ROMs</string>
|
||||||
|
@ -1,16 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
|
|
||||||
<style name="BaseAppTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar">
|
<style name="RoundedAppImage">
|
||||||
<item name="colorPrimary">@color/colorPrimary</item>
|
|
||||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
|
||||||
<item name="colorAccent">@color/colorAccent</item>
|
|
||||||
<item name="android:windowLightStatusBar">true</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="AppTheme" parent="BaseAppTheme" />
|
|
||||||
|
|
||||||
<style name="roundedAppImage">
|
|
||||||
<item name="cornerFamily">rounded</item>
|
<item name="cornerFamily">rounded</item>
|
||||||
<item name="cornerSize">6dp</item>
|
<item name="cornerSize">6dp</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="ChipChoice.Material">
|
||||||
|
<item name="colorPrimary">@color/colorOnPrimary</item>
|
||||||
|
<item name="colorOnPrimary">@color/colorPrimary</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="ChipChoice" parent="Widget.MaterialComponents.Chip.Choice">
|
||||||
|
<item name="android:textAllCaps">true</item>
|
||||||
|
<item name="chipStartPadding">8dp</item>
|
||||||
|
<item name="chipEndPadding">8dp</item>
|
||||||
|
<item name="materialThemeOverlay">@style/ChipChoice.Material</item>
|
||||||
|
<item name="shapeAppearance">@style/ShapeAppearance.MaterialComponents.LargeComponent</item>
|
||||||
|
</style>
|
||||||
</resources>
|
</resources>
|
||||||
|
24
app/src/main/res/values/themes.xml
Normal file
24
app/src/main/res/values/themes.xml
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<style name="BaseAppTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar">
|
||||||
|
<item name="colorPrimary">@color/colorPrimary</item>
|
||||||
|
<item name="colorPrimaryVariant">@color/colorPrimaryVariant</item>
|
||||||
|
<item name="colorOnPrimary">@color/colorOnPrimary</item>
|
||||||
|
<item name="colorSecondary">@color/colorOnPrimary</item>
|
||||||
|
<item name="colorOnSecondary">@color/colorPrimary</item>
|
||||||
|
<item name="android:statusBarColor">@color/colorPrimaryVariant</item>
|
||||||
|
<item name="android:navigationBarColor">@color/colorPrimaryVariant</item>
|
||||||
|
|
||||||
|
<item name="chipChoiceStyle">@style/ChipChoice</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="AppTheme" parent="BaseAppTheme">
|
||||||
|
<item name="android:windowLightNavigationBar" tools:targetApi="o_mr1">true</item>
|
||||||
|
<item name="android:windowLightStatusBar">true</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="ToolbarTheme" parent="AppTheme">
|
||||||
|
<item name="colorPrimary">@color/colorPrimary</item>
|
||||||
|
</style>
|
||||||
|
</resources>
|
@ -53,12 +53,12 @@
|
|||||||
android:visibility="invisible"
|
android:visibility="invisible"
|
||||||
motion:layout_constrainedWidth="true"
|
motion:layout_constrainedWidth="true"
|
||||||
motion:layout_constraintBottom_toBottomOf="parent"
|
motion:layout_constraintBottom_toBottomOf="parent"
|
||||||
motion:layout_constraintEnd_toStartOf="@id/refresh_icon"
|
|
||||||
motion:layout_constraintHorizontal_bias="0"
|
motion:layout_constraintHorizontal_bias="0"
|
||||||
motion:layout_constraintStart_toEndOf="@id/search_icon"
|
motion:layout_constraintStart_toEndOf="@id/search_icon"
|
||||||
motion:layout_constraintTop_toTopOf="parent"
|
motion:layout_constraintTop_toTopOf="parent"
|
||||||
motion:transitionEasing="linear"
|
motion:transitionEasing="linear"
|
||||||
android:alpha="0" />
|
android:alpha="0"
|
||||||
|
motion:layout_constraintEnd_toEndOf="parent" />
|
||||||
</ConstraintSet>
|
</ConstraintSet>
|
||||||
|
|
||||||
<ConstraintSet android:id="@+id/end">
|
<ConstraintSet android:id="@+id/end">
|
||||||
@ -83,10 +83,10 @@
|
|||||||
android:visibility="visible"
|
android:visibility="visible"
|
||||||
motion:layout_constrainedWidth="true"
|
motion:layout_constrainedWidth="true"
|
||||||
motion:layout_constraintBottom_toBottomOf="parent"
|
motion:layout_constraintBottom_toBottomOf="parent"
|
||||||
motion:layout_constraintEnd_toStartOf="@id/refresh_icon"
|
|
||||||
motion:layout_constraintHorizontal_bias="0"
|
motion:layout_constraintHorizontal_bias="0"
|
||||||
motion:layout_constraintStart_toEndOf="@id/search_icon"
|
motion:layout_constraintStart_toEndOf="@id/search_icon"
|
||||||
motion:layout_constraintTop_toTopOf="parent"
|
motion:layout_constraintTop_toTopOf="parent"
|
||||||
android:alpha="1" />
|
android:alpha="1"
|
||||||
|
motion:layout_constraintEnd_toEndOf="parent" />
|
||||||
</ConstraintSet>
|
</ConstraintSet>
|
||||||
</MotionScene>
|
</MotionScene>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
ext.kotlin_version = '1.4.21'
|
ext.kotlin_version = '1.4.30'
|
||||||
ext.lifecycle_version = '2.2.0'
|
ext.lifecycle_version = '2.2.0'
|
||||||
ext.hilt_version = '2.31.2-alpha'
|
ext.hilt_version = '2.31.2-alpha'
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user