Introduce new AppDialog base components

This commit is contained in:
lynxnb 2022-02-23 18:09:27 +01:00
parent 67881d2215
commit 25ddc846bb
27 changed files with 1105 additions and 7 deletions

View File

@ -0,0 +1,99 @@
/*
* SPDX-License-Identifier: MPL-2.0
* Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
*/
package emu.skyline.adapter.appdialog
import android.view.View
import android.view.ViewGroup
import android.view.ViewTreeObserver
import emu.skyline.adapter.GenericListItem
import emu.skyline.adapter.ViewBindingFactory
import emu.skyline.adapter.inflater
import emu.skyline.databinding.AppDialogCheatsItemBinding
object AppDialogCheatsItemBindingFactory : ViewBindingFactory {
override fun createBinding(parent : ViewGroup) = AppDialogCheatsItemBinding.inflate(parent.inflater(), parent, false)
}
class CheatsViewItem(
private val title : String,
private val author : String?,
private val description : String?,
private val code : String,
var isEnabled : Boolean,
private val onClick : ((item : CheatsViewItem, position : Int) -> Unit)? = null
) : GenericListItem<AppDialogCheatsItemBinding>() {
companion object {
const val MAX_LINES = 2
}
private var isExpandable : Boolean? = null
private var isExpanded = false
override fun getViewBindingFactory() = AppDialogCheatsItemBindingFactory
override fun bind(binding : AppDialogCheatsItemBinding, position : Int) {
binding.title.text = title
author?.let {
binding.author.text = author
binding.author.visibility = View.VISIBLE
} ?: run { binding.author.visibility = View.GONE }
description?.let {
binding.description.text = it
binding.description.visibility = View.VISIBLE
} ?: run { binding.description.visibility = View.GONE }
binding.code.text = code
binding.checkbox.isChecked = isEnabled
handleExpand(binding, position)
binding.checkbox.setOnCheckedChangeListener { _, isChecked : Boolean ->
isEnabled = isChecked
onClick?.invoke(this, position)
}
}
private fun handleExpand(binding : AppDialogCheatsItemBinding, position : Int) {
val setupExpandButton = {
if (isExpandable == true) {
binding.expandButton.visibility = View.VISIBLE
binding.root.setOnClickListener {
isExpanded = !isExpanded
adapter?.notifyItemChanged(position)
}
} else {
binding.expandButton.visibility = View.GONE
}
}
isExpandable ?: binding.root.viewTreeObserver.addOnPreDrawListener(object : ViewTreeObserver.OnPreDrawListener {
override fun onPreDraw() : Boolean {
isExpandable = binding.title.layout.getEllipsisCount(0) > 0 ||
binding.author.layout.getEllipsisCount(0) > 0 ||
binding.description.layout.getEllipsisCount(MAX_LINES - 1) > 0 ||
code.isNotEmpty()
setupExpandButton.invoke()
binding.root.viewTreeObserver.removeOnPreDrawListener(this)
return true
}
})
if (isExpanded) {
binding.expandButton.rotation = 180F
binding.title.isSingleLine = false
binding.author.isSingleLine = false
binding.description.maxLines = Int.MAX_VALUE
binding.code.visibility = View.VISIBLE
} else {
binding.expandButton.rotation = 0F
binding.title.isSingleLine = true
binding.author.isSingleLine = true
binding.description.maxLines = MAX_LINES
binding.code.visibility = View.GONE
}
setupExpandButton.invoke()
}
}

View File

@ -0,0 +1,36 @@
/*
* SPDX-License-Identifier: MPL-2.0
* Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
*/
package emu.skyline.adapter.appdialog
import android.view.View
import android.view.ViewGroup
import emu.skyline.adapter.GenericListItem
import emu.skyline.adapter.ViewBindingFactory
import emu.skyline.adapter.inflater
import emu.skyline.databinding.AppDialogDlcsItemBinding
object AppDialogDlcsItemBindingFactory : ViewBindingFactory {
override fun createBinding(parent : ViewGroup) = AppDialogDlcsItemBinding.inflate(parent.inflater(), parent, false)
}
class DlcsViewItem(
private val title : String,
var isEnabled : Boolean,
private val onClick : ((item : DlcsViewItem, position : Int) -> Unit)? = null
) : GenericListItem<AppDialogDlcsItemBinding>() {
override fun getViewBindingFactory() = AppDialogDlcsItemBindingFactory
override fun bind(binding : AppDialogDlcsItemBinding, position : Int) {
binding.checkbox.text = title
binding.checkbox.isChecked = isEnabled
binding.checkbox.isSelected = true
binding.checkbox.setOnCheckedChangeListener { _ : View, isChecked : Boolean ->
isEnabled = isChecked
onClick?.invoke(this, position)
}
}
}

