android: Add game shortcuts to about game dialog (#313)

Adapted from https://github.com/mandarine3ds/mandarine/pull/47

Co-authored-by: Charles Lombardo <clombardo169@gmail.com>
Co-authored-by: Ishan09811 <156402647+Ishan09811@users.noreply.github.com>
Co-authored-by: OpenSauce04 <opensauce04@gmail.com>
This commit is contained in:
Kleidis 2024-10-13 14:49:53 +02:00 committed by GitHub
parent 973faeb9d7
commit 3d1936cf5c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 81 additions and 6 deletions

View File

@ -1,4 +1,4 @@
// Copyright 2023 Citra Emulator Project // Copyright Citra Emulator Project / Lime3DS Emulator Project
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
@ -95,7 +95,13 @@ class EmulationActivity : AppCompatActivity() {
windowManager.defaultDisplay.rotation windowManager.defaultDisplay.rotation
) )
EmulationLifecycleUtil.addShutdownHook(hook = { this.finish() }) EmulationLifecycleUtil.addShutdownHook(hook = {
if (intent.getBooleanExtra("launched_from_shortcut", false)) {
finishAffinity()
} else {
this.finish()
}
})
isEmulationRunning = true isEmulationRunning = true
instance = this instance = this

View File

@ -14,6 +14,10 @@ import android.content.Context
import android.widget.TextView import android.widget.TextView
import android.widget.ImageView import android.widget.ImageView
import android.widget.Toast import android.widget.Toast
import android.graphics.drawable.BitmapDrawable
import android.graphics.Bitmap
import android.content.pm.ShortcutInfo
import android.content.pm.ShortcutManager
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.documentfile.provider.DocumentFile import androidx.documentfile.provider.DocumentFile
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
@ -23,6 +27,10 @@ import androidx.recyclerview.widget.AsyncDifferConfig
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import android.graphics.drawable.Icon
import kotlinx.coroutines.launch
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.CoroutineScope
import com.google.android.material.color.MaterialColors import com.google.android.material.color.MaterialColors
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetBehavior
@ -214,6 +222,24 @@ class GameAdapter(private val activity: AppCompatActivity, private val inflater:
view.findNavController().navigate(action) view.findNavController().navigate(action)
} }
bottomSheetView.findViewById<MaterialButton>(R.id.game_shortcut).setOnClickListener {
val shortcutManager = activity.getSystemService(ShortcutManager::class.java)
CoroutineScope(Dispatchers.IO).launch {
val bitmap = (bottomSheetView.findViewById<ImageView>(R.id.game_icon).drawable as BitmapDrawable).bitmap
val icon = Icon.createWithBitmap(bitmap)
val shortcut = ShortcutInfo.Builder(context, game.title)
.setShortLabel(game.title)
.setIcon(icon)
.setIntent(game.launchIntent.apply {
putExtra("launched_from_shortcut", true)
})
.build()
shortcutManager.requestPinShortcut(shortcut, null)
}
}
bottomSheetView.findViewById<MaterialButton>(R.id.cheats).setOnClickListener { bottomSheetView.findViewById<MaterialButton>(R.id.cheats).setOnClickListener {
val action = CheatsFragmentDirections.actionGlobalCheatsFragment(holder.game.titleId) val action = CheatsFragmentDirections.actionGlobalCheatsFragment(holder.game.titleId)
view.findNavController().navigate(action) view.findNavController().navigate(action)

View File

@ -1,4 +1,4 @@
// Copyright 2023 Citra Emulator Project // Copyright Citra Emulator Project / Lime3DS Emulator Project
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
@ -60,6 +60,7 @@ class GamesFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
homeViewModel.setNavigationVisibility(visible = true, animated = true) homeViewModel.setNavigationVisibility(visible = true, animated = true)
homeViewModel.setStatusBarShadeVisibility(visible = true) homeViewModel.setStatusBarShadeVisibility(visible = true)
val inflater = LayoutInflater.from(requireContext()) val inflater = LayoutInflater.from(requireContext())
binding.gridGames.apply { binding.gridGames.apply {

View File

@ -1,13 +1,17 @@
// Copyright 2023 Citra Emulator Project // Copyright Citra Emulator Project / Lime3DS Emulator Project
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
package io.github.lime3ds.android.model package io.github.lime3ds.android.model
import android.os.Parcelable import android.os.Parcelable
import android.content.Intent
import android.net.Uri
import java.util.HashSet import java.util.HashSet
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import io.github.lime3ds.android.LimeApplication
import io.github.lime3ds.android.activities.EmulationActivity
@Parcelize @Parcelize
@Serializable @Serializable
@ -27,6 +31,16 @@ class Game(
val keyAddedToLibraryTime get() = "${filename}_AddedToLibraryTime" val keyAddedToLibraryTime get() = "${filename}_AddedToLibraryTime"
val keyLastPlayedTime get() = "${filename}_LastPlayed" val keyLastPlayedTime get() = "${filename}_LastPlayed"
val launchIntent: Intent
get() = Intent(LimeApplication.appContext, EmulationActivity::class.java).apply {
action = Intent.ACTION_VIEW
data = if (isInstalled) {
LimeApplication.documentsTree.getUri(path)
} else {
Uri.parse(path)
}
}
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (other !is Game) { if (other !is Game) {
return false return false

View File

@ -1,4 +1,4 @@
// Copyright 2023 Citra Emulator Project // Copyright Citra Emulator Project / Lime3DS Emulator Project
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
@ -100,6 +100,12 @@ class DocumentsTree {
} }
} }
@Synchronized
fun getUri(filepath: String): Uri {
val node = resolvePath(filepath) ?: return Uri.EMPTY
return node.uri ?: return Uri.EMPTY
}
@Synchronized @Synchronized
fun isDirectory(filepath: String): Boolean { fun isDirectory(filepath: String): Boolean {
val node = resolvePath(filepath) ?: return false val node = resolvePath(filepath) ?: return false

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M280,920Q247,920 223.5,896.5Q200,873 200,840L200,120Q200,87 223.5,63.5Q247,40 280,40L680,40Q713,40 736.5,63.5Q760,87 760,120L760,280L680,280L680,240L280,240L280,720L680,720L680,680L760,680L760,840Q760,873 736.5,896.5Q713,920 680,920L280,920ZM686,520L480,520Q480,520 480,520Q480,520 480,520L480,640L400,640L400,520Q400,487 423.5,463.5Q447,440 480,440L686,440L624,376L680,320L840,480L680,640L624,584L686,520Z"/>
</vector>

View File

@ -109,6 +109,17 @@
android:focusedByDefault="true" android:focusedByDefault="true"
android:text="@string/play" android:text="@string/play"
app:icon="@drawable/ic_play" /> app:icon="@drawable/ic_play" />
<com.google.android.material.button.MaterialButton
android:id="@+id/game_shortcut"
style="@style/Widget.Material3.Button.IconButton.Filled.Tonal"
android:layout_width="48dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:contentDescription="@string/shortcut"
app:icon="@drawable/ic_shortcut"
app:iconGravity="textStart" />
<TextView <TextView
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -141,4 +152,4 @@
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout> </LinearLayout>

View File

@ -456,6 +456,7 @@
<!-- About Game Dialog --> <!-- About Game Dialog -->
<string name="play">Play</string> <string name="play">Play</string>
<string name="shortcut">Shortcut</string>
<!-- Cheats --> <!-- Cheats -->
<string name="cheats">Cheats</string> <string name="cheats">Cheats</string>