aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-android/app
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-android/app')
-rw-r--r--packages/meshbay-android/app/src/main/assets/bridge/meshbay-bridge.js6
-rw-r--r--packages/meshbay-android/app/src/main/kotlin/org/meshbay/client/MainActivity.kt25
-rw-r--r--packages/meshbay-android/app/src/main/kotlin/org/meshbay/client/bridge/Channels.kt2
-rw-r--r--packages/meshbay-android/app/src/main/kotlin/org/meshbay/client/cast/CastService.kt10
-rw-r--r--packages/meshbay-android/app/src/main/kotlin/org/meshbay/client/shell/ShellWebView.kt2
5 files changed, 32 insertions, 13 deletions
diff --git a/packages/meshbay-android/app/src/main/assets/bridge/meshbay-bridge.js b/packages/meshbay-android/app/src/main/assets/bridge/meshbay-bridge.js
index 4755531..7ace33b 100644
--- a/packages/meshbay-android/app/src/main/assets/bridge/meshbay-bridge.js
+++ b/packages/meshbay-android/app/src/main/assets/bridge/meshbay-bridge.js
@@ -162,6 +162,12 @@
chromecastPlay: () => call('cast:chromecast:play'),
} } : {}),
+ // Music is playing: keep the process, the Wi-Fi and the page awake with
+ // the screen off, behind a notification, until told otherwise.
+ playback: {
+ keepAlive: (on) => call('playback:keep-alive', on === true),
+ },
+
// Where downloads go, chosen once. A display name comes back, never a URI.
folder: {
choose: () => call('folder:choose'),
diff --git a/packages/meshbay-android/app/src/main/kotlin/org/meshbay/client/MainActivity.kt b/packages/meshbay-android/app/src/main/kotlin/org/meshbay/client/MainActivity.kt
index ea31bca..fc446b9 100644
--- a/packages/meshbay-android/app/src/main/kotlin/org/meshbay/client/MainActivity.kt
+++ b/packages/meshbay-android/app/src/main/kotlin/org/meshbay/client/MainActivity.kt
@@ -56,6 +56,8 @@ class MainActivity : Activity() {
try { assets.open("ui/locales/$code.js").bufferedReader().use { it.readText() } } catch (e: java.io.IOException) { null }
}
private var shim: ScriptHandler? = null
+ private var casting = false
+ private var playing = false
private var fullscreen: View? = null
private var fullscreenCallback: WebChromeClient.CustomViewCallback? = null
@@ -80,11 +82,11 @@ class MainActivity : Activity() {
startActivity = { intent -> runOnUiThread { startActivity(intent) } })
// A process killed mid-download left unfinished files; nothing else will.
Thread { saves.cleanUpAfterAKilledProcess() }.start()
- cast = CastChannels(this, onCasting = { on -> runOnUiThread { casting(on) } },
+ cast = CastChannels(this, onCasting = { on -> runOnUiThread { casting = on; keepAlive() } },
tell = { m -> runOnUiThread { android.widget.Toast.makeText(this, m, android.widget.Toast.LENGTH_LONG).show() } })
channels = Channels(hub, onHubChanged = { runOnUiThread { reloadForHub() } },
hasCatalogue = { code -> hasAsset("ui/locales/$code.js") }, keys = keys, saves = saves,
- cast = cast)
+ cast = cast, onPlayback = { on -> runOnUiThread { playing = on; keepAlive() } })
WebViewCompat.addWebMessageListener(web, Bridge.PORT, setOf(UiAssets.ORIGIN), Bridge(channels))
cast.control.warmUp()
installShim()
@@ -228,14 +230,20 @@ class MainActivity : Activity() {
}
/**
- * A cast keeps the process, the Wi-Fi and the page alive with the screen
- * off (spike S-2a, scenario F): the foreground service holds the first two,
- * the WebView reported visible holds the third.
+ * A cast, or music playing here, keeps the process, the Wi-Fi and the page
+ * alive with the screen off (spike S-2a, scenario F): the foreground service
+ * holds the first two, the WebView reported visible holds the third.
*/
- private fun casting(on: Boolean) {
+ private fun keepAlive() {
+ val on = casting || playing
web.keepVisible = on
val service = Intent(this, CastService::class.java)
- if (on) startForegroundService(service) else stopService(service)
+ if (!on) { stopService(service); return }
+ service.putExtra(CastService.EXTRA_TEXT,
+ if (casting) "Casting on this Wi-Fi" else text.get("music.queue_title", channels.locale))
+ // Refused from the background on Android 12+; the page is then simply
+ // frozen with the screen off, as it was before this existed.
+ try { startForegroundService(service) } catch (e: IllegalStateException) { Log.w(Bridge.TAG, "keep-alive refused: $e") }
}
private fun hasAsset(path: String) = try { assets.open(path).close(); true } catch (e: java.io.IOException) { false }
@@ -283,7 +291,8 @@ class MainActivity : Activity() {
}
override fun onDestroy() {
- if (::cast.isInitialized && cast.relay.active) { cast.relay.stop(); casting(false) }
+ if (::cast.isInitialized && cast.relay.active) cast.relay.stop()
+ if (casting || playing) { casting = false; playing = false; keepAlive() }
if (::web.isInitialized) { root.removeView(web); web.destroy() }
super.onDestroy()
}
diff --git a/packages/meshbay-android/app/src/main/kotlin/org/meshbay/client/bridge/Channels.kt b/packages/meshbay-android/app/src/main/kotlin/org/meshbay/client/bridge/Channels.kt
index 6992cdb..7542fa1 100644
--- a/packages/meshbay-android/app/src/main/kotlin/org/meshbay/client/bridge/Channels.kt
+++ b/packages/meshbay-android/app/src/main/kotlin/org/meshbay/client/bridge/Channels.kt
@@ -25,6 +25,7 @@ class Channels(
private val keys: KeyChannels? = null,
private val saves: SaveSinks? = null,
private val cast: CastChannels? = null,
+ private val onPlayback: (Boolean) -> Unit = {},
) {
@Volatile var locale = "en"
private set
@@ -34,6 +35,7 @@ class Channels(
"hub:fetch" -> hub.fetch(args.optString(0, ""), args.optJSONObject(1))
"ice:resolve-stun" -> resolveStun(args.optJSONArray(0) ?: JSONArray())
"ui:locale" -> setLocale(args.optString(0, ""))
+ "playback:keep-alive" -> { onPlayback(args.optBoolean(0, false)); true }
// Where downloads go, chosen once; a display name, never a URI.
"folder:choose" -> saves().chooseFolder()
diff --git a/packages/meshbay-android/app/src/main/kotlin/org/meshbay/client/cast/CastService.kt b/packages/meshbay-android/app/src/main/kotlin/org/meshbay/client/cast/CastService.kt
index eaa471f..3ea8f92 100644
--- a/packages/meshbay-android/app/src/main/kotlin/org/meshbay/client/cast/CastService.kt
+++ b/packages/meshbay-android/app/src/main/kotlin/org/meshbay/client/cast/CastService.kt
@@ -14,8 +14,9 @@ import android.os.IBinder
import android.os.PowerManager
/**
- * Keeps a cast alive with the screen off: a media-playback foreground service
- * with a partial wake lock and a Wi-Fi lock, for as long as the relay runs.
+ * Keeps a cast, or music playing on the phone itself, alive with the screen
+ * off: a media-playback foreground service with a partial wake lock and a
+ * Wi-Fi lock, for as long as either runs.
*
* Not sufficient alone — measured (spike S-2a): Chromium freezes a hidden page
* 60 s after the screen goes off whatever the process importance, and the
@@ -31,12 +32,12 @@ class CastService : Service() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val nm = getSystemService(NotificationManager::class.java)
- nm.createNotificationChannel(NotificationChannel(CHANNEL, "Casting", NotificationManager.IMPORTANCE_LOW))
+ nm.createNotificationChannel(NotificationChannel(CHANNEL, "Playback", NotificationManager.IMPORTANCE_LOW))
val open = PendingIntent.getActivity(this, 0,
packageManager.getLaunchIntentForPackage(packageName), PendingIntent.FLAG_IMMUTABLE)
val n = Notification.Builder(this, CHANNEL)
.setContentTitle(intent?.getStringExtra(EXTRA_TITLE) ?: "MeshBay")
- .setContentText("Casting on this Wi-Fi")
+ .setContentText(intent?.getStringExtra(EXTRA_TEXT) ?: "Casting on this Wi-Fi")
.setSmallIcon(android.R.drawable.ic_media_play)
.setContentIntent(open)
.setOngoing(true)
@@ -65,6 +66,7 @@ class CastService : Service() {
private const val CHANNEL = "cast"
private const val ID = 7
const val EXTRA_TITLE = "title"
+ const val EXTRA_TEXT = "text"
// A bound on the wake lock, not on the cast: a process that is killed
// without stopping the service must not hold the CPU for ever.
private const val MAX_HOLD_MS = 6L * 3600 * 1000
diff --git a/packages/meshbay-android/app/src/main/kotlin/org/meshbay/client/shell/ShellWebView.kt b/packages/meshbay-android/app/src/main/kotlin/org/meshbay/client/shell/ShellWebView.kt
index 92a186f..731da69 100644
--- a/packages/meshbay-android/app/src/main/kotlin/org/meshbay/client/shell/ShellWebView.kt
+++ b/packages/meshbay-android/app/src/main/kotlin/org/meshbay/client/shell/ShellWebView.kt
@@ -8,7 +8,7 @@ import android.webkit.WebView
* While `keepVisible` is set, the WebView is told its window stayed visible
* when the screen turns off. Chromium then never marks the page hidden, and
* its freeze of hidden pages — exactly 60 s after hiding, measured (spike
- * S-2a C) — never starts. Set only while a cast runs: a page that is never
+ * S-2a C) — never starts. Set only while a cast runs or music plays: a page that is never
* hidden is never throttled, which is the battery cost the freeze exists to
* avoid.
*/