View File

@ -0,0 +1,26 @@
/*
* SPDX-License-Identifier: MPL-2.0
* Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
*/
package emu.skyline.adapter.appdialog
import android.view.View
import android.view.ViewGroup
import com.google.android.material.bottomsheet.BottomSheetBehavior
import emu.skyline.adapter.GenericListItem
import emu.skyline.adapter.ViewBindingFactory
import emu.skyline.adapter.inflater
import emu.skyline.databinding.AppDialogDragIndicatorBinding
object AppDialogDragIndicatorBindingFactory : ViewBindingFactory {
override fun createBinding(parent : ViewGroup) = AppDialogDragIndicatorBinding.inflate(parent.inflater(), parent, false)
}
class DragIndicatorViewItem(private val behavior : BottomSheetBehavior<View>) : GenericListItem<AppDialogDragIndicatorBinding>() {
override fun getViewBindingFactory() = AppDialogDragIndicatorBindingFactory
override fun bind(binding : AppDialogDragIndicatorBinding, position : Int) {
behavior.addBottomSheetCallback(binding.dragIndicator.callback)
}
}

View File

@ -0,0 +1,71 @@
/*
* SPDX-License-Identifier: MPL-2.0
* Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
*/
package emu.skyline.adapter.appdialog
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.pm.ShortcutInfo
import android.content.pm.ShortcutManager
import android.graphics.drawable.Icon
import android.view.View
import android.view.ViewGroup
import androidx.core.content.getSystemService
import androidx.core.graphics.drawable.toBitmap
import emu.skyline.EmulationActivity
import emu.skyline.R
import emu.skyline.adapter.GenericListItem
import emu.skyline.adapter.ViewBindingFactory
import emu.skyline.adapter.inflater
import emu.skyline.data.AppItem
import emu.skyline.databinding.AppDialogGameInfoBinding
import emu.skyline.loader.LoaderResult
object ControllerBindingFactory : ViewBindingFactory {
override fun createBinding(parent : ViewGroup) = AppDialogGameInfoBinding.inflate(parent.inflater(), parent, false)
}
class GameInfoViewItem(private val context : Context, private val item : AppItem, private val testedVersion : String?, private val rating : Int?) : GenericListItem<AppDialogGameInfoBinding>() {
override fun getViewBindingFactory() = ControllerBindingFactory
override fun bind(binding : AppDialogGameInfoBinding, position : Int) {
val missingIcon = context.getDrawable(R.drawable.default_icon)!!.toBitmap(256, 256)
binding.gameIcon.setImageBitmap(item.icon ?: missingIcon)
binding.gameTitle.text = item.title
binding.gameSubtitle.text = item.subTitle ?: item.loaderResultString(context)
// Make the title text view selected for marquee to work
binding.gameTitle.isSelected = true
binding.gameSubtitle.isSelected = true
binding.flex.visibility = if (rating == null && testedVersion == null) View.INVISIBLE else View.VISIBLE
binding.ratingBar.rating = (rating ?: 0).toFloat()
binding.testedVersion.text = testedVersion?.let { context.getString(R.string.tested_on, it) } ?: context.getString(R.string.not_tested)
binding.gamePlay.isEnabled = item.loaderResult == LoaderResult.Success
binding.gamePlay.setOnClickListener {
context.startActivity(Intent(context, EmulationActivity::class.java).apply { data = item.uri })
}
val shortcutManager = context.getSystemService<ShortcutManager>()!!
binding.gamePin.isEnabled = shortcutManager.isRequestPinShortcutSupported
binding.gamePin.setOnClickListener {
val info = ShortcutInfo.Builder(context, item.title)
info.setShortLabel(item.title)
info.setActivity(ComponentName(context, EmulationActivity::class.java))
info.setIcon(Icon.createWithAdaptiveBitmap(item.icon ?: missingIcon))
val intent = Intent(context, EmulationActivity::class.java)
intent.data = item.uri
intent.action = Intent.ACTION_VIEW
info.setIntent(intent)
shortcutManager.requestPinShortcut(info.build(), null)
}
}
}

