Added Multiplayer Turn Notification Service (#1947)

* Added Multiplayer Turn Notification Service
https://github.com/yairm210/Unciv/issues/1680

* Minor update on Credits.md (#1949)

# Nations
- Fix typo on word "crescent"
- Fix link that flew off on word "sword"

* 3.5.14-patch1

* Edit startgame screen. (#1950)

Co-authored-by: u-ndefine <41176671+u-ndefine@users.noreply.github.com>

* Small improvements suggested in pull request review
https://github.com/yairm210/Unciv/pull/1947

* Removed potential concurrency hazzards caused by access to non-final variables. Added option to turn off persistent notification (may be necessary on pre-Oreo phones.)
Added suggested comments. https://github.com/yairm210/Unciv/pull/1947

* Fixed miscommited debug code. https://github.com/yairm210/Unciv/pull/1947

Co-authored-by: u-ndefine <41176671+u-ndefine@users.noreply.github.com>
Co-authored-by: Yair Morgenstern <yairm210@hotmail.com>
Co-authored-by: lishaoxia1985 <49801619+lishaoxia1985@users.noreply.github.com>
This commit is contained in:
wrov
2020-02-17 17:34:46 +01:00
committed by GitHub
parent 9d1e45de08
commit bc15d97de7
9 changed files with 369 additions and 23 deletions

View File

@ -14,7 +14,8 @@
android:theme="@style/GdxTheme" > android:theme="@style/GdxTheme" >
<activity <activity
android:name="com.unciv.app.AndroidLauncher" android:name="com.unciv.app.AndroidLauncher"
android:label="@string/app_name" android:launchMode="singleTask"
android:label="@string/app_name"
android:screenOrientation="userLandscape" android:screenOrientation="userLandscape"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"> android:configChanges="keyboard|keyboardHidden|orientation|screenSize">
<intent-filter> <intent-filter>

View File

@ -1734,3 +1734,16 @@ Haka War Dance = Haka-Kriegstanz
-10% combat strength for adjacent enemy units = -10% Kampfstärke für angrenzende feindliche Einheiten -10% combat strength for adjacent enemy units = -10% Kampfstärke für angrenzende feindliche Einheiten
Rejuvenation = Verjüngung Rejuvenation = Verjüngung
All healing effects doubled = Alle Heilungseffekte verdoppelt All healing effects doubled = Alle Heilungseffekte verdoppelt
# Multiplayer Turn Checker Service
An error has occured = Ein Fehler ist aufgetreten
Multiplayer turn notifier service terminated = Multiplayer Zug Benachrichtigungsdienst wurde beendet
Your friends are waiting on your turn. = Deine Freunde warten auf deinen Zug.
Unciv - It's your turn! = Unciv - Du bist am Zug!
Unciv will inform you when it's your turn. = Unciv wird dich benachrichtigen wenn du am Zug bist.
Last checked: [lastTimeChecked]. Checks ca. every [checkPeriod] minute(s) when Internet available. = Zuletzt geprüft: [lastTimeChecked]. Prüft etwa alle [checkPeriod] Minute(n) wenn Internet vorhanden.
Configurable in Unciv options menu. = Konfigurierbar im Unciv Optionsmenü.
Unciv multiplayer turn notifier running = Unciv Multiplayer Zug Benachrichtiger läuft.
Multiplayer options = Multiplayer Einstellungen
Enable out-of-game turn notifications = Aktiviere Zug Benachrichtigungen außerhalb des Spiels
Time between turn checks out-of-game (in minutes) = Intervall zwischen Zug Prüfungen (in Minuten)
Show persistent notification for turn notifier service = Zeige persistente Benachrichtung für den Zug Benachrichtungsdienst

View File

@ -1720,3 +1720,16 @@ Haka War Dance =
-10% combat strength for adjacent enemy units = -10% combat strength for adjacent enemy units =
Rejuvenation = Rejuvenation =
All healing effects doubled = All healing effects doubled =
# Multiplayer Turn Checker Service
An error has occured =
Multiplayer turn notifier service terminated =
Your friends are waiting on your turn. =
Unciv - It's your turn! =
Unciv will inform you when it's your turn. =
Last checked: [lastTimeChecked]. Checks ca. every [checkPeriod] minute(s) when Internet available. =
Configurable in Unciv options menu. =
Unciv multiplayer turn notifier running =
Multiplayer options =
Enable out-of-game turn notifications =
Time between turn checks out-of-game (in minutes) =
Show persistent notification for turn notifier service =

View File

@ -27,6 +27,11 @@ android {
archivesBaseName = "Unciv" archivesBaseName = "Unciv"
} }
// necessary for Android Work lib
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8.toString()
}
// Had to add this crap for Travis to build, it wanted to sign the app // Had to add this crap for Travis to build, it wanted to sign the app
// but couldn't create the debug keystore for some reason // but couldn't create the debug keystore for some reason
@ -45,7 +50,7 @@ android {
aaptOptions { aaptOptions {
ignoreAssetsPattern "!SaveFiles:!fonts:!maps:!music:!mods" ignoreAssetsPattern "!SaveFiles:!fonts:!maps:!music:!mods"
} }
minifyEnabled false minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
} }
@ -116,6 +121,11 @@ task run(type: Exec) {
commandLine "$adb", 'shell', 'am', 'start', '-n', 'com.unciv.app/AndroidLauncher' commandLine "$adb", 'shell', 'am', 'start', '-n', 'com.unciv.app/AndroidLauncher'
} }
dependencies {
implementation 'androidx.core:core:1.2.0'
implementation "androidx.work:work-runtime-ktx:2.3.1"
}
// sets up the Android Eclipse project, using the old Ant based build. // sets up the Android Eclipse project, using the old Ant based build.
eclipse { eclipse {
// need to specify Java source sets explicitly, SpringSource Gradle Eclipse plugin // need to specify Java source sets explicitly, SpringSource Gradle Eclipse plugin
@ -132,8 +142,8 @@ eclipse {
} }
classpath { classpath {
plusConfigurations += [ project.configurations.compile ] plusConfigurations += [ project.configurations.compile ]
containers 'com.android.ide.eclipse.adt.ANDROID_FRAMEWORK', 'com.android.ide.eclipse.adt.LIBRARIES' containers 'com.android.ide.eclipse.adt.ANDROID_FRAMEWORK', 'com.android.ide.eclipse.adt.LIBRARIES'
} }
project { project {
@ -151,7 +161,7 @@ eclipse {
idea { idea {
module { module {
sourceDirs += file("src") sourceDirs += file("src")
scopes = [ COMPILE: [plus:[project.configurations.compile]]] scopes = [ COMPILE: [plus:[project.configurations.compile]]]
iml { iml {
withXml { withXml {

View File

@ -2,6 +2,8 @@ package com.unciv.app
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import androidx.core.app.NotificationManagerCompat
import androidx.work.WorkManager
import com.badlogic.gdx.backends.android.AndroidApplication import com.badlogic.gdx.backends.android.AndroidApplication
import com.badlogic.gdx.backends.android.AndroidApplicationConfiguration import com.badlogic.gdx.backends.android.AndroidApplicationConfiguration
import com.unciv.UncivGame import com.unciv.UncivGame
@ -10,6 +12,7 @@ import java.io.File
class AndroidLauncher : AndroidApplication() { class AndroidLauncher : AndroidApplication() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
createNotificationChannels()
// Only allow mods on KK+, to avoid READ_EXTERNAL_STORAGE permission earlier versions need // Only allow mods on KK+, to avoid READ_EXTERNAL_STORAGE permission earlier versions need
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
@ -23,6 +26,14 @@ class AndroidLauncher : AndroidApplication() {
initialize(game, config) initialize(game, config)
} }
/**
* Necessary for Multiplayer Turner Checker, starting with Android Oreo
*/
private fun createNotificationChannels() {
MultiplayerTurnCheckWorker.createNotificationChannelInfo(context)
MultiplayerTurnCheckWorker.createNotificationChannelService(context)
}
/** /**
* Copies mods from external data directory (where users can access) to the private one (where * Copies mods from external data directory (where users can access) to the private one (where
* libGDX reads from). Note: deletes all files currently in the private mod directory and * libGDX reads from). Note: deletes all files currently in the private mod directory and
@ -43,4 +54,22 @@ class AndroidLauncher : AndroidApplication() {
if (!externalModsDir.exists()) externalModsDir.mkdirs() if (!externalModsDir.exists()) externalModsDir.mkdirs()
externalModsDir.copyRecursively(internalModsDir) externalModsDir.copyRecursively(internalModsDir)
} }
override fun onPause() {
if (UncivGame.Current.settings.multiplayerTurnCheckerEnabled
&& UncivGame.Current.isGameInfoInitialized()
&& UncivGame.Current.gameInfo.gameParameters.isOnlineMultiplayer) {
MultiplayerTurnCheckWorker.startTurnChecker(applicationContext, UncivGame.Current.gameInfo, UncivGame.Current.settings)
}
super.onPause()
}
override fun onResume() {
WorkManager.getInstance(applicationContext).cancelAllWorkByTag(MultiplayerTurnCheckWorker.WORK_TAG)
with(NotificationManagerCompat.from(this)) {
cancel(MultiplayerTurnCheckWorker.NOTIFICATION_ID_INFO)
cancel(MultiplayerTurnCheckWorker.NOTIFICATION_ID_SERVICE)
}
super.onResume()
}
} }

View File

@ -0,0 +1,235 @@
package com.unciv.app
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.os.Build
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationCompat.DEFAULT_VIBRATE
import androidx.core.app.NotificationManagerCompat
import androidx.work.*
import com.badlogic.gdx.backends.android.AndroidApplication
import com.unciv.logic.GameInfo
import com.unciv.models.metadata.GameSettings
import com.unciv.models.translations.tr
import com.unciv.ui.worldscreen.mainmenu.OnlineMultiplayer
import java.util.*
import java.util.concurrent.TimeUnit
class MultiplayerTurnCheckWorker(appContext: Context, workerParams: WorkerParameters)
: Worker(appContext, workerParams) {
companion object {
const val WORK_TAG = "UNCIV_MULTIPLAYER_TURN_CHECKER_WORKER"
const val NOTIFICATION_ID_SERVICE = 1
const val NOTIFICATION_ID_INFO = 2
private const val NOTIFICATION_CHANNEL_ID_INFO = "UNCIV_NOTIFICATION_CHANNEL_INFO"
private const val NOTIFICATION_CHANNEL_ID_SERVICE = "UNCIV_NOTIFICATION_CHANNEL_SERVICE"
@Volatile private var failCount = 0
@Volatile private var gameId = ""
@Volatile private var userId = ""
@Volatile private var configuredDelay = 5L
@Volatile private var persistentNotificationEnabled = true
fun enqueue(appContext: Context, delayInMinutes: Long) {
val constraints = Constraints.Builder()
// If no internet is available, worker waits before becoming active.
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
val checkTurnWork = OneTimeWorkRequestBuilder<MultiplayerTurnCheckWorker>()
.setConstraints(constraints)
.setInitialDelay(delayInMinutes, TimeUnit.MINUTES)
.addTag(WORK_TAG)
.build()
WorkManager.getInstance(appContext).enqueue(checkTurnWork)
}
/**
* Notification Channel for 'It's your turn' and error notifications.
*
* This code is necessary for API level >= 26
* API level < 26 does not support Notification Channels
* For more infos: https://developer.android.com/training/notify-user/channels.html#CreateChannel
*/
fun createNotificationChannelInfo(appContext: Context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val name = "Unciv Multiplayer Turn Checker Alert"
val descriptionText = "Informs you when it's your turn in multiplayer."
val importance = NotificationManager.IMPORTANCE_HIGH
val mChannel = NotificationChannel(NOTIFICATION_CHANNEL_ID_INFO, name, importance)
mChannel.description = descriptionText
mChannel.setShowBadge(true)
mChannel.setLockscreenVisibility(NotificationCompat.VISIBILITY_PUBLIC)
val notificationManager = appContext.getSystemService(AndroidApplication.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(mChannel)
}
}
/**
* Notification Channel for persistent service notification.
*
* This code is necessary for API level >= 26
* API level < 26 does not support Notification Channels
* For more infos: https://developer.android.com/training/notify-user/channels.html#CreateChannel
*/
fun createNotificationChannelService(appContext: Context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val name = "Unciv Multiplayer Turn Checker Persistent Status"
val descriptionText = "Shown constantly to inform you about background checking."
val importance = NotificationManager.IMPORTANCE_MIN
val mChannel = NotificationChannel(NOTIFICATION_CHANNEL_ID_SERVICE, name, importance)
mChannel.description = descriptionText
val notificationManager = appContext.getSystemService(AndroidApplication.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(mChannel)
}
}
/**
* The persistent notification is purely for informational reasons.
* It is not technically necessary for the Worker, since it is not a Service.
*/
fun showPersistentNotification(appContext: Context, lastTimeChecked: String, checkPeriod: String) {
if (persistentNotificationEnabled) {
val pendingIntent: PendingIntent =
Intent(appContext, AndroidLauncher::class.java).let { notificationIntent ->
PendingIntent.getActivity(appContext, 0, notificationIntent, 0)
}
val notification: NotificationCompat.Builder = NotificationCompat.Builder(appContext, NOTIFICATION_CHANNEL_ID_SERVICE)
.setPriority(NotificationManagerCompat.IMPORTANCE_MIN) // it's only a status
.setContentTitle("Unciv multiplayer turn notifier running".tr())
.setStyle(NotificationCompat.BigTextStyle()
.bigText("Unciv will inform you when it's your turn.".tr() + "\n" +
"Last checked: [$lastTimeChecked]. Checks ca. every [$checkPeriod] minute(s) when Internet available.".tr()
+ " " + "Configurable in Unciv options menu.".tr()))
.setSmallIcon(R.drawable.uncivicon2)
.setContentIntent(pendingIntent)
.setCategory(NotificationCompat.CATEGORY_SERVICE)
.setOnlyAlertOnce(true)
.setOngoing(true)
with(NotificationManagerCompat.from(appContext)) {
notify(NOTIFICATION_ID_INFO, notification.build())
}
}
}
fun notifyUserAboutTurn(applicationContext: Context) {
val pendingIntent: PendingIntent =
Intent(applicationContext, AndroidLauncher::class.java).let { notificationIntent ->
PendingIntent.getActivity(applicationContext, 0, notificationIntent, 0)
}
val contentTitle = "Unciv - It's your turn!".tr()
val notification: NotificationCompat.Builder = NotificationCompat.Builder(applicationContext, NOTIFICATION_CHANNEL_ID_INFO)
.setPriority(NotificationManagerCompat.IMPORTANCE_HIGH) // people are waiting!
.setContentTitle(contentTitle)
.setContentText("Your friends are waiting on your turn.".tr())
.setTicker(contentTitle)
// without at least vibrate, some Android versions don't show a heads-up notification
.setDefaults(DEFAULT_VIBRATE)
.setLights(Color.YELLOW, 300, 100)
.setSmallIcon(R.drawable.uncivicon2)
.setContentIntent(pendingIntent)
.setCategory(NotificationCompat.CATEGORY_SOCIAL)
.setOngoing(false)
with(NotificationManagerCompat.from(applicationContext)) {
notify(NOTIFICATION_ID_INFO, notification.build())
}
}
fun startTurnChecker(applicationContext: Context, gameInfo: GameInfo, settings: GameSettings) {
if (gameInfo.currentPlayerCiv.playerId == settings.userId) {
// May be useful to remind a player that he forgot to complete his turn.
notifyUserAboutTurn(applicationContext)
} else {
gameId = gameInfo.gameId
userId = settings.userId
configuredDelay = settings.multiplayerTurnCheckerDelayInMinutes
persistentNotificationEnabled = settings.multiplayerTurnCheckerPermanentNotificationEnabled
showPersistentNotification(applicationContext,
"", settings.multiplayerTurnCheckerDelayInMinutes.toString())
// Initial check always happens after a minute, ignoring delay config. Better user experience this way.
enqueue(applicationContext, 1)
}
}
}
override fun doWork(): Result {
try {
val latestGame = OnlineMultiplayer().tryDownloadGame(gameId)
if (latestGame.currentPlayerCiv.playerId == userId) {
notifyUserAboutTurn(applicationContext)
with(NotificationManagerCompat.from(applicationContext)) {
cancel(NOTIFICATION_ID_SERVICE)
}
} else {
enqueue(applicationContext, configuredDelay)
updatePersistentNotification()
}
failCount = 0
} catch (ex: Exception) {
if (failCount++ > 3) {
showErrorNotification()
with(NotificationManagerCompat.from(applicationContext)) {
cancel(NOTIFICATION_ID_SERVICE)
}
return Result.failure()
} else {
// 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.
enqueue(applicationContext, 1)
updatePersistentNotification()
}
}
return Result.success()
}
private fun updatePersistentNotification() {
val cal = GregorianCalendar.getInstance()
val hour = cal.get(GregorianCalendar.HOUR_OF_DAY).toString()
var minute = cal.get(GregorianCalendar.MINUTE).toString()
if (minute.length == 1) {
minute = "0$minute"
}
val displayTime = "$hour:$minute"
showPersistentNotification(applicationContext, displayTime,
configuredDelay.toString())
}
private fun showErrorNotification() {
val pendingIntent: PendingIntent =
Intent(applicationContext, AndroidLauncher::class.java).let { notificationIntent ->
PendingIntent.getActivity(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("An error has occured".tr())
.setContentText("Multiplayer turn notifier service terminated".tr())
.setSmallIcon(R.drawable.uncivicon2)
// without at least vibrate, some Android versions don't show a heads-up notification
.setDefaults(DEFAULT_VIBRATE)
.setLights(Color.YELLOW, 300, 100)
.setContentIntent(pendingIntent)
.setCategory(NotificationCompat.CATEGORY_ERROR)
.setOngoing(false)
with(NotificationManagerCompat.from(applicationContext)) {
notify(NOTIFICATION_ID_INFO, notification.build())
}
}
}

View File

@ -31,6 +31,7 @@ class UncivGame(
constructor(version: String) : this(version, null) constructor(version: String) : this(version, null)
lateinit var gameInfo: GameInfo lateinit var gameInfo: GameInfo
fun isGameInfoInitialized() = ::gameInfo.isInitialized
lateinit var settings : GameSettings lateinit var settings : GameSettings
lateinit var crashController: CrashController lateinit var crashController: CrashController
/** /**

View File

@ -26,6 +26,9 @@ class GameSettings {
var nuclearWeaponEnabled = false var nuclearWeaponEnabled = false
var continuousRendering = true var continuousRendering = true
var userId = "" var userId = ""
var multiplayerTurnCheckerEnabled = true
var multiplayerTurnCheckerPermanentNotificationEnabled = true
var multiplayerTurnCheckerDelayInMinutes = 5L
fun save(){ fun save(){
GameSaver().setGeneralSettings(this) GameSaver().setGeneralSettings(this)

View File

@ -1,5 +1,6 @@
package com.unciv.ui.worldscreen.mainmenu package com.unciv.ui.worldscreen.mainmenu
import com.badlogic.gdx.Application
import com.badlogic.gdx.Gdx import com.badlogic.gdx.Gdx
import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.scenes.scene2d.Actor import com.badlogic.gdx.scenes.scene2d.Actor
@ -39,38 +40,38 @@ class WorldScreenOptionsPopup(val worldScreen:WorldScreen) : Popup(worldScreen){
innerTable.add("Display options".toLabel(fontSize = 24)).colspan(2).row() innerTable.add("Display options".toLabel(fontSize = 24)).colspan(2).row()
innerTable.add("Show worked tiles".toLabel()) innerTable.add("Show worked tiles".toLabel())
addButton(innerTable, if (settings.showWorkedTiles) "Yes".tr() else "No".tr()) { addButton(innerTable, if (settings.showWorkedTiles) "Yes" else "No") {
settings.showWorkedTiles= !settings.showWorkedTiles settings.showWorkedTiles= !settings.showWorkedTiles
update() update()
} }
innerTable.add("Show resources and improvements".toLabel()) innerTable.add("Show resources and improvements".toLabel())
addButton(innerTable, if (settings.showResourcesAndImprovements) "Yes".tr() else "No".tr()) { addButton(innerTable, if (settings.showResourcesAndImprovements) "Yes" else "No") {
settings.showResourcesAndImprovements = !settings.showResourcesAndImprovements settings.showResourcesAndImprovements = !settings.showResourcesAndImprovements
update() update()
} }
innerTable.add("Show tutorials".toLabel()) innerTable.add("Show tutorials".toLabel())
addButton(innerTable, if (settings.showTutorials) "Yes".tr() else "No".tr()) { addButton(innerTable, if (settings.showTutorials) "Yes" else "No") {
settings.showTutorials = !settings.showTutorials settings.showTutorials = !settings.showTutorials
update() update()
} }
innerTable.add("Show minimap".toLabel()) innerTable.add("Show minimap".toLabel())
addButton(innerTable, if (settings.showMinimap) "Yes".tr() else "No".tr()) { addButton(innerTable, if (settings.showMinimap) "Yes" else "No") {
settings.showMinimap = !settings.showMinimap settings.showMinimap = !settings.showMinimap
update() update()
} }
innerTable.add("Show pixel units".toLabel()) innerTable.add("Show pixel units".toLabel())
addButton(innerTable, if (settings.showPixelUnits) "Yes".tr() else "No".tr()) { addButton(innerTable, if (settings.showPixelUnits) "Yes" else "No") {
settings.showPixelUnits = !settings.showPixelUnits settings.showPixelUnits = !settings.showPixelUnits
update() update()
} }
innerTable.add("Show pixel improvements".toLabel()) innerTable.add("Show pixel improvements".toLabel())
addButton(innerTable, if (settings.showPixelImprovements) "Yes".tr() else "No".tr()) { addButton(innerTable, if (settings.showPixelImprovements) "Yes" else "No") {
settings.showPixelImprovements = !settings.showPixelImprovements settings.showPixelImprovements = !settings.showPixelImprovements
update() update()
} }
@ -83,56 +84,76 @@ class WorldScreenOptionsPopup(val worldScreen:WorldScreen) : Popup(worldScreen){
// Do not add to template.properties yet please. // Do not add to template.properties yet please.
innerTable.add("Continuous rendering\n(HIGHLY EXPERIMENTAL)".toLabel()) innerTable.add("Continuous rendering\n(HIGHLY EXPERIMENTAL)".toLabel())
addButton(innerTable, if (settings.continuousRendering) "Yes".tr() else "No".tr()) { addButton(innerTable, if (settings.continuousRendering) "Yes" else "No") {
settings.continuousRendering = !settings.continuousRendering settings.continuousRendering = !settings.continuousRendering
Gdx.graphics.isContinuousRendering = settings.continuousRendering Gdx.graphics.isContinuousRendering = settings.continuousRendering
update() update()
} }
innerTable.add("Gameplay options".toLabel(fontSize = 24)).colspan(2).row() innerTable.add("Gameplay options".toLabel(fontSize = 24)).colspan(2).padTop(20f).row()
innerTable.add("Check for idle units".toLabel()) innerTable.add("Check for idle units".toLabel())
addButton(innerTable, if (settings.checkForDueUnits) "Yes".tr() else "No".tr()) { addButton(innerTable, if (settings.checkForDueUnits) "Yes" else "No") {
settings.checkForDueUnits = !settings.checkForDueUnits settings.checkForDueUnits = !settings.checkForDueUnits
update() update()
} }
innerTable.add("Move units with a single tap".toLabel()) innerTable.add("Move units with a single tap".toLabel())
addButton(innerTable, if (settings.singleTapMove) "Yes".tr() else "No".tr()) { addButton(innerTable, if (settings.singleTapMove) "Yes" else "No") {
settings.singleTapMove = !settings.singleTapMove settings.singleTapMove = !settings.singleTapMove
update() update()
} }
innerTable.add("Auto-assign city production".toLabel()) innerTable.add("Auto-assign city production".toLabel())
addButton(innerTable, if (settings.autoAssignCityProduction) "Yes".tr() else "No".tr()) { addButton(innerTable, if (settings.autoAssignCityProduction) "Yes" else "No") {
settings.autoAssignCityProduction = !settings.autoAssignCityProduction settings.autoAssignCityProduction = !settings.autoAssignCityProduction
update() update()
} }
innerTable.add("Auto-build roads".toLabel()) innerTable.add("Auto-build roads".toLabel())
addButton(innerTable, if (settings.autoBuildingRoads) "Yes".tr() else "No".tr()) { addButton(innerTable, if (settings.autoBuildingRoads) "Yes" else "No") {
settings.autoBuildingRoads = !settings.autoBuildingRoads settings.autoBuildingRoads = !settings.autoBuildingRoads
update() update()
} }
innerTable.add("Enable nuclear weapons".toLabel()) innerTable.add("Enable nuclear weapons".toLabel())
addButton(innerTable, if (settings.nuclearWeaponEnabled) "Yes".tr() else "No".tr()) { addButton(innerTable, if (settings.nuclearWeaponEnabled) "Yes" else "No") {
settings.nuclearWeaponEnabled = !settings.nuclearWeaponEnabled settings.nuclearWeaponEnabled = !settings.nuclearWeaponEnabled
update() update()
} }
addAutosaveTurnsSelectBox(innerTable) addAutosaveTurnsSelectBox(innerTable)
innerTable.add("Other options".toLabel(fontSize = 24)).colspan(2).row() // at the moment the notification service only exists on Android
if (Gdx.app.type == Application.ApplicationType.Android) {
innerTable.add("Multiplayer options".toLabel(fontSize = 24)).colspan(2).padTop(20f).row()
innerTable.add("Enable out-of-game turn notifications".toLabel())
addButton(innerTable, if (settings.multiplayerTurnCheckerEnabled) "Yes" else "No") {
settings.multiplayerTurnCheckerEnabled = !settings.multiplayerTurnCheckerEnabled
update()
}
if (settings.multiplayerTurnCheckerEnabled) {
addMultiplayerTurnCheckerDelayBox(innerTable)
innerTable.add("Show persistent notification for turn notifier service".toLabel())
addButton(innerTable, if (settings.multiplayerTurnCheckerPermanentNotificationEnabled) "Yes" else "No") {
settings.multiplayerTurnCheckerPermanentNotificationEnabled = !settings.multiplayerTurnCheckerPermanentNotificationEnabled
update()
}
}
}
innerTable.add("Other options".toLabel(fontSize = 24)).colspan(2).padTop(20f).row()
addSoundEffectsVolumeSlider(innerTable) addSoundEffectsVolumeSlider(innerTable)
addMusicVolumeSlider(innerTable) addMusicVolumeSlider(innerTable)
innerTable.add("Version".toLabel()) innerTable.add("Version".toLabel()).pad(10f)
innerTable.add(UncivGame.Current.version.toLabel()).row() innerTable.add(UncivGame.Current.version.toLabel()).pad(10f).row()
val scrollPane = ScrollPane(innerTable, skin) val scrollPane = ScrollPane(innerTable, skin)
@ -167,7 +188,7 @@ class WorldScreenOptionsPopup(val worldScreen:WorldScreen) : Popup(worldScreen){
Sounds.play(UncivSound.Click) Sounds.play(UncivSound.Click)
} }
}) })
innerTable.add(soundEffectsVolumeSlider).row() innerTable.add(soundEffectsVolumeSlider).pad(10f).row()
} }
private fun addMusicVolumeSlider(innerTable: Table) { private fun addMusicVolumeSlider(innerTable: Table) {
@ -184,7 +205,7 @@ class WorldScreenOptionsPopup(val worldScreen:WorldScreen) : Popup(worldScreen){
UncivGame.Current.music?.volume = 0.4f * musicVolumeSlider.value UncivGame.Current.music?.volume = 0.4f * musicVolumeSlider.value
} }
}) })
innerTable.add(musicVolumeSlider).row() innerTable.add(musicVolumeSlider).pad(10f).row()
} }
else{ else{
val downloadMusicButton = TextButton("Download music".tr(),CameraStageBaseScreen.skin) val downloadMusicButton = TextButton("Download music".tr(),CameraStageBaseScreen.skin)
@ -276,6 +297,26 @@ class WorldScreenOptionsPopup(val worldScreen:WorldScreen) : Popup(worldScreen){
}) })
} }
private fun addMultiplayerTurnCheckerDelayBox(innerTable: Table) {
innerTable.add("Time between turn checks out-of-game (in minutes)".toLabel())
val checkDelaySelectBox = SelectBox<Long>(skin)
val possibleDelaysArray = Array<Long>()
possibleDelaysArray.addAll(1L, 2L, 5L, 15L)
checkDelaySelectBox.items = possibleDelaysArray
checkDelaySelectBox.selected = UncivGame.Current.settings.multiplayerTurnCheckerDelayInMinutes
innerTable.add(checkDelaySelectBox).pad(10f).row()
checkDelaySelectBox.addListener(object : ChangeListener() {
override fun changed(event: ChangeEvent?, actor: Actor?) {
UncivGame.Current.settings.multiplayerTurnCheckerDelayInMinutes = checkDelaySelectBox.selected
UncivGame.Current.settings.save()
update()
}
})
}
private fun addLanguageSelectBox(innerTable: Table) { private fun addLanguageSelectBox(innerTable: Table) {
val languageSelectBox = SelectBox<Language>(skin) val languageSelectBox = SelectBox<Language>(skin)
val languageArray = Array<Language>() val languageArray = Array<Language>()