mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-10 15:59:33 +07:00
Added deep link to multiplayer games (#6273)
This commit is contained in:
@ -29,9 +29,18 @@
|
|||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
<intent-filter android:autoVerify="true" >
|
||||||
|
<action android:name="android.intent.action.VIEW"/>
|
||||||
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
|
<category android:name="android.intent.category.BROWSABLE"/>
|
||||||
|
<data
|
||||||
|
android:scheme="https"
|
||||||
|
android:host="unciv.app"
|
||||||
|
android:path="/multiplayer"
|
||||||
|
/>
|
||||||
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name="com.unciv.app.AndroidTvLauncher"
|
android:name="com.unciv.app.AndroidTvLauncher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
|
@ -2,7 +2,7 @@ package com.unciv.app
|
|||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.ActivityInfo
|
import android.content.pm.ActivityInfo
|
||||||
import android.os.Build
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.core.app.NotificationManagerCompat
|
import androidx.core.app.NotificationManagerCompat
|
||||||
import androidx.work.WorkManager
|
import androidx.work.WorkManager
|
||||||
@ -16,19 +16,16 @@ import java.io.File
|
|||||||
|
|
||||||
open class AndroidLauncher : AndroidApplication() {
|
open class AndroidLauncher : AndroidApplication() {
|
||||||
private var customSaveLocationHelper: CustomSaveLocationHelperAndroid? = null
|
private var customSaveLocationHelper: CustomSaveLocationHelperAndroid? = null
|
||||||
|
private var game: UncivGame? = null
|
||||||
|
private var deepLinkedMultiplayerGame: String? = null
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
customSaveLocationHelper = CustomSaveLocationHelperAndroid(this)
|
||||||
customSaveLocationHelper = CustomSaveLocationHelperAndroid(this)
|
|
||||||
}
|
|
||||||
MultiplayerTurnCheckWorker.createNotificationChannels(applicationContext)
|
MultiplayerTurnCheckWorker.createNotificationChannels(applicationContext)
|
||||||
|
|
||||||
// Only allow mods on KK+, to avoid READ_EXTERNAL_STORAGE permission earlier versions need
|
copyMods()
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
val externalfilesDir = getExternalFilesDir(null)
|
||||||
copyMods()
|
if (externalfilesDir != null) GameSaver.externalFilesDirForAndroid = externalfilesDir.path
|
||||||
val externalfilesDir = getExternalFilesDir(null)
|
|
||||||
if (externalfilesDir != null) GameSaver.externalFilesDirForAndroid = externalfilesDir.path
|
|
||||||
}
|
|
||||||
|
|
||||||
// Manage orientation lock
|
// Manage orientation lock
|
||||||
val limitOrientationsHelper = LimitOrientationsHelperAndroid(this)
|
val limitOrientationsHelper = LimitOrientationsHelperAndroid(this)
|
||||||
@ -44,8 +41,18 @@ open class AndroidLauncher : AndroidApplication() {
|
|||||||
customSaveLocationHelper = customSaveLocationHelper,
|
customSaveLocationHelper = customSaveLocationHelper,
|
||||||
limitOrientationsHelper = limitOrientationsHelper
|
limitOrientationsHelper = limitOrientationsHelper
|
||||||
)
|
)
|
||||||
val game = UncivGame(androidParameters)
|
|
||||||
|
game = UncivGame(androidParameters)
|
||||||
initialize(game, config)
|
initialize(game, config)
|
||||||
|
|
||||||
|
// This is also needed in onCreate to open links and notifications
|
||||||
|
// correctly even if the app was not running
|
||||||
|
if (intent.action == Intent.ACTION_VIEW) {
|
||||||
|
val uri: Uri? = intent.data
|
||||||
|
deepLinkedMultiplayerGame = uri?.getQueryParameter("id")
|
||||||
|
} else {
|
||||||
|
deepLinkedMultiplayerGame = null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -84,15 +91,30 @@ open class AndroidLauncher : AndroidApplication() {
|
|||||||
}
|
}
|
||||||
} catch (ex: Exception) {
|
} catch (ex: Exception) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (deepLinkedMultiplayerGame != null) {
|
||||||
|
game?.deepLinkedMultiplayerGame = deepLinkedMultiplayerGame;
|
||||||
|
deepLinkedMultiplayerGame = null
|
||||||
|
}
|
||||||
|
|
||||||
super.onResume()
|
super.onResume()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
override fun onNewIntent(intent: Intent?) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
super.onNewIntent(intent)
|
||||||
// This should only happen on API 19+ but it's wrapped in the if check to keep the
|
if (intent == null)
|
||||||
// compiler happy
|
return
|
||||||
customSaveLocationHelper?.handleIntentData(requestCode, data?.data)
|
|
||||||
|
if (intent.action == Intent.ACTION_VIEW) {
|
||||||
|
val uri: Uri? = intent.data
|
||||||
|
deepLinkedMultiplayerGame = uri?.getQueryParameter("id")
|
||||||
|
} else {
|
||||||
|
deepLinkedMultiplayerGame = null
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||||
|
customSaveLocationHelper?.handleIntentData(requestCode, data?.data)
|
||||||
super.onActivityResult(requestCode, resultCode, data)
|
super.onActivityResult(requestCode, resultCode, data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import android.app.PendingIntent
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import androidx.core.app.NotificationCompat.DEFAULT_VIBRATE
|
import androidx.core.app.NotificationCompat.DEFAULT_VIBRATE
|
||||||
@ -138,17 +139,18 @@ class MultiplayerTurnCheckWorker(appContext: Context, workerParams: WorkerParame
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun notifyUserAboutTurn(applicationContext: Context, gameName: String) {
|
fun notifyUserAboutTurn(applicationContext: Context, game: Pair<String, String>) {
|
||||||
val pendingIntent: PendingIntent =
|
val intent = Intent(applicationContext, AndroidLauncher::class.java).apply {
|
||||||
Intent(applicationContext, AndroidLauncher::class.java).let { notificationIntent ->
|
action = Intent.ACTION_VIEW
|
||||||
PendingIntent.getActivity(applicationContext, 0, notificationIntent, 0)
|
data = Uri.parse("https://unciv.app/multiplayer?id=${game.second}")
|
||||||
}
|
}
|
||||||
|
val pendingIntent = PendingIntent.getActivity(applicationContext, 0, intent, 0)
|
||||||
|
|
||||||
val contentTitle = applicationContext.resources.getString(R.string.Notify_YourTurn_Short)
|
val contentTitle = applicationContext.resources.getString(R.string.Notify_YourTurn_Short)
|
||||||
val notification: NotificationCompat.Builder = NotificationCompat.Builder(applicationContext, NOTIFICATION_CHANNEL_ID_INFO)
|
val notification: NotificationCompat.Builder = NotificationCompat.Builder(applicationContext, NOTIFICATION_CHANNEL_ID_INFO)
|
||||||
.setPriority(NotificationManagerCompat.IMPORTANCE_HIGH) // people are waiting!
|
.setPriority(NotificationManagerCompat.IMPORTANCE_HIGH) // people are waiting!
|
||||||
.setContentTitle(contentTitle)
|
.setContentTitle(contentTitle)
|
||||||
.setContentText(applicationContext.resources.getString(R.string.Notify_YourTurn_Long).replace("[gameName]", gameName))
|
.setContentText(applicationContext.resources.getString(R.string.Notify_YourTurn_Long).replace("[gameName]", game.first))
|
||||||
.setTicker(contentTitle)
|
.setTicker(contentTitle)
|
||||||
// without at least vibrate, some Android versions don't show a heads-up notification
|
// without at least vibrate, some Android versions don't show a heads-up notification
|
||||||
.setDefaults(DEFAULT_VIBRATE)
|
.setDefaults(DEFAULT_VIBRATE)
|
||||||
@ -190,7 +192,7 @@ class MultiplayerTurnCheckWorker(appContext: Context, workerParams: WorkerParame
|
|||||||
// Of the turnNotification is OFF, this will be -1 since we never saved this game in the array
|
// Of the turnNotification is OFF, this will be -1 since we never saved this game in the array
|
||||||
// Or possibly reading the preview file returned an exception
|
// Or possibly reading the preview file returned an exception
|
||||||
if (gameIndex!=-1)
|
if (gameIndex!=-1)
|
||||||
notifyUserAboutTurn(applicationContext, gameNames[gameIndex])
|
notifyUserAboutTurn(applicationContext, Pair(gameNames[gameIndex], gameIds[gameIndex]))
|
||||||
} else {
|
} else {
|
||||||
val inputData = workDataOf(Pair(FAIL_COUNT, 0), Pair(GAME_ID, gameIds), Pair(GAME_NAME, gameNames),
|
val inputData = workDataOf(Pair(FAIL_COUNT, 0), Pair(GAME_ID, gameIds), Pair(GAME_NAME, gameNames),
|
||||||
Pair(USER_ID, settings.userId), Pair(CONFIGURED_DELAY, settings.multiplayerTurnCheckerDelayInMinutes),
|
Pair(USER_ID, settings.userId), Pair(CONFIGURED_DELAY, settings.multiplayerTurnCheckerDelayInMinutes),
|
||||||
@ -238,8 +240,8 @@ class MultiplayerTurnCheckWorker(appContext: Context, workerParams: WorkerParame
|
|||||||
val gameNames = inputData.getStringArray(GAME_NAME)!!
|
val gameNames = inputData.getStringArray(GAME_NAME)!!
|
||||||
var arrayIndex = 0
|
var arrayIndex = 0
|
||||||
// We only want to notify the user or update persisted notification once but still want
|
// We only want to notify the user or update persisted notification once but still want
|
||||||
// to download all games to update the files hence this bool
|
// to download all games to update the files so we save the first one we find
|
||||||
var foundGame = ""
|
var foundGame: Pair<String, String>? = null
|
||||||
|
|
||||||
for (gameId in gameIds){
|
for (gameId in gameIds){
|
||||||
//gameId could be an empty string if startTurnChecker fails to load all files
|
//gameId could be an empty string if startTurnChecker fails to load all files
|
||||||
@ -260,10 +262,8 @@ class MultiplayerTurnCheckWorker(appContext: Context, workerParams: WorkerParame
|
|||||||
*/
|
*/
|
||||||
GameSaver.saveGame(gamePreview, gameNames[arrayIndex])
|
GameSaver.saveGame(gamePreview, gameNames[arrayIndex])
|
||||||
|
|
||||||
if (currentTurnPlayer.playerId == inputData.getString(USER_ID)!! && foundGame.isEmpty()) {
|
if (currentTurnPlayer.playerId == inputData.getString(USER_ID)!! && foundGame == null) {
|
||||||
// We only save the first found game as the player will go into the
|
foundGame = Pair(gameNames[arrayIndex], gameIds[arrayIndex])
|
||||||
// multiplayer screen anyway to join the game and see the other ones
|
|
||||||
foundGame = gameNames[arrayIndex]
|
|
||||||
}
|
}
|
||||||
arrayIndex++
|
arrayIndex++
|
||||||
} catch (ex: FileNotFoundException){
|
} catch (ex: FileNotFoundException){
|
||||||
@ -276,7 +276,7 @@ class MultiplayerTurnCheckWorker(appContext: Context, workerParams: WorkerParame
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (foundGame.isNotEmpty()){
|
if (foundGame != null){
|
||||||
notifyUserAboutTurn(applicationContext, foundGame)
|
notifyUserAboutTurn(applicationContext, foundGame)
|
||||||
with(NotificationManagerCompat.from(applicationContext)) {
|
with(NotificationManagerCompat.from(applicationContext)) {
|
||||||
cancel(NOTIFICATION_ID_SERVICE)
|
cancel(NOTIFICATION_ID_SERVICE)
|
||||||
|
@ -19,9 +19,10 @@ import com.unciv.ui.audio.MusicMood
|
|||||||
import com.unciv.ui.utils.*
|
import com.unciv.ui.utils.*
|
||||||
import com.unciv.ui.worldscreen.PlayerReadyScreen
|
import com.unciv.ui.worldscreen.PlayerReadyScreen
|
||||||
import com.unciv.ui.worldscreen.WorldScreen
|
import com.unciv.ui.worldscreen.WorldScreen
|
||||||
|
import com.unciv.ui.worldscreen.mainmenu.OnlineMultiplayer
|
||||||
|
import java.lang.Exception
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
|
||||||
class UncivGame(parameters: UncivGameParameters) : Game() {
|
class UncivGame(parameters: UncivGameParameters) : Game() {
|
||||||
// we need this secondary constructor because Java code for iOS can't handle Kotlin lambda parameters
|
// we need this secondary constructor because Java code for iOS can't handle Kotlin lambda parameters
|
||||||
constructor(version: String) : this(UncivGameParameters(version, null))
|
constructor(version: String) : this(UncivGameParameters(version, null))
|
||||||
@ -34,6 +35,7 @@ class UncivGame(parameters: UncivGameParameters) : Game() {
|
|||||||
val customSaveLocationHelper = parameters.customSaveLocationHelper
|
val customSaveLocationHelper = parameters.customSaveLocationHelper
|
||||||
val limitOrientationsHelper = parameters.limitOrientationsHelper
|
val limitOrientationsHelper = parameters.limitOrientationsHelper
|
||||||
|
|
||||||
|
var deepLinkedMultiplayerGame: String? = null
|
||||||
lateinit var gameInfo: GameInfo
|
lateinit var gameInfo: GameInfo
|
||||||
fun isGameInfoInitialized() = this::gameInfo.isInitialized
|
fun isGameInfoInitialized() = this::gameInfo.isInitialized
|
||||||
lateinit var settings: GameSettings
|
lateinit var settings: GameSettings
|
||||||
@ -121,7 +123,17 @@ class UncivGame(parameters: UncivGameParameters) : Game() {
|
|||||||
|
|
||||||
if (settings.isFreshlyCreated) {
|
if (settings.isFreshlyCreated) {
|
||||||
setScreen(LanguagePickerScreen())
|
setScreen(LanguagePickerScreen())
|
||||||
} else { setScreen(MainMenuScreen()) }
|
} else {
|
||||||
|
if (deepLinkedMultiplayerGame == null)
|
||||||
|
setScreen(MainMenuScreen())
|
||||||
|
else {
|
||||||
|
try {
|
||||||
|
loadGame(OnlineMultiplayer().tryDownloadGame(deepLinkedMultiplayerGame!!))
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
setScreen(MainMenuScreen())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
isInitialized = true
|
isInitialized = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -159,6 +171,17 @@ class UncivGame(parameters: UncivGameParameters) : Game() {
|
|||||||
super.resume()
|
super.resume()
|
||||||
musicController.resume()
|
musicController.resume()
|
||||||
if (!isInitialized) return // The stuff from Create() is still happening, so the main screen will load eventually
|
if (!isInitialized) return // The stuff from Create() is still happening, so the main screen will load eventually
|
||||||
|
|
||||||
|
// This is also needed in resume to open links and notifications
|
||||||
|
// correctly when the app was already running. The handling in onCreate
|
||||||
|
// does not seem to be enough
|
||||||
|
if (deepLinkedMultiplayerGame != null) {
|
||||||
|
try {
|
||||||
|
loadGame(OnlineMultiplayer().tryDownloadGame(deepLinkedMultiplayerGame!!))
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
setScreen(MainMenuScreen())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun pause() {
|
override fun pause() {
|
||||||
|
Reference in New Issue
Block a user