View File

@ -0,0 +1,70 @@
/*
* SPDX-License-Identifier: MPL-2.0
* Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
*/
package emu.skyline.adapter.appdialog
import android.view.View
import android.view.ViewGroup
import android.view.ViewTreeObserver
import emu.skyline.adapter.GenericListItem
import emu.skyline.adapter.ViewBindingFactory
import emu.skyline.adapter.inflater
import emu.skyline.databinding.AppDialogIssuesItemBinding
object AppDialogIssuesItemBindingFactory : ViewBindingFactory {
override fun createBinding(parent : ViewGroup) = AppDialogIssuesItemBinding.inflate(parent.inflater(), parent, false)
}
class IssuesViewItem(private val title : String, private val description : String?) : GenericListItem<AppDialogIssuesItemBinding>() {
private var isExpandable : Boolean? = null
private var isExpanded = false
override fun getViewBindingFactory() = AppDialogIssuesItemBindingFactory
override fun bind(binding : AppDialogIssuesItemBinding, position : Int) {
binding.title.text = title
description?.let {
binding.description.text = it
binding.description.visibility = View.VISIBLE
} ?: run { binding.description.visibility = View.GONE }
handleExpand(binding, position)
}
private fun handleExpand(binding : AppDialogIssuesItemBinding, position : Int) {
val setupExpandButton = {
if (isExpandable == true) {
binding.expandButton.visibility = View.VISIBLE
binding.root.setOnClickListener {
isExpanded = !isExpanded
adapter?.notifyItemChanged(position)
}
} else {
binding.expandButton.visibility = View.GONE
}
}
isExpandable ?: binding.root.viewTreeObserver.addOnPreDrawListener(object : ViewTreeObserver.OnPreDrawListener {
override fun onPreDraw() : Boolean {
isExpandable = binding.title.layout.getEllipsisCount(0) > 0 || description?.isNotEmpty() == true
setupExpandButton.invoke()
binding.root.viewTreeObserver.removeOnPreDrawListener(this)
return true
}
})
if (isExpanded) {
binding.expandButton.rotation = 180F
binding.title.isSingleLine = false
binding.description.visibility = View.VISIBLE
} else {
binding.expandButton.rotation = 0F
binding.title.isSingleLine = true
binding.description.visibility = View.GONE
}
setupExpandButton.invoke()
}
}

View File

@ -0,0 +1,58 @@
/*
* SPDX-License-Identifier: MPL-2.0
* Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
*/
package emu.skyline.adapter.appdialog
import android.view.ViewGroup
import android.view.ViewTreeObserver
import emu.skyline.adapter.GenericListItem
import emu.skyline.adapter.ViewBindingFactory
import emu.skyline.adapter.inflater
import emu.skyline.databinding.AppDialogNotesItemBinding
object AppDialogNotesItemBindingFactory : ViewBindingFactory {
override fun createBinding(parent : ViewGroup) = AppDialogNotesItemBinding.inflate(parent.inflater(), parent, false)
}
class NotesViewItem(private val title : String) : GenericListItem<AppDialogNotesItemBinding>() {
companion object {
const val MAX_LINES = 3
}
private var isExpandable : Boolean? = null
private var isExpanded = false
override fun getViewBindingFactory() = AppDialogNotesItemBindingFactory
override fun bind(binding : AppDialogNotesItemBinding, position : Int) {
binding.title.text = title
handleExpand(binding, position)
}
private fun handleExpand(binding : AppDialogNotesItemBinding, position : Int) {
val setupExpandButton = {
if (isExpandable == true) {
binding.root.setOnClickListener {
isExpanded = !isExpanded
adapter?.notifyItemChanged(position)
}
}
}
isExpandable ?: binding.root.viewTreeObserver.addOnPreDrawListener(object : ViewTreeObserver.OnPreDrawListener {
override fun onPreDraw() : Boolean {
isExpandable = binding.title.layout.getEllipsisCount(MAX_LINES - 1) > 0
setupExpandButton.invoke()
binding.root.viewTreeObserver.removeOnPreDrawListener(this)
return true
}
})
binding.title.maxLines = if (isExpanded) Int.MAX_VALUE else MAX_LINES
setupExpandButton.invoke()
}
}

