Fixed Multiplayer Turn Notifier periodically failing with error notification. (#2054)
This commit is contained in:
parent
10b95c6c1c
commit
1df0c408aa
8 changed files with 96 additions and 29 deletions
|
@ -1,5 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.unciv.app" >
|
||||
|
||||
<uses-sdk/>
|
||||
|
@ -17,13 +18,14 @@
|
|||
android:launchMode="singleTask"
|
||||
android:label="@string/app_name"
|
||||
android:screenOrientation="userLandscape"
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenSize">
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
|
||||
tools:ignore="LockedOrientationActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
|
||||
</activity>
|
||||
<receiver android:name=".CopyToClipboardReceiver" android:exported="false" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
<string name="Notify_YourTurn_Short">Unciv - Du bist am Zug!</string>
|
||||
<string name="Notify_YourTurn_Long">Deine Freunde warten auf deinen Zug.</string>
|
||||
<string name="Notify_Error_Short">Ein Fehler ist aufgetreten</string>
|
||||
<string name="Notify_Error_Long">Multiplayer Zug Benachrichtigungsdienst wurde beendet</string>
|
||||
<string name="Notify_Persist_Short">Letzter online Zug Check:</string>
|
||||
<string name="Notify_Error_Long">Multiplayer Zug Benachrichtigungsdienst wurde beendet.</string>
|
||||
<string name="Notify_Persist_Short">Letzter online Zugcheck:</string>
|
||||
<string name="Notify_Persist_Long_P1">Unciv wird dich benachrichtigen, wenn du im Multiplayer am Zug bist.</string>
|
||||
<string name="Notify_Persist_Long_P2">Prüft etwa alle</string>
|
||||
<string name="Notify_Persist_Long_P3">Minute(n) wenn Internet vorhanden.</string>
|
||||
|
@ -14,4 +14,7 @@
|
|||
<string name="Notify_ChannelInfo_Long">Informiert dich, wenn du im Multiplayer am Zug bist.</string>
|
||||
<string name="Notify_ChannelService_Short">Unciv Multiplayer Zugprüfer Persistenter Status</string>
|
||||
<string name="Notify_ChannelService_Long">Permanent angezeigte Benachrichtigung, welche dich über die Hintergrundaktivität des Dienstes informiert.</string>
|
||||
<string name="Notify_Error_StackTrace_Toast">Stacktraces in Zwischenablage kopiert.</string>
|
||||
<string name="Notify_Error_CopyAction">Kopiere Stacktraces in Zwischenablage</string>
|
||||
<string name="Notify_Error_Retrying">Fehler, wiederhole…</string>
|
||||
</resources>
|
|
@ -4,7 +4,7 @@
|
|||
<string name="Notify_YourTurn_Short">Unciv - It\'s your turn!</string>
|
||||
<string name="Notify_YourTurn_Long">Your friends are waiting on your turn.</string>
|
||||
<string name="Notify_Error_Short">An error has occurred</string>
|
||||
<string name="Notify_Error_Long">Multiplayer turn notifier service terminated</string>
|
||||
<string name="Notify_Error_Long">Multiplayer turn notifier service terminated.</string>
|
||||
<string name="Notify_Persist_Short">Last online turn check:</string>
|
||||
<string name="Notify_Persist_Long_P1">Unciv will inform you when it\'s your turn in multiplayer.</string>
|
||||
<string name="Notify_Persist_Long_P2">Checks ca. every</string>
|
||||
|
@ -14,4 +14,7 @@
|
|||
<string name="Notify_ChannelInfo_Long">Informs you when it\'s your turn in multiplayer.</string>
|
||||
<string name="Notify_ChannelService_Short">Unciv Multiplayer Turn Checker Persistent Status</string>
|
||||
<string name="Notify_ChannelService_Long">Shown constantly to inform you about background checking.</string>
|
||||
<string name="Notify_Error_StackTrace_Toast">Stacktraces copied to clipboard.</string>
|
||||
<string name="Notify_Error_CopyAction">Copy stacktrace to clipboard</string>
|
||||
<string name="Notify_Error_Retrying">failed, retrying…</string>
|
||||
</resources>
|
||||
|
|
19
android/src/com/unciv/app/CopyToClipboardReceiver.kt
Normal file
19
android/src/com/unciv/app/CopyToClipboardReceiver.kt
Normal file
|
@ -0,0 +1,19 @@
|
|||
package com.unciv.app
|
||||
|
||||
import android.content.*
|
||||
import android.widget.Toast
|
||||
import com.badlogic.gdx.backends.android.AndroidApplication
|
||||
|
||||
/**
|
||||
* This Receiver can be called from an Action on the error Notification shown by MultiplayerTurnCheckWorker.
|
||||
* It copies the text given to it to clipboard and then shows a Toast.
|
||||
* It's intended to help find out why the Turn Notifier failed.
|
||||
*/
|
||||
class CopyToClipboardReceiver : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
val clipboard: ClipboardManager = context.getSystemService(AndroidApplication.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
val clip = ClipData.newPlainText("exception", intent.getStringExtra(MultiplayerTurnCheckWorker.CLIPBOARD_EXTRA))
|
||||
clipboard.setPrimaryClip(clip)
|
||||
Toast.makeText(context, context.resources.getString(R.string.Notify_Error_StackTrace_Toast), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
|
@ -15,6 +15,9 @@ import com.badlogic.gdx.backends.android.AndroidApplication
|
|||
import com.unciv.logic.GameInfo
|
||||
import com.unciv.models.metadata.GameSettings
|
||||
import com.unciv.ui.worldscreen.mainmenu.OnlineMultiplayer
|
||||
import java.io.PrintWriter
|
||||
import java.io.StringWriter
|
||||
import java.io.Writer
|
||||
import java.util.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
|
@ -24,6 +27,7 @@ class MultiplayerTurnCheckWorker(appContext: Context, workerParams: WorkerParame
|
|||
|
||||
companion object {
|
||||
const val WORK_TAG = "UNCIV_MULTIPLAYER_TURN_CHECKER_WORKER"
|
||||
const val CLIPBOARD_EXTRA = "CLIPBOARD_STRING"
|
||||
const val NOTIFICATION_ID_SERVICE = 1
|
||||
const val NOTIFICATION_ID_INFO = 2
|
||||
|
||||
|
@ -202,41 +206,49 @@ class MultiplayerTurnCheckWorker(appContext: Context, workerParams: WorkerParame
|
|||
}
|
||||
|
||||
override fun doWork(): Result {
|
||||
val showPersistNotific = inputData.getBoolean(PERSISTENT_NOTIFICATION_ENABLED, true)
|
||||
val configuredDelay = inputData.getInt(CONFIGURED_DELAY, 5)
|
||||
try {
|
||||
val latestGame = OnlineMultiplayer().tryDownloadGame(inputData.getString(GAME_ID)!!)
|
||||
if (latestGame.currentPlayerCiv.playerId == inputData.getString(USER_ID)!!) {
|
||||
val currentTurnPlayer = OnlineMultiplayer().tryDownloadCurrentTurnCiv(inputData.getString(GAME_ID)!!)
|
||||
if (currentTurnPlayer.playerId == inputData.getString(USER_ID)!!) {
|
||||
notifyUserAboutTurn(applicationContext)
|
||||
with(NotificationManagerCompat.from(applicationContext)) {
|
||||
cancel(NOTIFICATION_ID_SERVICE)
|
||||
}
|
||||
} else {
|
||||
updatePersistentNotification(inputData)
|
||||
if (showPersistNotific) { updatePersistentNotification(inputData) }
|
||||
// We have to reset the fail counter since no exception appeared
|
||||
val inputDataFailReset = Data.Builder().putAll(inputData).putInt(FAIL_COUNT, 0).build()
|
||||
enqueue(applicationContext, inputData.getInt(CONFIGURED_DELAY, 5), inputDataFailReset)
|
||||
enqueue(applicationContext, configuredDelay, inputDataFailReset)
|
||||
}
|
||||
} catch (ex: Exception) {
|
||||
val failCount = inputData.getInt(FAIL_COUNT, 0)
|
||||
if (failCount > 3) {
|
||||
showErrorNotification()
|
||||
showErrorNotification(getStackTraceString(ex))
|
||||
with(NotificationManagerCompat.from(applicationContext)) {
|
||||
cancel(NOTIFICATION_ID_SERVICE)
|
||||
}
|
||||
return Result.failure()
|
||||
} else {
|
||||
if (showPersistNotific) { showPersistentNotification(applicationContext,
|
||||
applicationContext.resources.getString(R.string.Notify_Error_Retrying), configuredDelay.toString()) }
|
||||
// If check fails, retry in one minute.
|
||||
// Makes sense, since checks only happen if Internet is available in principle.
|
||||
// Therefore a failure means either a problem with the GameInfo or with Dropbox.
|
||||
val inputDataFailIncrease = Data.Builder().putAll(inputData).putInt(FAIL_COUNT, failCount + 1).build()
|
||||
enqueue(applicationContext, 1, inputDataFailIncrease)
|
||||
// Persistent Notification is not updated, because user may think check succeed.
|
||||
}
|
||||
}
|
||||
return Result.success()
|
||||
}
|
||||
|
||||
private fun getStackTraceString(ex: Exception): String {
|
||||
val writer: Writer = StringWriter()
|
||||
ex.printStackTrace(PrintWriter(writer))
|
||||
return writer.toString()
|
||||
}
|
||||
|
||||
private fun updatePersistentNotification(inputData: Data) {
|
||||
if (inputData.getBoolean(PERSISTENT_NOTIFICATION_ENABLED, true)) {
|
||||
val cal = GregorianCalendar.getInstance()
|
||||
val hour = cal.get(GregorianCalendar.HOUR_OF_DAY).toString()
|
||||
var minute = cal.get(GregorianCalendar.MINUTE).toString()
|
||||
|
@ -248,14 +260,18 @@ class MultiplayerTurnCheckWorker(appContext: Context, workerParams: WorkerParame
|
|||
showPersistentNotification(applicationContext, displayTime,
|
||||
inputData.getInt(CONFIGURED_DELAY, 5).toString())
|
||||
}
|
||||
}
|
||||
|
||||
private fun showErrorNotification() {
|
||||
val pendingIntent: PendingIntent =
|
||||
private fun showErrorNotification(stackTraceString: String) {
|
||||
val pendingLaunchGameIntent: PendingIntent =
|
||||
Intent(applicationContext, AndroidLauncher::class.java).let { notificationIntent ->
|
||||
PendingIntent.getActivity(applicationContext, 0, notificationIntent, 0)
|
||||
}
|
||||
|
||||
val pendingCopyClipboardIntent: PendingIntent =
|
||||
Intent(applicationContext, CopyToClipboardReceiver::class.java).putExtra(CLIPBOARD_EXTRA, stackTraceString)
|
||||
.let { notificationIntent -> PendingIntent.getBroadcast(applicationContext,0, notificationIntent, 0)
|
||||
}
|
||||
|
||||
val notification: NotificationCompat.Builder = NotificationCompat.Builder(applicationContext, NOTIFICATION_CHANNEL_ID_INFO)
|
||||
.setPriority(NotificationManagerCompat.IMPORTANCE_DEFAULT) // No direct user action expected
|
||||
.setContentTitle(applicationContext.resources.getString(R.string.Notify_Error_Short))
|
||||
|
@ -264,9 +280,10 @@ class MultiplayerTurnCheckWorker(appContext: Context, workerParams: WorkerParame
|
|||
// without at least vibrate, some Android versions don't show a heads-up notification
|
||||
.setDefaults(DEFAULT_VIBRATE)
|
||||
.setLights(Color.YELLOW, 300, 100)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setContentIntent(pendingLaunchGameIntent)
|
||||
.setCategory(NotificationCompat.CATEGORY_ERROR)
|
||||
.setOngoing(false)
|
||||
.addAction(0, applicationContext.resources.getString(R.string.Notify_Error_CopyAction), pendingCopyClipboardIntent)
|
||||
|
||||
with(NotificationManagerCompat.from(applicationContext)) {
|
||||
notify(NOTIFICATION_ID_INFO, notification.build())
|
||||
|
|
|
@ -3,6 +3,7 @@ package com.unciv.logic
|
|||
import com.badlogic.gdx.Gdx
|
||||
import com.badlogic.gdx.files.FileHandle
|
||||
import com.badlogic.gdx.utils.Json
|
||||
import com.unciv.logic.civilization.CivilizationInfo
|
||||
import com.unciv.models.metadata.GameSettings
|
||||
import com.unciv.ui.utils.ImageGetter
|
||||
import java.io.File
|
||||
|
@ -95,5 +96,15 @@ class GameSaver {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns current turn's player from GameInfo JSON-String for multiplayer.
|
||||
* Does not initialize transitive GameInfo data.
|
||||
* It is therefore stateless and save to call for Multiplayer Turn Notifier, unlike gameInfoFromString().
|
||||
*/
|
||||
fun currentTurnCivFromString(gameData: String): CivilizationInfo {
|
||||
val game = json().fromJson(GameInfo::class.java, gameData)
|
||||
return game.getCivilization(game.currentPlayer)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ package com.unciv.ui.worldscreen.mainmenu
|
|||
|
||||
import com.unciv.logic.GameInfo
|
||||
import com.unciv.logic.GameSaver
|
||||
import com.unciv.logic.civilization.CivilizationInfo
|
||||
import com.unciv.ui.saves.Gzip
|
||||
import java.io.BufferedReader
|
||||
import java.io.DataOutputStream
|
||||
|
@ -9,7 +10,7 @@ import java.io.InputStream
|
|||
import java.io.InputStreamReader
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URL
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.nio.charset.Charset
|
||||
|
||||
class DropBox {
|
||||
|
||||
|
@ -27,7 +28,8 @@ class DropBox {
|
|||
|
||||
try {
|
||||
if (data != "") {
|
||||
val postData: ByteArray = data.toByteArray(StandardCharsets.UTF_8)
|
||||
// StandardCharsets.UTF_8 requires API 19
|
||||
val postData: ByteArray = data.toByteArray(Charset.forName("UTF-8"))
|
||||
val outputStream = DataOutputStream(outputStream)
|
||||
outputStream.write(postData)
|
||||
outputStream.flush()
|
||||
|
@ -103,4 +105,14 @@ class OnlineMultiplayer {
|
|||
val zippedGameInfo = DropBox().downloadFileAsString(getGameLocation(gameId))
|
||||
return GameSaver().gameInfoFromString(Gzip.unzip(zippedGameInfo))
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns current turn's player.
|
||||
* Does not initialize transitive GameInfo data.
|
||||
* It is therefore stateless and save to call for Multiplayer Turn Notifier, unlike tryDownloadGame().
|
||||
*/
|
||||
fun tryDownloadCurrentTurnCiv(gameId: String): CivilizationInfo {
|
||||
val zippedGameInfo = DropBox().downloadFileAsString(getGameLocation(gameId))
|
||||
return GameSaver().currentTurnCivFromString(Gzip.unzip(zippedGameInfo))
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue