mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-11-25 08:46:54 +01:00
WIP
This commit is contained in:
parent
8360cdf82e
commit
b443287007
@ -2,6 +2,7 @@ plugins {
|
|||||||
id 'com.android.application'
|
id 'com.android.application'
|
||||||
id 'kotlin-android'
|
id 'kotlin-android'
|
||||||
id 'kotlin-kapt'
|
id 'kotlin-kapt'
|
||||||
|
id 'kotlinx-serialization'
|
||||||
id 'dagger.hilt.android.plugin'
|
id 'dagger.hilt.android.plugin'
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,6 +93,7 @@ dependencies {
|
|||||||
implementation 'androidx.appcompat:appcompat:1.3.1'
|
implementation 'androidx.appcompat:appcompat:1.3.1'
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.1'
|
implementation 'androidx.constraintlayout:constraintlayout:2.1.1'
|
||||||
implementation 'androidx.preference:preference-ktx:1.1.1'
|
implementation 'androidx.preference:preference-ktx:1.1.1'
|
||||||
|
implementation "androidx.recyclerview:recyclerview:1.2.1"
|
||||||
implementation 'com.google.android.material:material:1.4.0'
|
implementation 'com.google.android.material:material:1.4.0'
|
||||||
implementation 'androidx.documentfile:documentfile:1.0.1'
|
implementation 'androidx.documentfile:documentfile:1.0.1'
|
||||||
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
|
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
|
||||||
@ -104,9 +106,12 @@ dependencies {
|
|||||||
|
|
||||||
/* JetBrains */
|
/* JetBrains */
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||||
|
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2"
|
||||||
|
|
||||||
/* Other Java */
|
/* Other Java */
|
||||||
implementation 'info.debatty:java-string-similarity:2.0.0'
|
implementation 'info.debatty:java-string-similarity:2.0.0'
|
||||||
|
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
|
||||||
|
implementation "com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:0.8.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
kapt {
|
kapt {
|
||||||
|
28
app/proguard-rules.pro
vendored
28
app/proguard-rules.pro
vendored
@ -4,3 +4,31 @@
|
|||||||
|
|
||||||
# Retain all classes within Skyline for traces + JNI access + Serializable classes
|
# Retain all classes within Skyline for traces + JNI access + Serializable classes
|
||||||
-keep class emu.skyline.** { *; }
|
-keep class emu.skyline.** { *; }
|
||||||
|
|
||||||
|
# Kotlinx Serialization rules
|
||||||
|
# Keep `Companion` object fields of serializable classes.
|
||||||
|
# This avoids serializer lookup through `getDeclaredClasses` as done for named companion objects.
|
||||||
|
-if @kotlinx.serialization.Serializable class **
|
||||||
|
-keepclassmembers class <1> {
|
||||||
|
static <1>$Companion Companion;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Keep `serializer()` on companion objects (both default and named) of serializable classes.
|
||||||
|
-if @kotlinx.serialization.Serializable class ** {
|
||||||
|
static **$* *;
|
||||||
|
}
|
||||||
|
-keepclassmembers class <2>$<3> {
|
||||||
|
kotlinx.serialization.KSerializer serializer(...);
|
||||||
|
}
|
||||||
|
|
||||||
|
# Keep `INSTANCE.serializer()` of serializable objects.
|
||||||
|
-if @kotlinx.serialization.Serializable class ** {
|
||||||
|
public static ** INSTANCE;
|
||||||
|
}
|
||||||
|
-keepclassmembers class <1> {
|
||||||
|
public static <1> INSTANCE;
|
||||||
|
kotlinx.serialization.KSerializer serializer(...);
|
||||||
|
}
|
||||||
|
|
||||||
|
# @Serializable and @Polymorphic are used at runtime for polymorphic serialization.
|
||||||
|
-keepattributes RuntimeVisibleAnnotations,AnnotationDefault
|
||||||
|
@ -5,23 +5,32 @@
|
|||||||
|
|
||||||
package emu.skyline
|
package emu.skyline
|
||||||
|
|
||||||
import android.content.ComponentName
|
import android.graphics.Rect
|
||||||
import android.content.Intent
|
|
||||||
import android.content.pm.ShortcutInfo
|
|
||||||
import android.content.pm.ShortcutManager
|
|
||||||
import android.graphics.drawable.Icon
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.KeyEvent
|
import android.view.KeyEvent
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.annotation.StringRes
|
||||||
import androidx.core.graphics.drawable.toBitmap
|
import androidx.recyclerview.widget.*
|
||||||
|
import androidx.viewbinding.ViewBinding
|
||||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||||
|
import com.google.android.material.snackbar.Snackbar
|
||||||
|
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
|
||||||
|
import emu.skyline.adapter.GenericAdapter
|
||||||
|
import emu.skyline.adapter.GenericListItem
|
||||||
|
import emu.skyline.adapter.appdialog.*
|
||||||
import emu.skyline.data.AppItem
|
import emu.skyline.data.AppItem
|
||||||
import emu.skyline.databinding.AppDialogBinding
|
import emu.skyline.databinding.AppDialogBinding
|
||||||
import emu.skyline.loader.LoaderResult
|
import emu.skyline.network.TitleMetaData
|
||||||
|
import emu.skyline.network.TitleMetaService
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import okhttp3.MediaType
|
||||||
|
import retrofit2.Call
|
||||||
|
import retrofit2.Callback
|
||||||
|
import retrofit2.Response
|
||||||
|
import retrofit2.Retrofit
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This dialog is used to show extra game metadata and provide extra options such as pinning the game to the home screen
|
* This dialog is used to show extra game metadata and provide extra options such as pinning the game to the home screen
|
||||||
@ -43,6 +52,12 @@ class AppDialog : BottomSheetDialogFragment() {
|
|||||||
|
|
||||||
private lateinit var binding : AppDialogBinding
|
private lateinit var binding : AppDialogBinding
|
||||||
|
|
||||||
|
private val baseAdapter = ConcatAdapter()
|
||||||
|
private val gameInfoAdapter = GenericAdapter()
|
||||||
|
private val updatesAdapter = GenericAdapter()
|
||||||
|
private val dlcsAdapter = GenericAdapter()
|
||||||
|
private val tlmdAdapter = GenericAdapter()
|
||||||
|
|
||||||
private val item by lazy { requireArguments().getSerializable("item") as AppItem }
|
private val item by lazy { requireArguments().getSerializable("item") as AppItem }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -56,9 +71,6 @@ class AppDialog : BottomSheetDialogFragment() {
|
|||||||
override fun onStart() {
|
override fun onStart() {
|
||||||
super.onStart()
|
super.onStart()
|
||||||
|
|
||||||
val behavior = BottomSheetBehavior.from(requireView().parent as View)
|
|
||||||
behavior.state = BottomSheetBehavior.STATE_EXPANDED
|
|
||||||
|
|
||||||
dialog?.setOnKeyListener { _, keyCode, event ->
|
dialog?.setOnKeyListener { _, keyCode, event ->
|
||||||
if (keyCode == KeyEvent.KEYCODE_BUTTON_B && event.action == KeyEvent.ACTION_UP) {
|
if (keyCode == KeyEvent.KEYCODE_BUTTON_B && event.action == KeyEvent.ACTION_UP) {
|
||||||
dialog?.onBackPressed()
|
dialog?.onBackPressed()
|
||||||
@ -71,33 +83,121 @@ class AppDialog : BottomSheetDialogFragment() {
|
|||||||
override fun onViewCreated(view : View, savedInstanceState : Bundle?) {
|
override fun onViewCreated(view : View, savedInstanceState : Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
val missingIcon = ContextCompat.getDrawable(requireActivity(), R.drawable.default_icon)!!.toBitmap(256, 256)
|
baseAdapter.apply {
|
||||||
|
addAdapter(gameInfoAdapter)
|
||||||
binding.gameIcon.setImageBitmap(item.icon ?: missingIcon)
|
addAdapter(updatesAdapter)
|
||||||
binding.gameTitle.text = item.title
|
addAdapter(dlcsAdapter)
|
||||||
binding.gameSubtitle.text = item.subTitle ?: item.loaderResultString(requireContext())
|
addAdapter(tlmdAdapter)
|
||||||
|
|
||||||
binding.gamePlay.isEnabled = item.loaderResult == LoaderResult.Success
|
|
||||||
binding.gamePlay.setOnClickListener {
|
|
||||||
startActivity(Intent(activity, EmulationActivity::class.java).apply { data = item.uri })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val shortcutManager = requireActivity().getSystemService(ShortcutManager::class.java)
|
val retrofit = Retrofit.Builder()
|
||||||
binding.gamePin.isEnabled = shortcutManager.isRequestPinShortcutSupported
|
.baseUrl("https://raw.githubusercontent.com/skyline-emu/title-meta/")
|
||||||
|
.addConverterFactory(Json.asConverterFactory(MediaType.get("application/json")))
|
||||||
|
.build()
|
||||||
|
|
||||||
binding.gamePin.setOnClickListener {
|
val service : TitleMetaService = retrofit.create(TitleMetaService::class.java)
|
||||||
val info = ShortcutInfo.Builder(context, item.title)
|
|
||||||
info.setShortLabel(item.title)
|
|
||||||
info.setActivity(ComponentName(requireContext(), EmulationActivity::class.java))
|
|
||||||
info.setIcon(Icon.createWithAdaptiveBitmap(item.icon ?: missingIcon))
|
|
||||||
|
|
||||||
val intent = Intent(context, EmulationActivity::class.java)
|
service.getData(item.title).enqueue(object : Callback<TitleMetaData> {
|
||||||
intent.data = item.uri
|
override fun onResponse(call : Call<TitleMetaData>, response : Response<TitleMetaData>) {
|
||||||
intent.action = Intent.ACTION_VIEW
|
//response.body()?.let { populateAdaptersAfterData(it) }
|
||||||
|
TODO("NOT")
|
||||||
|
}
|
||||||
|
|
||||||
info.setIntent(intent)
|
override fun onFailure(call : Call<TitleMetaData>, t : Throwable) {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
shortcutManager.requestPinShortcut(info.build(), null)
|
populateAdaptersBeforeData()
|
||||||
|
|
||||||
|
binding.content.apply {
|
||||||
|
addItemDecoration(SpacingItemDecoration(resources.getDimensionPixelSize(R.dimen.section_spacing)))
|
||||||
|
adapter = baseAdapter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun populateAdaptersBeforeData() {
|
||||||
|
populateGameInfoAdapter(null)
|
||||||
|
//populateUpdatesAdapter()
|
||||||
|
//populateDlcsAdapter()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun populateAdaptersAfterData(data : TitleMetaData) {
|
||||||
|
populateGameInfoAdapter(data)
|
||||||
|
populateTlmdAdapter(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun populateGameInfoAdapter(data : TitleMetaData?) {
|
||||||
|
val entries : MutableList<GenericListItem<out ViewBinding>> = ArrayList()
|
||||||
|
|
||||||
|
entries.apply {
|
||||||
|
add(DragIndicatorViewItem(BottomSheetBehavior.from(requireView().parent as View)))
|
||||||
|
add(GameInfoViewItem(requireActivity(), item, data?.version, data?.rating))
|
||||||
|
}
|
||||||
|
|
||||||
|
gameInfoAdapter.setItems(entries)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun populateUpdatesAdapter() {
|
||||||
|
val entries : MutableList<GenericListItem<out ViewBinding>> = ArrayList()
|
||||||
|
|
||||||
|
entries.apply {
|
||||||
|
add(SectionHeaderViewItem(requireContext().getString(R.string.updates)))
|
||||||
|
add(UpdatesViewItem("TODO base_version"))
|
||||||
|
}
|
||||||
|
|
||||||
|
updatesAdapter.apply {
|
||||||
|
selectedPosition = 0 + 1
|
||||||
|
setItems(entries)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun populateDlcsAdapter() {
|
||||||
|
val entries : MutableList<GenericListItem<out ViewBinding>> = ArrayList()
|
||||||
|
|
||||||
|
entries.apply {
|
||||||
|
add(SectionHeaderViewItem(requireContext().getString(R.string.dlcs)))
|
||||||
|
}
|
||||||
|
|
||||||
|
dlcsAdapter.setItems(entries)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun populateTlmdAdapter(data : TitleMetaData) {
|
||||||
|
val entries : MutableList<GenericListItem<out ViewBinding>> = ArrayList()
|
||||||
|
|
||||||
|
entries.apply {
|
||||||
|
data.issues?.let { issues ->
|
||||||
|
add(SectionHeaderViewItem(requireContext().getString(R.string.issues)) { _, _ ->
|
||||||
|
Snackbar.make(this@AppDialog.requireView(), data.discussion, Snackbar.LENGTH_SHORT).show()
|
||||||
|
})
|
||||||
|
issues.forEach { issue ->
|
||||||
|
add(IssuesViewItem(issue.title, issue.description))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data.notes?.let { notes ->
|
||||||
|
add(SectionHeaderViewItem(requireContext().getString(R.string.notes)))
|
||||||
|
notes.forEach { note ->
|
||||||
|
add(NotesViewItem(note))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data.cheats?.let { cheats ->
|
||||||
|
add(SectionHeaderViewItem(requireContext().getString(R.string.cheats)))
|
||||||
|
cheats.forEach { (key, cheat) ->
|
||||||
|
add(CheatsViewItem(cheat.title, cheat.author, cheat.description, cheat.code, false))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tlmdAdapter.setItems(entries)
|
||||||
|
}
|
||||||
|
|
||||||
|
private inner class SpacingItemDecoration(private val padding : Int) : RecyclerView.ItemDecoration() {
|
||||||
|
override fun getItemOffsets(outRect : Rect, view : View, parent : RecyclerView, state : RecyclerView.State) {
|
||||||
|
super.getItemOffsets(outRect, view, parent, state)
|
||||||
|
outRect.set(0, 0, 0, padding)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getResString(@StringRes resId : Int) = requireContext().getString(resId)
|
||||||
}
|
}
|
||||||
|
@ -23,12 +23,13 @@ import emu.skyline.adapter.inflater
|
|||||||
import emu.skyline.data.AppItem
|
import emu.skyline.data.AppItem
|
||||||
import emu.skyline.databinding.AppDialogGameInfoBinding
|
import emu.skyline.databinding.AppDialogGameInfoBinding
|
||||||
import emu.skyline.loader.LoaderResult
|
import emu.skyline.loader.LoaderResult
|
||||||
|
import emu.skyline.network.TitleRating
|
||||||
|
|
||||||
object ControllerBindingFactory : ViewBindingFactory {
|
object ControllerBindingFactory : ViewBindingFactory {
|
||||||
override fun createBinding(parent : ViewGroup) = AppDialogGameInfoBinding.inflate(parent.inflater(), parent, false)
|
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>() {
|
class GameInfoViewItem(private val context : Context, private val item : AppItem, private val testedVersion : String?, private val rating : TitleRating?) : GenericListItem<AppDialogGameInfoBinding>() {
|
||||||
override fun getViewBindingFactory() = ControllerBindingFactory
|
override fun getViewBindingFactory() = ControllerBindingFactory
|
||||||
|
|
||||||
override fun bind(binding : AppDialogGameInfoBinding, position : Int) {
|
override fun bind(binding : AppDialogGameInfoBinding, position : Int) {
|
||||||
@ -42,7 +43,7 @@ class GameInfoViewItem(private val context : Context, private val item : AppItem
|
|||||||
binding.gameSubtitle.isSelected = true
|
binding.gameSubtitle.isSelected = true
|
||||||
|
|
||||||
binding.flex.visibility = if (rating == null && testedVersion == null) View.INVISIBLE else View.VISIBLE
|
binding.flex.visibility = if (rating == null && testedVersion == null) View.INVISIBLE else View.VISIBLE
|
||||||
binding.ratingBar.rating = (rating ?: 0).toFloat()
|
binding.ratingBar.rating = (rating ?: TitleRating.None).ordinal.toFloat()
|
||||||
binding.testedVersion.text = testedVersion?.let { context.getString(R.string.tested_on, it) } ?: context.getString(R.string.not_tested)
|
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.isEnabled = item.loaderResult == LoaderResult.Success
|
||||||
|
47
app/src/main/java/emu/skyline/network/TitleMetaData.kt
Normal file
47
app/src/main/java/emu/skyline/network/TitleMetaData.kt
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: MPL-2.0
|
||||||
|
* Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||||
|
*/
|
||||||
|
|
||||||
|
package emu.skyline.network
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class TitleMetaData(
|
||||||
|
val name : String,
|
||||||
|
val id : String,
|
||||||
|
val version : String,
|
||||||
|
val rating : TitleRating,
|
||||||
|
val discussion : String,
|
||||||
|
val issues : List<Issue>? = null,
|
||||||
|
val notes : List<String>? = null,
|
||||||
|
val cheats : Map<String, Cheat>? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
enum class TitleRating {
|
||||||
|
None,
|
||||||
|
@SerialName("crash") Crash,
|
||||||
|
@SerialName("intro") Intro,
|
||||||
|
@SerialName("major-bugs") MajorBugs,
|
||||||
|
@SerialName("minor-bugs") MinorBugs,
|
||||||
|
@SerialName("perfect") Perfect,
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Issue(
|
||||||
|
val title : String,
|
||||||
|
val description : String? = null,
|
||||||
|
val url : String,
|
||||||
|
//val workarounds : List<List<String>>, // TODO
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Cheat(
|
||||||
|
val title : String,
|
||||||
|
val description : String? = null,
|
||||||
|
val author : String? = null,
|
||||||
|
val code : String,
|
||||||
|
)
|
15
app/src/main/java/emu/skyline/network/TitleMetaService.kt
Normal file
15
app/src/main/java/emu/skyline/network/TitleMetaService.kt
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: MPL-2.0
|
||||||
|
* Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||||
|
*/
|
||||||
|
|
||||||
|
package emu.skyline.network
|
||||||
|
|
||||||
|
import retrofit2.Call
|
||||||
|
import retrofit2.http.GET
|
||||||
|
import retrofit2.http.Path
|
||||||
|
|
||||||
|
interface TitleMetaService {
|
||||||
|
@GET("main/{id}/title.json")
|
||||||
|
fun getData(@Path("id") titleId : String) : Call<TitleMetaData>
|
||||||
|
}
|
@ -1,82 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
android:id="@+id/content"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center_horizontal"
|
android:clipChildren="false"
|
||||||
android:nextFocusRight="@id/game_play"
|
android:clipToPadding="false"
|
||||||
android:padding="16dp">
|
android:paddingHorizontal="16dp"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
|
||||||
<com.google.android.material.imageview.ShapeableImageView
|
|
||||||
android:id="@+id/game_icon"
|
|
||||||
android:layout_width="150dp"
|
|
||||||
android:layout_height="150dp"
|
|
||||||
android:contentDescription="@string/icon"
|
|
||||||
android:focusable="false"
|
|
||||||
app:shapeAppearance="?attr/shapeAppearanceSmallComponent"
|
|
||||||
tools:src="@drawable/default_icon" />
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/game_title"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
|
||||||
android:textSize="18sp"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="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:textAppearance="?android:attr/textAppearanceListItemSecondary"
|
|
||||||
android:textColor="@android:color/tertiary_text_light"
|
|
||||||
android:textSize="14sp"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/game_title"
|
|
||||||
app:layout_constraintStart_toStartOf="@id/game_title"
|
|
||||||
tools:text="Nintendo" />
|
|
||||||
|
|
||||||
<com.google.android.flexbox.FlexboxLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="start"
|
|
||||||
android:layout_marginTop="12dp"
|
|
||||||
app:layout_constraintStart_toStartOf="@id/game_title"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/game_subtitle"
|
|
||||||
app:flexWrap="wrap">
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/game_play"
|
|
||||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton.Icon"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginEnd="6dp"
|
|
||||||
android:focusedByDefault="true"
|
|
||||||
android:text="@string/play"
|
|
||||||
android:textColor="?attr/colorAccent"
|
|
||||||
app:layout_minWidth="146dp"
|
|
||||||
app:icon="@drawable/ic_play"
|
|
||||||
app:iconTint="?attr/colorAccent" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/game_pin"
|
|
||||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="6dp"
|
|
||||||
app:layout_maxWidth="55dp"
|
|
||||||
app:iconGravity="textStart"
|
|
||||||
app:iconPadding="0dp"
|
|
||||||
app:icon="@drawable/ic_add_home"
|
|
||||||
android:textColor="?attr/colorAccent" />
|
|
||||||
</com.google.android.flexbox.FlexboxLayout>
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
</LinearLayout>
|
|
||||||
|
142
app/src/main/res/layout/app_dialog_dummy.xml
Normal file
142
app/src/main/res/layout/app_dialog_dummy.xml
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?><!-- This is a dummy layout which serves as a preview of AppDialog -->
|
||||||
|
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingHorizontal="16dp">
|
||||||
|
|
||||||
|
<include
|
||||||
|
layout="@layout/app_dialog_drag_indicator"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/section_spacing" />
|
||||||
|
|
||||||
|
<include
|
||||||
|
layout="@layout/app_dialog_game_info"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/section_spacing" />
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@+id/updates_title"
|
||||||
|
layout="@layout/app_dialog_section_header" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/section_spacing" />
|
||||||
|
|
||||||
|
<include layout="@layout/app_dialog_updates_item" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/section_spacing" />
|
||||||
|
|
||||||
|
<include layout="@layout/app_dialog_updates_item" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/section_spacing" />
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@+id/dlcs_title"
|
||||||
|
layout="@layout/app_dialog_section_header" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/section_spacing" />
|
||||||
|
|
||||||
|
<include layout="@layout/app_dialog_dlcs_item" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/section_spacing" />
|
||||||
|
|
||||||
|
<include layout="@layout/app_dialog_dlcs_item" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/section_spacing" />
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@+id/issues_title"
|
||||||
|
layout="@layout/app_dialog_section_header"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/section_spacing" />
|
||||||
|
|
||||||
|
<include layout="@layout/app_dialog_issues_item" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/section_spacing" />
|
||||||
|
|
||||||
|
<include layout="@layout/app_dialog_issues_item" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/section_spacing" />
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@+id/notes_title"
|
||||||
|
layout="@layout/app_dialog_section_header"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/section_spacing" />
|
||||||
|
|
||||||
|
<include layout="@layout/app_dialog_notes_item" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/section_spacing" />
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@+id/cheats_title"
|
||||||
|
layout="@layout/app_dialog_section_header"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/section_spacing" />
|
||||||
|
|
||||||
|
<include layout="@layout/app_dialog_cheats_item" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/section_spacing" />
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@+id/saves_title"
|
||||||
|
layout="@layout/app_dialog_section_header"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/section_spacing" />
|
||||||
|
|
||||||
|
<include layout="@layout/app_dialog_saves_item" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/section_spacing" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</ScrollView>
|
@ -5,7 +5,7 @@
|
|||||||
<dimen name="corner_radius_large">12dp</dimen>
|
<dimen name="corner_radius_large">12dp</dimen>
|
||||||
|
|
||||||
<!-- Main - AppDialog -->
|
<!-- Main - AppDialog -->
|
||||||
<dimen name="section_spacing">10dp</dimen>
|
<dimen name="section_spacing">6dp</dimen>
|
||||||
<dimen name="section_title_padding">6dp</dimen>
|
<dimen name="section_title_padding">6dp</dimen>
|
||||||
<dimen name="section_item_card_padding">12dp</dimen>
|
<dimen name="section_item_card_padding">12dp</dimen>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -15,6 +15,7 @@ buildscript {
|
|||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:7.1.0-beta02'
|
classpath 'com.android.tools.build:gradle:7.1.0-beta02'
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
|
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
|
||||||
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
|
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
|
||||||
|
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
|
Loading…
Reference in New Issue
Block a user