View File

@ -0,0 +1,85 @@
/*
* SPDX-License-Identifier: MPL-2.0
* Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
*/
package emu.skyline.adapter.appdialog
import android.os.Handler
import android.os.Looper
import android.view.View
import android.view.ViewGroup
import android.view.ViewTreeObserver
import emu.skyline.adapter.GenericListItem
import emu.skyline.adapter.ViewBindingFactory
import emu.skyline.adapter.inflater
import emu.skyline.databinding.AppDialogSavesItemBinding
object AppDialogSavesItemBindingFactory : ViewBindingFactory {
override fun createBinding(parent : ViewGroup) = AppDialogSavesItemBinding.inflate(parent.inflater(), parent, false)
}
class SavesViewItem(
private val title : String,
private val author : String,
private val description : String,
private val onClick : ((item : SavesViewItem, position : Int) -> Unit)? = null
) : GenericListItem<AppDialogSavesItemBinding>() {
companion object {
const val MAX_LINES = 2
}
private var isExpandable : Boolean? = null
private var isExpanded = false
private var isActive = false
override fun getViewBindingFactory() = AppDialogSavesItemBindingFactory
override fun bind(binding : AppDialogSavesItemBinding, position : Int) {
binding.title.text = title
binding.author.text = author
binding.description.text = description
binding.progress.isIndeterminate = true // temp
handleExpand(binding, position)
}
private fun handleExpand(binding : AppDialogSavesItemBinding, position : Int) {
val setupExpandButton = {
if (isExpandable == true) {
binding.expandButton.visibility = View.VISIBLE
binding.root.setOnClickListener {
isExpanded = !isExpanded
adapter?.notifyItemChanged(position)
}
} else {
binding.expandButton.visibility = View.GONE
}
}
isExpandable ?: binding.root.viewTreeObserver.addOnPreDrawListener(object : ViewTreeObserver.OnPreDrawListener {
override fun onPreDraw() : Boolean {
isExpandable = binding.title.layout.getEllipsisCount(0) > 0 ||
binding.author.layout.getEllipsisCount(0) > 0 ||
binding.description.layout.getEllipsisCount(MAX_LINES - 1) > 0
setupExpandButton.invoke()
binding.root.viewTreeObserver.removeOnPreDrawListener(this)
return true
}
})
if (isExpanded) {
binding.expandButton.rotation = 180F
binding.title.isSingleLine = false
binding.author.isSingleLine = false
binding.description.maxLines = Int.MAX_VALUE
} else {
binding.expandButton.rotation = 0F
binding.title.isSingleLine = true
binding.author.isSingleLine = true
binding.description.maxLines = MAX_LINES
}
setupExpandButton.invoke()
}
}

View File

@ -0,0 +1,42 @@
/*
* SPDX-License-Identifier: MPL-2.0
* Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
*/
package emu.skyline.adapter.appdialog
import android.view.View
import android.view.ViewGroup
import emu.skyline.adapter.GenericListItem
import emu.skyline.adapter.ViewBindingFactory
import emu.skyline.adapter.inflater
import emu.skyline.databinding.AppDialogSectionHeaderBinding
object AppDialogSectionHeaderBindingFactory : ViewBindingFactory {
override fun createBinding(parent : ViewGroup) = AppDialogSectionHeaderBinding.inflate(parent.inflater(), parent, false)
}
class SectionHeaderViewItem(
private val title : String,
private val attribute : String? = null,
private val onButtonClick : ((item : SectionHeaderViewItem, position : Int) -> Unit)? = null
) : GenericListItem<AppDialogSectionHeaderBinding>() {
override fun getViewBindingFactory() = AppDialogSectionHeaderBindingFactory
override fun bind(binding : AppDialogSectionHeaderBinding, position : Int) {
binding.title.text = title
if (attribute != null) {
binding.subtitle.text = attribute
binding.subtitle.visibility = View.VISIBLE
} else {
binding.subtitle.visibility = View.GONE
}
if (onButtonClick != null) {
binding.button.setOnClickListener { onButtonClick.invoke(this, position) }
binding.button.visibility = View.VISIBLE
} else {
binding.button.visibility = View.GONE
}
}
}

