aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-android/app/src/main/kotlin/org
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-android/app/src/main/kotlin/org')
-rw-r--r--packages/meshbay-android/app/src/main/kotlin/org/meshbay/client/MainActivity.kt12
-rw-r--r--packages/meshbay-android/app/src/main/kotlin/org/meshbay/client/phonesync/Profiles.kt24
2 files changed, 33 insertions, 3 deletions
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 0545ead..09cef0e 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
@@ -40,6 +40,7 @@ import org.meshbay.client.phonesync.ContactSource
import org.meshbay.client.phonesync.DestinationChannels
import org.meshbay.client.phonesync.DocChannels
import org.meshbay.client.phonesync.Flavor
+import org.meshbay.client.phonesync.Profiles
import org.meshbay.client.photos.PhotoChannels
import org.meshbay.client.save.SaveSinks
import org.meshbay.client.shell.Pickers
@@ -64,6 +65,8 @@ class MainActivity : Activity() {
private lateinit var channels: Channels
private lateinit var photos: PhotoChannels
private var docs: List<DocChannels> = emptyList()
+ /** Backups exist in the personal profile only (Profiles.kt). */
+ private var backups = false
private var network: android.net.ConnectivityManager.NetworkCallback? = null
private val pickers = Pickers(this)
private val text = NativeText { code ->
@@ -101,11 +104,12 @@ class MainActivity : Activity() {
Thread { saves.cleanUpAfterAKilledProcess() }.start()
cast = CastChannels(this, onCasting = { on -> runOnUiThread { casting = on; keepAlive() } },
tell = { m -> runOnUiThread { android.widget.Toast.makeText(this, m, android.widget.Toast.LENGTH_LONG).show() } })
+ backups = Profiles.isPersonal(this)
val destinations = DestinationChannels(getSharedPreferences(DestinationChannels.PREFS, Context.MODE_PRIVATE))
photos = PhotoChannels(this, getSharedPreferences(PhotoChannels.PREFS, Context.MODE_PRIVATE), destinations,
java.io.File(filesDir, "photosync"),
onKeepAlive = { on, line -> runOnUiThread { backup(on, line) } })
- docs = listOf(
+ docs = if (!backups) emptyList() else listOf(
DocChannels("contactsync", this, getSharedPreferences("contactsync", Context.MODE_PRIVATE), destinations,
java.io.File(cacheDir, "contactsync"), ContactSource(this),
notifyId = 10, permissionRequest = 4209),
@@ -121,7 +125,8 @@ class MainActivity : Activity() {
channelNames = { mapOf(
Notifier.CHANNEL_CHAT to text.get("push.channel_chat", channels.locale),
Notifier.CHANNEL_OTHER to text.get("push.channel_other", channels.locale)) }),
- photos = photos, destinations = destinations, docs = docs)
+ photos = photos.takeIf { backups }, destinations = destinations.takeIf { backups },
+ docs = docs)
WebViewCompat.addWebMessageListener(web, Bridge.PORT, setOf(UiAssets.ORIGIN), Bridge(channels))
cast.control.warmUp()
installShim()
@@ -167,7 +172,7 @@ class MainActivity : Activity() {
override fun shouldInterceptRequest(view: WebView, request: WebResourceRequest): WebResourceResponse? {
val url = request.url
if (url.host == UiAssets.HOST && url.path?.startsWith(PhotoChannels.PATH) == true) {
- return photos.serve(url.path ?: "") ?: refused()
+ return photos.takeIf { backups }?.serve(url.path ?: "") ?: refused()
}
if (url.host == UiAssets.HOST && url.path?.startsWith(DocChannels.PATH) == true) {
val path = url.path ?: ""
@@ -253,6 +258,7 @@ class MainActivity : Activity() {
val binary = WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_ARRAY_BUFFER)
val prelude = "const HUB_BASE = ${JSONObject.quote(hub.base)};\nconst BINARY = $binary;\n" +
"const CAST = ${cast.control.available()};\n" +
+ "const BACKUP = $backups;\n" +
"const MESSAGES = ${docs.any { it.handles("messagesync:") }};\n"
shim = WebViewCompat.addDocumentStartJavaScript(web, "(function(){$prelude$source\n})();", setOf(UiAssets.ORIGIN))
}
diff --git a/packages/meshbay-android/app/src/main/kotlin/org/meshbay/client/phonesync/Profiles.kt b/packages/meshbay-android/app/src/main/kotlin/org/meshbay/client/phonesync/Profiles.kt
new file mode 100644
index 0000000..660cfaa
--- /dev/null
+++ b/packages/meshbay-android/app/src/main/kotlin/org/meshbay/client/phonesync/Profiles.kt
@@ -0,0 +1,24 @@
+package org.meshbay.client.phonesync
+
+import android.content.Context
+import android.os.Build
+import android.os.UserManager
+
+/**
+ * Backups are of the personal profile only, never of a work profile: a copy
+ * of the application installed inside a work profile offers none of them.
+ *
+ * In the personal profile, every source reads that profile alone: MediaStore,
+ * ContactsContract, the SMS and calendar providers answer for the profile
+ * they are asked from, and none of the cross-profile (`ENTERPRISE_*`) URIs
+ * is used (`test_android_shell.py` checks for them).
+ */
+object Profiles {
+ fun isPersonal(context: Context): Boolean {
+ val users = context.getSystemService(UserManager::class.java)
+ // Before Android 11 an application cannot ask whether its own profile
+ // is managed; a work profile is never the system user, so that is
+ // the test there (a secondary user on a shared tablet loses backups).
+ return if (Build.VERSION.SDK_INT >= 30) !users.isManagedProfile else users.isSystemUser
+ }
+}