diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml
index 1f7191dbf8..b97b5bf05e 100644
--- a/android/AndroidManifest.xml
+++ b/android/AndroidManifest.xml
@@ -29,9 +29,18 @@
+
+
+
+
+
+
-
= Build.VERSION_CODES.KITKAT) {
- customSaveLocationHelper = CustomSaveLocationHelperAndroid(this)
- }
+ customSaveLocationHelper = CustomSaveLocationHelperAndroid(this)
MultiplayerTurnCheckWorker.createNotificationChannels(applicationContext)
- // Only allow mods on KK+, to avoid READ_EXTERNAL_STORAGE permission earlier versions need
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
- copyMods()
- val externalfilesDir = getExternalFilesDir(null)
- if (externalfilesDir != null) GameSaver.externalFilesDirForAndroid = externalfilesDir.path
- }
+ copyMods()
+ val externalfilesDir = getExternalFilesDir(null)
+ if (externalfilesDir != null) GameSaver.externalFilesDirForAndroid = externalfilesDir.path
// Manage orientation lock
val limitOrientationsHelper = LimitOrientationsHelperAndroid(this)
@@ -44,8 +41,18 @@ open class AndroidLauncher : AndroidApplication() {
customSaveLocationHelper = customSaveLocationHelper,
limitOrientationsHelper = limitOrientationsHelper
)
- val game = UncivGame(androidParameters)
+
+ game = UncivGame(androidParameters)
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) {
}
+
+ if (deepLinkedMultiplayerGame != null) {
+ game?.deepLinkedMultiplayerGame = deepLinkedMultiplayerGame;
+ deepLinkedMultiplayerGame = null
+ }
+
super.onResume()
}
- override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
- // This should only happen on API 19+ but it's wrapped in the if check to keep the
- // compiler happy
- customSaveLocationHelper?.handleIntentData(requestCode, data?.data)
+ override fun onNewIntent(intent: Intent?) {
+ super.onNewIntent(intent)
+ if (intent == null)
+ return
+
+ 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)
}
}
diff --git a/android/src/com/unciv/app/MultiplayerTurnCheckWorker.kt b/android/src/com/unciv/app/MultiplayerTurnCheckWorker.kt
index 0e0b28375a..2e2e29447a 100644
--- a/android/src/com/unciv/app/MultiplayerTurnCheckWorker.kt
+++ b/android/src/com/unciv/app/MultiplayerTurnCheckWorker.kt
@@ -6,6 +6,7 @@ import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.graphics.Color
+import android.net.Uri
import android.os.Build
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationCompat.DEFAULT_VIBRATE
@@ -138,17 +139,18 @@ class MultiplayerTurnCheckWorker(appContext: Context, workerParams: WorkerParame
}
}
- fun notifyUserAboutTurn(applicationContext: Context, gameName: String) {
- val pendingIntent: PendingIntent =
- Intent(applicationContext, AndroidLauncher::class.java).let { notificationIntent ->
- PendingIntent.getActivity(applicationContext, 0, notificationIntent, 0)
- }
+ fun notifyUserAboutTurn(applicationContext: Context, game: Pair) {
+ val intent = Intent(applicationContext, AndroidLauncher::class.java).apply {
+ action = Intent.ACTION_VIEW
+ 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 notification: NotificationCompat.Builder = NotificationCompat.Builder(applicationContext, NOTIFICATION_CHANNEL_ID_INFO)
.setPriority(NotificationManagerCompat.IMPORTANCE_HIGH) // people are waiting!
.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)
// without at least vibrate, some Android versions don't show a heads-up notification
.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
// Or possibly reading the preview file returned an exception
if (gameIndex!=-1)
- notifyUserAboutTurn(applicationContext, gameNames[gameIndex])
+ notifyUserAboutTurn(applicationContext, Pair(gameNames[gameIndex], gameIds[gameIndex]))
} else {
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),
@@ -238,8 +240,8 @@ class MultiplayerTurnCheckWorker(appContext: Context, workerParams: WorkerParame
val gameNames = inputData.getStringArray(GAME_NAME)!!
var arrayIndex = 0
// 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
- var foundGame = ""
+ // to download all games to update the files so we save the first one we find
+ var foundGame: Pair? = null
for (gameId in gameIds){
//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])
- if (currentTurnPlayer.playerId == inputData.getString(USER_ID)!! && foundGame.isEmpty()) {
- // We only save the first found game as the player will go into the
- // multiplayer screen anyway to join the game and see the other ones
- foundGame = gameNames[arrayIndex]
+ if (currentTurnPlayer.playerId == inputData.getString(USER_ID)!! && foundGame == null) {
+ foundGame = Pair(gameNames[arrayIndex], gameIds[arrayIndex])
}
arrayIndex++
} catch (ex: FileNotFoundException){
@@ -276,7 +276,7 @@ class MultiplayerTurnCheckWorker(appContext: Context, workerParams: WorkerParame
}
}
- if (foundGame.isNotEmpty()){
+ if (foundGame != null){
notifyUserAboutTurn(applicationContext, foundGame)
with(NotificationManagerCompat.from(applicationContext)) {
cancel(NOTIFICATION_ID_SERVICE)
diff --git a/core/src/com/unciv/UncivGame.kt b/core/src/com/unciv/UncivGame.kt
index a1634f772a..9bd6e73f2c 100644
--- a/core/src/com/unciv/UncivGame.kt
+++ b/core/src/com/unciv/UncivGame.kt
@@ -19,9 +19,10 @@ import com.unciv.ui.audio.MusicMood
import com.unciv.ui.utils.*
import com.unciv.ui.worldscreen.PlayerReadyScreen
import com.unciv.ui.worldscreen.WorldScreen
+import com.unciv.ui.worldscreen.mainmenu.OnlineMultiplayer
+import java.lang.Exception
import java.util.*
-
class UncivGame(parameters: UncivGameParameters) : Game() {
// we need this secondary constructor because Java code for iOS can't handle Kotlin lambda parameters
constructor(version: String) : this(UncivGameParameters(version, null))
@@ -34,6 +35,7 @@ class UncivGame(parameters: UncivGameParameters) : Game() {
val customSaveLocationHelper = parameters.customSaveLocationHelper
val limitOrientationsHelper = parameters.limitOrientationsHelper
+ var deepLinkedMultiplayerGame: String? = null
lateinit var gameInfo: GameInfo
fun isGameInfoInitialized() = this::gameInfo.isInitialized
lateinit var settings: GameSettings
@@ -121,7 +123,17 @@ class UncivGame(parameters: UncivGameParameters) : Game() {
if (settings.isFreshlyCreated) {
setScreen(LanguagePickerScreen())
- } else { setScreen(MainMenuScreen()) }
+ } else {
+ if (deepLinkedMultiplayerGame == null)
+ setScreen(MainMenuScreen())
+ else {
+ try {
+ loadGame(OnlineMultiplayer().tryDownloadGame(deepLinkedMultiplayerGame!!))
+ } catch (ex: Exception) {
+ setScreen(MainMenuScreen())
+ }
+ }
+ }
isInitialized = true
}
}
@@ -159,6 +171,17 @@ class UncivGame(parameters: UncivGameParameters) : Game() {
super.resume()
musicController.resume()
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() {