View File

@ -0,0 +1,38 @@
/*
* SPDX-License-Identifier: MPL-2.0
* Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
*/
package emu.skyline.adapter.appdialog
import android.view.View
import android.view.ViewGroup
import emu.skyline.adapter.GenericListItem
import emu.skyline.adapter.ViewBindingFactory
import emu.skyline.adapter.inflater
import emu.skyline.databinding.AppDialogUpdatesItemBinding
object AppDialogUpdatesItemBindingFactory : ViewBindingFactory {
override fun createBinding(parent : ViewGroup) = AppDialogUpdatesItemBinding.inflate(parent.inflater(), parent, false)
}
class UpdatesViewItem(
private val title : String,
private val onClick : ((item : UpdatesViewItem, position : Int) -> Unit)? = null
) : GenericListItem<AppDialogUpdatesItemBinding>() {
override fun getViewBindingFactory() = AppDialogUpdatesItemBindingFactory
override fun bind(binding : AppDialogUpdatesItemBinding, position : Int) {
binding.radioButton.text = title
binding.radioButton.isChecked = adapter?.selectedPosition == position
binding.radioButton.isSelected = true
binding.radioButton.setOnCheckedChangeListener { _ : View, isChecked : Boolean ->
if (isChecked) {
adapter?.selectedPosition = position
onClick?.invoke(this, position)
adapter?.itemCount?.let { adapter?.notifyItemRangeChanged(0, it) }
}
}
}
}

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
<solid android:color="@color/onBackgroundLight" />
<corners android:radius="@dimen/corner_radius" />
</shape>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<size
android:width="5dp"
android:height="5dp" />
<solid android:color="@android:color/tertiary_text_light" />
</shape>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?attr/colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M16.59,8.59L12,13.17 7.41,8.59 6,10l6,6 6,-6z" />
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?attr/colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M18,15v3H6v-3H4v3c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2v-3H18zM17,11l-1.41,-1.41L13,12.17V4h-2v8.17L8.41,9.59L7,11l5,5L17,11z" />
</vector>

View File

@ -1,9 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="?attr/colorOnSecondary"
android:pathData="M19,19H5V5h7V3H5c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2v-7h-2v7zM14,3v2h3.59l-9.83,9.83 1.41,1.41L19,6.41V10h2V3h-7z" />
android:fillColor="?attr/colorOnSecondary"
android:pathData="M19,19H5V5h7V3H5c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2v-7h-2v7zM14,3v2h3.59l-9.83,9.83 1.41,1.41L19,6.41V10h2V3h-7z" />
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?attr/colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M8,6h8c1.1,0 2,0.9 2,2v8c0,1.1 -0.9,2 -2,2H8c-1.1,0 -2,-0.9 -2,-2V8c0,-1.1 0.9,-2 2,-2z" />
</vector>

View File

@ -0,0 +1,100 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
style="@style/Widget.App.OutlinedCard"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/section_item_card_padding">
<com.google.android.material.checkbox.MaterialCheckBox
android:id="@+id/checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="0dp"
android:minHeight="0dp"
app:layout_constraintBottom_toBottomOf="@id/author"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/title"
tools:checked="true" />
<TextView
android:id="@+id/title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:ellipsize="end"
android:includeFontPadding="false"
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1"
app:layout_constraintEnd_toStartOf="@id/expand_button"
app:layout_constraintStart_toEndOf="@id/checkbox"
app:layout_constraintTop_toTopOf="@id/expand_button"
tools:singleLine="false"
tools:text="Cheat Benchmark Cheat Benchmark Cheat Benchmark Cheat Benchmark" />
<ImageView
android:id="@+id/expand_button"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="8dp"
android:contentDescription="@string/expand_button_title"
android:src="@drawable/expand_indicator"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/title"
app:layout_constraintTop_toTopOf="parent"
app:tint="?android:attr/textColorPrimary"
tools:rotation="180" />
<TextView
android:id="@+id/author"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:ellipsize="end"
android:fontFamily="sans-serif-medium"
android:includeFontPadding="false"
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1"
android:textColor="?android:attr/textColorSecondary"
android:textSize="14sp"
android:textStyle="italic"
app:layout_constraintEnd_toEndOf="@id/title"
app:layout_constraintStart_toStartOf="@id/title"
app:layout_constraintTop_toBottomOf="@id/title"
tools:singleLine="true"
tools:text="by Mark" />
<TextView
android:id="@+id/description"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:ellipsize="end"
android:includeFontPadding="false"
app:layout_constraintEnd_toEndOf="@id/expand_button"
app:layout_constraintStart_toStartOf="@id/author"
app:layout_constraintTop_toBottomOf="@id/author"
tools:maxLines="2"
tools:text="A cheat opcode benchmark" />
<TextView
android:id="@+id/code"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:background="@drawable/code_block"
android:ellipsize="end"
android:fontFamily="monospace"
android:includeFontPadding="false"
android:padding="8dp"
android:textSize="12sp"
app:layout_constraintEnd_toEndOf="@id/expand_button"
app:layout_constraintStart_toStartOf="@id/title"
app:layout_constraintTop_toBottomOf="@id/description"
tools:text="00000000 00000000 00000000\n00000000 00000000 00000000\n00000000 00000000 00000000\n00000000 00000000 00000000\n00000000 00000000 00000000" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.checkbox.MaterialCheckBox xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:marqueeRepeatLimit="marquee_forever"
android:minHeight="0dp"
android:singleLine="true"
android:textSize="15sp"
tools:text="Booster Course Pass" />

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<emu.skyline.views.DragIndicatorView
android:id="@+id/drag_indicator"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:contentDescription="@null"
android:focusable="false"
android:paddingTop="10dp" />
</FrameLayout>

View File

@ -0,0 +1,127 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:animateLayoutChanges="true">
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/game_icon"
android:layout_width="130dp"
android:layout_height="130dp"
android:contentDescription="@string/icon"
android:focusable="false"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:shapeAppearance="?attr/shapeAppearanceSmallComponent"
tools:src="@drawable/default_icon" />
<TextView
android:id="@+id/game_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:ellipsize="marquee"
android:marqueeRepeatLimit="marquee_forever"
android:singleLine="true"
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"
android:textSize="18sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/game_icon"
app:layout_constraintTop_toTopOf="parent"
tools:text="The Legend of Zelda: Breath of the Wild" />
<TextView
android:id="@+id/game_subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:marqueeRepeatLimit="marquee_forever"
android:singleLine="true"
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle2"
android:textColor="@android:color/tertiary_text_light"
android:textSize="14sp"
app:layout_constraintStart_toStartOf="@id/game_title"
app:layout_constraintTop_toBottomOf="@id/game_title"
tools:text="Nintendo" />
<com.google.android.flexbox.FlexboxLayout
android:id="@+id/flex"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:visibility="invisible"
app:alignItems="center"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/game_title"
app:layout_constraintTop_toBottomOf="@id/game_subtitle"
tools:visibility="visible">
<RatingBar
android:id="@+id/rating_bar"
style="?attr/ratingBarStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:max="5"
android:min="0"
android:numStars="5"
android:stepSize="1"
tools:rating="3" />
<ImageView
android:id="@+id/dot_separator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@null"
android:paddingHorizontal="6dp"
android:src="@drawable/dot_separator" />
<TextView
android:id="@+id/tested_version"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:includeFontPadding="false"
android:marqueeRepeatLimit="marquee_forever"
android:singleLine="true"
android:textColor="@android:color/tertiary_text_light"
tools:text="Tested on 1.0.0" />
</com.google.android.flexbox.FlexboxLayout>
<com.google.android.material.button.MaterialButton
android:id="@+id/game_play"
style="@style/Widget.MaterialComponents.Button.Icon"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:focusedByDefault="true"
android:stateListAnimator="@null"
android:text="@string/play"
app:icon="@drawable/ic_play"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/game_pin"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="@id/game_title"
app:layout_constraintTop_toBottomOf="@+id/flex"
app:layout_constraintWidth_max="146dp" />
<com.google.android.material.button.MaterialButton
android:id="@+id/game_pin"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="55dp"
android:layout_height="0dp"
android:layout_marginStart="12dp"
android:layout_marginEnd="12dp"
android:contentDescription="@string/pin_to_homescreen"
app:icon="@drawable/ic_add_home"
app:iconGravity="textStart"
app:iconPadding="0dp"
app:layout_constraintBottom_toBottomOf="@id/game_play"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/game_play"
app:layout_constraintTop_toTopOf="@id/game_play" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
style="@style/Widget.App.OutlinedCard"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/section_item_card_padding">
<TextView
android:id="@+id/title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:ellipsize="end"
android:includeFontPadding="false"
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1"
app:layout_constraintEnd_toStartOf="@id/expand_button"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/expand_button"
tools:singleLine="true"
tools:text="Chain Chompikins isn't golden" />
<ImageView
android:id="@+id/expand_button"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="8dp"
android:contentDescription="@string/expand_button_title"
android:src="@drawable/expand_indicator"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/title"
app:layout_constraintTop_toTopOf="parent"
app:tint="?android:attr/textColorPrimary"
tools:rotation="180" />
<TextView
android:id="@+id/description"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:includeFontPadding="false"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="@id/expand_button"
app:layout_constraintStart_toStartOf="@id/title"
app:layout_constraintTop_toBottomOf="@id/title"
tools:text="Madame Broode's Chain Chompikins from Cascade Kingdom which should appear golden is yellow instead"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
style="@style/Widget.App.OutlinedCard"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/section_item_card_padding">
<TextView
android:id="@+id/title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:ellipsize="end"
android:includeFontPadding="false"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:maxLines="2"
tools:text="Don't change the system time, it'll invalidate your saves." />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>

View File

@ -0,0 +1,114 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
style="@style/Widget.App.OutlinedCard"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/section_item_card_padding">
<com.google.android.material.progressindicator.CircularProgressIndicator
android:id="@+id/progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:max="100"
android:minWidth="0dp"
android:minHeight="0dp"
android:progress="65"
android:visibility="invisible"
app:indicatorSize="32dp"
app:layout_constraintBottom_toBottomOf="@id/author"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/title"
app:trackThickness="3dp" />
<ImageView
android:id="@+id/download_button"
android:layout_width="0dp"
android:layout_height="0dp"
android:contentDescription="@null"
android:padding="4dp"
android:src="@drawable/ic_download"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="@id/progress"
app:layout_constraintEnd_toEndOf="@id/progress"
app:layout_constraintStart_toStartOf="@id/progress"
app:layout_constraintTop_toTopOf="@id/progress" />
<ImageView
android:id="@+id/stop_button"
android:layout_width="0dp"
android:layout_height="0dp"
android:contentDescription="@null"
android:padding="10dp"
android:src="@drawable/ic_stop"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="@id/progress"
app:layout_constraintEnd_toEndOf="@id/progress"
app:layout_constraintStart_toStartOf="@id/progress"
app:layout_constraintTop_toTopOf="@id/progress" />
<TextView
android:id="@+id/title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:ellipsize="end"
android:includeFontPadding="false"
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1"
app:layout_constraintEnd_toStartOf="@id/expand_button"
app:layout_constraintStart_toEndOf="@id/progress"
app:layout_constraintTop_toTopOf="@id/expand_button"
tools:singleLine="false"
tools:text="Cap Kingdom Cap Kingdom Cap Kingdom Cap Kingdom Cap Kingdom Cap Kingdom" />
<ImageView
android:id="@+id/expand_button"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="8dp"
android:contentDescription="@string/expand_button_title"
android:src="@drawable/expand_indicator"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/title"
app:layout_constraintTop_toTopOf="parent"
app:tint="?android:attr/textColorPrimary"
tools:rotation="180" />
<TextView
android:id="@+id/author"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:ellipsize="end"
android:fontFamily="sans-serif-medium"
android:includeFontPadding="false"
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1"
android:textColor="?android:attr/textColorSecondary"
android:textSize="14sp"
android:textStyle="italic"
app:layout_constraintEnd_toEndOf="@id/title"
app:layout_constraintStart_toStartOf="@id/title"
app:layout_constraintTop_toBottomOf="@id/title"
tools:singleLine="true"
tools:text="by WillFaustCuber" />
<TextView
android:id="@+id/description"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:ellipsize="end"
android:includeFontPadding="false"
app:layout_constraintEnd_toEndOf="@id/expand_button"
app:layout_constraintStart_toStartOf="@id/author"
app:layout_constraintTop_toBottomOf="@id/author"
tools:maxLines="2"
tools:text="At the start of the game, right after the intro" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>

View File

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="bottom"
android:orientation="horizontal"
android:paddingTop="@dimen/section_title_padding">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/SectionTitle"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Title" />
<TextView
android:id="@+id/subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:textAppearance="@style/SectionTitle.Attribute"
app:layout_constraintBottom_toBottomOf="@id/title"
app:layout_constraintStart_toEndOf="@id/title"
tools:text="Subtitle" />
<ImageButton
android:id="@+id/button"
style="@style/Widget.MaterialComponents.Button.UnelevatedButton"
android:layout_width="18dp"
android:layout_height="0dp"
android:background="?android:attr/actionBarItemBackground"
android:contentDescription="@string/open_external"
android:src="@drawable/ic_open"
app:layout_constraintBottom_toBottomOf="@id/title"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/title"
app:tint="?attr/colorAccent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.radiobutton.MaterialRadioButton xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/radio_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:marqueeRepeatLimit="marquee_forever"
android:minHeight="0dp"
android:singleLine="true"
android:textSize="15sp"
tools:text="1.3.0" />

View File

@ -3,4 +3,9 @@
<dimen name="grid_padding">8dp</dimen>
<dimen name="corner_radius">6dp</dimen>
<dimen name="corner_radius_large">12dp</dimen>
<!-- Main - AppDialog -->
<dimen name="section_spacing">10dp</dimen>
<dimen name="section_title_padding">6dp</dimen>
<dimen name="section_item_card_padding">12dp</dimen>
</resources>

View File

@ -21,6 +21,20 @@
<string name="invalid_file">Invalid file</string>
<string name="missing_title_key">Missing title key</string>
<string name="incomplete_prod_keys">Incomplete production keys</string>
<!-- Main - AppDialog -->
<string name="updates">Updates</string>
<string name="dlcs">DLCs</string>
<string name="issues">Issues</string>
<string name="notes">Notes</string>
<string name="cheats">Cheats</string>
<string name="saves">Saves</string>
<string name="no_updates">No updates available</string>
<string name="no_dlcs">No DLCs available</string>
<string name="pin_to_homescreen">Pin to homescreen</string>
<string name="open_external">Open external link</string>
<string name="apply">Apply</string>
<string name="tested_on">Tested on %1$s</string>
<string name="not_tested">Not tested</string>
<!-- Settings - Emulator -->
<string name="emulator">Emulator</string>
<string name="search_location">Search Location</string>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<resources xmlns:tools="http://schemas.android.com/tools">
<style name="SectionTitle" parent="TextAppearance.MaterialComponents.Headline6">
<item name="android:textSize">16sp</item>
@ -45,4 +45,12 @@
<item name="chipEndPadding">8dp</item>
<item name="shapeAppearance">@style/ShapeAppearance.MaterialComponents.LargeComponent</item>
</style>
<style name="Widget.App.OutlinedCard" parent="Widget.MaterialComponents.CardView">
<item name="cardBackgroundColor">@android:color/transparent</item>
<item name="cardElevation">0dp</item>
<item name="strokeColor" tools:ignore="PrivateResource">@color/mtrl_btn_stroke_color_selector</item>
<item name="strokeWidth">1dp</item>
<item name="android:clickable">true</item>
</style>
</resources>