From a5b173003dd871ee1f7a0a11e34b0f49040d5271 Mon Sep 17 00:00:00 2001 From: Luis Guzman Date: Sun, 6 Sep 2026 21:37:23 -0600 Subject: [PATCH 01/18] K2GO-395 feat(networkpolicy): metered-data cost-consent gate + proactive alert Add a defensive gate that asks for consent before starting a heavy download on a metered network, plus a process-wide observer that warns on switching to metered data and clears the session consent on leaving metered. The rule keys on the active default network NET_CAPABILITY_NOT_METERED, not on transport -- grounded in device evidence (ADR-395): a cellular IMS PDN reports NOT_METERED while the internet APN does not, and a phone hotspot can arrive metered on the client. Pure-JVM domain with unit tests; data reads the one ConnectivityManager source; presentation is a stateless gate (BrandDialog) plus the observer started from IIABApplication. Reference wiring: the ZIM confirm commit point (ZimConfirmFragment), not the ~2s provisioner drain. Other seams (Books, Kolibri, rootfs install, DownloadManager) and the l10n migration are follow-ups per ADR-395. Strings parked in strings_untranslated.xml pending 33-locale translation. --- .../appdevforall/k2go/IIABApplication.java | 4 + .../data/AndroidNetworkClassifier.java | 43 ++++ .../data/SessionMeteredConsentStore.java | 37 +++ .../domain/MeteredConsentStore.java | 21 ++ .../networkpolicy/domain/NetworkClass.java | 35 +++ .../networkpolicy/domain/NetworkPolicy.java | 29 +++ .../domain/NetworkPolicyDecision.java | 14 ++ .../domain/NetworkTransition.java | 17 ++ .../presentation/MeteredNetworkObserver.java | 96 ++++++++ .../presentation/NetworkPolicyGate.java | 68 ++++++ .../k2go/redesign/ZimConfirmFragment.java | 9 +- .../main/res/values/strings_untranslated.xml | 12 +- .../domain/NetworkClassTest.java | 30 +++ .../domain/NetworkPolicyTest.java | 33 +++ .../domain/NetworkTransitionTest.java | 30 +++ .../docs/ADR-395-network-cost-consent.md | 217 ++++++++++++++++++ 16 files changed, 692 insertions(+), 3 deletions(-) create mode 100644 controller/app/src/main/java/org/appdevforall/k2go/networkpolicy/data/AndroidNetworkClassifier.java create mode 100644 controller/app/src/main/java/org/appdevforall/k2go/networkpolicy/data/SessionMeteredConsentStore.java create mode 100644 controller/app/src/main/java/org/appdevforall/k2go/networkpolicy/domain/MeteredConsentStore.java create mode 100644 controller/app/src/main/java/org/appdevforall/k2go/networkpolicy/domain/NetworkClass.java create mode 100644 controller/app/src/main/java/org/appdevforall/k2go/networkpolicy/domain/NetworkPolicy.java create mode 100644 controller/app/src/main/java/org/appdevforall/k2go/networkpolicy/domain/NetworkPolicyDecision.java create mode 100644 controller/app/src/main/java/org/appdevforall/k2go/networkpolicy/domain/NetworkTransition.java create mode 100644 controller/app/src/main/java/org/appdevforall/k2go/networkpolicy/presentation/MeteredNetworkObserver.java create mode 100644 controller/app/src/main/java/org/appdevforall/k2go/networkpolicy/presentation/NetworkPolicyGate.java create mode 100644 controller/app/src/test/java/org/appdevforall/k2go/networkpolicy/domain/NetworkClassTest.java create mode 100644 controller/app/src/test/java/org/appdevforall/k2go/networkpolicy/domain/NetworkPolicyTest.java create mode 100644 controller/app/src/test/java/org/appdevforall/k2go/networkpolicy/domain/NetworkTransitionTest.java create mode 100644 controller/docs/ADR-395-network-cost-consent.md diff --git a/controller/app/src/main/java/org/appdevforall/k2go/IIABApplication.java b/controller/app/src/main/java/org/appdevforall/k2go/IIABApplication.java index 3dc3786eb..e067e3dbf 100644 --- a/controller/app/src/main/java/org/appdevforall/k2go/IIABApplication.java +++ b/controller/app/src/main/java/org/appdevforall/k2go/IIABApplication.java @@ -34,6 +34,10 @@ public void onCreate() { // with NO foreground Activity. The tick stands down while an Activity is foregrounded (the Activity // poll + bridge drive then); it only actuates OFF-UI when backgrounded. org.appdevforall.k2go.env.ServerLifecycleReconciler.get().startBackgroundTick(this); + // K2GO-395 (ADR-395): one process-scoped watcher of the default-network cost class. + // Proactive alert on crossing into metered + clears the session metered-consent on leaving + // metered. Reuses the existing NetworkStateLiveData callback (one source of the change fact). + org.appdevforall.k2go.networkpolicy.presentation.MeteredNetworkObserver.start(this); // We inject Conscrypt as the app's primary security provider try { Security.insertProviderAt(Conscrypt.newProvider(), 1); diff --git a/controller/app/src/main/java/org/appdevforall/k2go/networkpolicy/data/AndroidNetworkClassifier.java b/controller/app/src/main/java/org/appdevforall/k2go/networkpolicy/data/AndroidNetworkClassifier.java new file mode 100644 index 000000000..9d20ee7a6 --- /dev/null +++ b/controller/app/src/main/java/org/appdevforall/k2go/networkpolicy/data/AndroidNetworkClassifier.java @@ -0,0 +1,43 @@ +package org.appdevforall.k2go.networkpolicy.data; + +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.Network; +import android.net.NetworkCapabilities; + +import androidx.annotation.NonNull; + +import org.appdevforall.k2go.networkpolicy.domain.NetworkClass; + +/** + * Reads the cost class off the ACTIVE DEFAULT network. + * + *

This is the single reader of ConnectivityManager for cost decisions + * (ADR-395). The two existing internet checks -- DashboardRebuild.hasInternet and + * InstallService.hasValidatedInternet -- should route through here as a follow-up + * so there is one source of the "what is the network" fact, not three. + * + *

The rule is by NET_CAPABILITY_NOT_METERED, never by transport: on real + * hardware the cellular IMS PDN reports NOT_METERED while the internet APN does + * not (see ADR-395 device evidence). The pure mapping lives in + * {@link NetworkClass#from(boolean, boolean)}; this class only extracts the two + * facts from Android. + */ +public final class AndroidNetworkClassifier { + + private AndroidNetworkClassifier() {} + + @NonNull + public static NetworkClass classify(@NonNull Context ctx) { + ConnectivityManager cm = + (ConnectivityManager) ctx.getSystemService(Context.CONNECTIVITY_SERVICE); + if (cm == null) return NetworkClass.NONE; + Network net = cm.getActiveNetwork(); + if (net == null) return NetworkClass.NONE; + NetworkCapabilities caps = cm.getNetworkCapabilities(net); + if (caps == null) return NetworkClass.NONE; + boolean hasInternet = caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); + boolean notMetered = caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED); + return NetworkClass.from(hasInternet, notMetered); + } +} diff --git a/controller/app/src/main/java/org/appdevforall/k2go/networkpolicy/data/SessionMeteredConsentStore.java b/controller/app/src/main/java/org/appdevforall/k2go/networkpolicy/data/SessionMeteredConsentStore.java new file mode 100644 index 000000000..a2efb4f99 --- /dev/null +++ b/controller/app/src/main/java/org/appdevforall/k2go/networkpolicy/data/SessionMeteredConsentStore.java @@ -0,0 +1,37 @@ +package org.appdevforall.k2go.networkpolicy.data; + +import org.appdevforall.k2go.networkpolicy.domain.MeteredConsentStore; + +/** + * In-memory, process-lifetime consent (ADR-395). Not persisted on purpose: the + * grant must not outlive the session, so cost awareness returns on the next + * launch. The metered-network observer clears it the moment the network returns + * to non-metered, so the grant never outlives the metered episode either. + */ +public final class SessionMeteredConsentStore implements MeteredConsentStore { + + private static final SessionMeteredConsentStore INSTANCE = new SessionMeteredConsentStore(); + + public static SessionMeteredConsentStore get() { + return INSTANCE; + } + + private SessionMeteredConsentStore() {} + + private volatile boolean granted = false; + + @Override + public boolean isGranted() { + return granted; + } + + @Override + public void grant() { + granted = true; + } + + @Override + public void clear() { + granted = false; + } +} diff --git a/controller/app/src/main/java/org/appdevforall/k2go/networkpolicy/domain/MeteredConsentStore.java b/controller/app/src/main/java/org/appdevforall/k2go/networkpolicy/domain/MeteredConsentStore.java new file mode 100644 index 000000000..b500f4613 --- /dev/null +++ b/controller/app/src/main/java/org/appdevforall/k2go/networkpolicy/domain/MeteredConsentStore.java @@ -0,0 +1,21 @@ +package org.appdevforall.k2go.networkpolicy.domain; + +/** + * Holds the one ephemeral fact the gate needs: did the user consent to spend + * metered data for this session? + * + *

Ephemeral by design (ADR-395): a persisted "always allow" would defeat the + * cost-awareness goal, and a persisted grant that nobody clears is the + * stuck-marker anti-pattern this project avoids. Lifecycle: the consent dialog + * calls {@link #grant()}; the metered-network observer calls {@link #clear()} + * when the default network returns to unmetered; process death clears it because + * the only implementation keeps it in memory. + */ +public interface MeteredConsentStore { + + boolean isGranted(); + + void grant(); + + void clear(); +} diff --git a/controller/app/src/main/java/org/appdevforall/k2go/networkpolicy/domain/NetworkClass.java b/controller/app/src/main/java/org/appdevforall/k2go/networkpolicy/domain/NetworkClass.java new file mode 100644 index 000000000..0b8b2668a --- /dev/null +++ b/controller/app/src/main/java/org/appdevforall/k2go/networkpolicy/domain/NetworkClass.java @@ -0,0 +1,35 @@ +package org.appdevforall.k2go.networkpolicy.domain; + +/** + * Cost class of the ACTIVE DEFAULT network, from the app point of view. + * + *

The split is by metered state, not by transport. On real hardware a carrier + * runs several cellular data networks at once: the IMS signaling network reports + * NOT_METERED, while the general-internet APN does not. Keying on + * TRANSPORT_CELLULAR would therefore misjudge cost. The one reliable signal is + * the active default network NET_CAPABILITY_NOT_METERED. See + * ADR-395 (device evidence appendix) for the measured values. + */ +public enum NetworkClass { + + /** Has internet and is not metered (home Wi-Fi, unmetered ethernet). Free to use. */ + UNMETERED, + + /** Has internet but is metered (cellular internet APN, a metered Wi-Fi hotspot). Costs data. */ + METERED, + + /** No internet-capable default network. Nothing can be downloaded. */ + NONE; + + /** + * Pure mapping from the two facts the data layer reads off the active default + * network. Kept here so the rule is unit-tested without Android. + * + * @param hasInternet the default network has NET_CAPABILITY_INTERNET + * @param notMetered the default network has NET_CAPABILITY_NOT_METERED + */ + public static NetworkClass from(boolean hasInternet, boolean notMetered) { + if (!hasInternet) return NONE; + return notMetered ? UNMETERED : METERED; + } +} diff --git a/controller/app/src/main/java/org/appdevforall/k2go/networkpolicy/domain/NetworkPolicy.java b/controller/app/src/main/java/org/appdevforall/k2go/networkpolicy/domain/NetworkPolicy.java new file mode 100644 index 000000000..35bfa1c6f --- /dev/null +++ b/controller/app/src/main/java/org/appdevforall/k2go/networkpolicy/domain/NetworkPolicy.java @@ -0,0 +1,29 @@ +package org.appdevforall.k2go.networkpolicy.domain; + +/** + * The single rule for "may a heavy transfer start now?". Pure, no Android. + * + *

Defensive by design (see ADR-395): the gate acts at the START of a new + * transfer. It does not micro-manage a transfer already in flight -- once bytes + * move on a link the app does not own (the in-proot server pulls content over + * the device default network), Android gives no fine control. So the contract + * is simple: do not START anything costly without consent. + */ +public final class NetworkPolicy { + + /** + * @param net cost class of the active default network + * @param consented the user granted "spend metered data" for this session + */ + public NetworkPolicyDecision decideHeavyStart(NetworkClass net, boolean consented) { + switch (net) { + case UNMETERED: + return NetworkPolicyDecision.ALLOW; + case METERED: + return consented ? NetworkPolicyDecision.ALLOW : NetworkPolicyDecision.NEEDS_CONSENT; + case NONE: + default: + return NetworkPolicyDecision.BLOCKED_NO_NETWORK; + } + } +} diff --git a/controller/app/src/main/java/org/appdevforall/k2go/networkpolicy/domain/NetworkPolicyDecision.java b/controller/app/src/main/java/org/appdevforall/k2go/networkpolicy/domain/NetworkPolicyDecision.java new file mode 100644 index 000000000..a42b8c0e3 --- /dev/null +++ b/controller/app/src/main/java/org/appdevforall/k2go/networkpolicy/domain/NetworkPolicyDecision.java @@ -0,0 +1,14 @@ +package org.appdevforall.k2go.networkpolicy.domain; + +/** What a caller must do before starting a heavy (costly) transfer. */ +public enum NetworkPolicyDecision { + + /** Proceed now. The network is free, or the user already consented to spend data. */ + ALLOW, + + /** Ask the user to consent to spending metered data; proceed only on a yes. */ + NEEDS_CONSENT, + + /** No usable network. Do not start; tell the user they are offline. */ + BLOCKED_NO_NETWORK +} diff --git a/controller/app/src/main/java/org/appdevforall/k2go/networkpolicy/domain/NetworkTransition.java b/controller/app/src/main/java/org/appdevforall/k2go/networkpolicy/domain/NetworkTransition.java new file mode 100644 index 000000000..19b094989 --- /dev/null +++ b/controller/app/src/main/java/org/appdevforall/k2go/networkpolicy/domain/NetworkTransition.java @@ -0,0 +1,17 @@ +package org.appdevforall.k2go.networkpolicy.domain; + +/** Pure rule for the proactive alert: warn when the default network becomes metered. */ +public final class NetworkTransition { + + private NetworkTransition() {} + + /** + * True when the default network just crossed INTO a metered state from a + * non-metered one -- the moment to warn the user that further activity spends + * data. A metered-to-metered change, or any change back to unmetered, never + * warns. + */ + public static boolean shouldWarn(NetworkClass previous, NetworkClass next) { + return next == NetworkClass.METERED && previous != NetworkClass.METERED; + } +} diff --git a/controller/app/src/main/java/org/appdevforall/k2go/networkpolicy/presentation/MeteredNetworkObserver.java b/controller/app/src/main/java/org/appdevforall/k2go/networkpolicy/presentation/MeteredNetworkObserver.java new file mode 100644 index 000000000..50d1f31c5 --- /dev/null +++ b/controller/app/src/main/java/org/appdevforall/k2go/networkpolicy/presentation/MeteredNetworkObserver.java @@ -0,0 +1,96 @@ +package org.appdevforall.k2go.networkpolicy.presentation; + +import android.app.Application; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.content.Context; +import android.os.Build; + +import androidx.annotation.NonNull; +import androidx.core.app.NotificationCompat; +import androidx.core.app.NotificationManagerCompat; + +import org.appdevforall.k2go.R; +import org.appdevforall.k2go.networkpolicy.data.AndroidNetworkClassifier; +import org.appdevforall.k2go.networkpolicy.data.SessionMeteredConsentStore; +import org.appdevforall.k2go.networkpolicy.domain.NetworkClass; +import org.appdevforall.k2go.networkpolicy.domain.NetworkTransition; +import org.appdevforall.k2go.sync.transport.NetworkStateLiveData; + +/** + * Process-wide watcher of the default-network cost class. Started once from + * IIABApplication, mirroring {@code ServerLifecycleReconciler} (one process-scoped + * owner). It REUSES the single existing default-network callback + * ({@link NetworkStateLiveData}) instead of registering a second one -- one + * source for the "network changed" fact (ADR-395). + * + *

Two jobs: + *

+ */ +public final class MeteredNetworkObserver { + + private static final String CHANNEL_ID = "network_cost"; + private static final int NOTIF_ID = 0x4E50; // stable id: re-alert replaces, never stacks + + private MeteredNetworkObserver() {} + + private static NetworkClass last = null; + + /** Idempotent; call once from Application.onCreate on the main thread. */ + public static void start(@NonNull Application app) { + ensureChannel(app); + last = AndroidNetworkClassifier.classify(app); + // observeForever keeps NetworkStateLiveData active for the process lifetime, + // which is exactly the scope we want; no separate registration. + NetworkStateLiveData.get(app).observeForever(token -> onNetworkChanged(app)); + } + + private static void onNetworkChanged(@NonNull Application app) { + NetworkClass previous = last; + NetworkClass next = AndroidNetworkClassifier.classify(app); + if (next == previous) return; + last = next; + if (next != NetworkClass.METERED) { + SessionMeteredConsentStore.get().clear(); + } + if (NetworkTransition.shouldWarn(previous, next)) { + notifyMetered(app); + } + } + + private static void notifyMetered(@NonNull Context ctx) { + NotificationCompat.Builder b = new NotificationCompat.Builder(ctx, CHANNEL_ID) + .setSmallIcon(android.R.drawable.stat_sys_warning) + .setContentTitle(ctx.getString(R.string.k2go_netpolicy_switched_title)) + .setContentText(ctx.getString(R.string.k2go_netpolicy_switched_msg)) + .setStyle(new NotificationCompat.BigTextStyle() + .bigText(ctx.getString(R.string.k2go_netpolicy_switched_msg))) + .setCategory(NotificationCompat.CATEGORY_STATUS) + .setAutoCancel(true) + .setOnlyAlertOnce(true) + .setPriority(NotificationCompat.PRIORITY_DEFAULT); + try { + NotificationManagerCompat.from(ctx).notify(NOTIF_ID, b.build()); + } catch (SecurityException ignored) { + // POST_NOTIFICATIONS not granted (Android 13+): the start-gate still protects cost. + } + } + + private static void ensureChannel(@NonNull Context ctx) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + NotificationChannel ch = new NotificationChannel( + CHANNEL_ID, + ctx.getString(R.string.k2go_netpolicy_channel), + NotificationManager.IMPORTANCE_DEFAULT); + NotificationManager m = ctx.getSystemService(NotificationManager.class); + if (m != null) m.createNotificationChannel(ch); + } + } +} diff --git a/controller/app/src/main/java/org/appdevforall/k2go/networkpolicy/presentation/NetworkPolicyGate.java b/controller/app/src/main/java/org/appdevforall/k2go/networkpolicy/presentation/NetworkPolicyGate.java new file mode 100644 index 000000000..76f6eda7b --- /dev/null +++ b/controller/app/src/main/java/org/appdevforall/k2go/networkpolicy/presentation/NetworkPolicyGate.java @@ -0,0 +1,68 @@ +package org.appdevforall.k2go.networkpolicy.presentation; + +import android.app.Activity; +import android.view.View; + +import androidx.annotation.NonNull; + +import org.appdevforall.k2go.R; +import org.appdevforall.k2go.networkpolicy.data.AndroidNetworkClassifier; +import org.appdevforall.k2go.networkpolicy.data.SessionMeteredConsentStore; +import org.appdevforall.k2go.networkpolicy.domain.MeteredConsentStore; +import org.appdevforall.k2go.networkpolicy.domain.NetworkClass; +import org.appdevforall.k2go.networkpolicy.domain.NetworkPolicy; +import org.appdevforall.k2go.networkpolicy.domain.NetworkPolicyDecision; +import org.appdevforall.k2go.ui.dialog.BrandDialog; +import org.appdevforall.k2go.util.Snackbars; + +/** + * The single consult point before any heavy download starts (ADR-395). A caller + * wraps its existing start call: + * + *
NetworkPolicyGate.guardHeavyStart(activity, () -> a.startZimDownload());
+ * + *

Stateless, like {@code OpReturnNavigator}: it owns no "is metered" flag. It + * reads the live class off {@link AndroidNetworkClassifier} and the session + * consent off {@link SessionMeteredConsentStore}, applies the pure + * {@link NetworkPolicy}, and either proceeds, asks, or blocks. + * + *

It gates the START only. It does NOT control a transfer already in flight -- + * the content bytes are pulled by the in-proot server over the device default + * network, which Android gives the app no handle to throttle (ADR-395). Callers + * put this at the user's commit point (the Download button), never on the + * background drain that re-hands an already-authorized wishlist. + */ +public final class NetworkPolicyGate { + + private NetworkPolicyGate() {} + + private static final NetworkPolicy POLICY = new NetworkPolicy(); + + public static void guardHeavyStart(@NonNull Activity activity, @NonNull Runnable onProceed) { + MeteredConsentStore consent = SessionMeteredConsentStore.get(); + NetworkClass net = AndroidNetworkClassifier.classify(activity); + NetworkPolicyDecision decision = POLICY.decideHeavyStart(net, consent.isGranted()); + switch (decision) { + case ALLOW: + onProceed.run(); + return; + case NEEDS_CONSENT: + new BrandDialog(activity) + .setTitle(R.string.k2go_netpolicy_metered_title) + .setMessage(R.string.k2go_netpolicy_metered_msg) + .setPositive(R.string.k2go_netpolicy_continue, () -> { + consent.grant(); + onProceed.run(); + }) + .setNegative(R.string.k2go_netpolicy_not_now, null) + .show(); + return; + case BLOCKED_NO_NETWORK: + default: + View root = activity.findViewById(android.R.id.content); + if (root != null) { + Snackbars.make(root, R.string.k2go_netpolicy_offline).show(); + } + } + } +} diff --git a/controller/app/src/main/java/org/appdevforall/k2go/redesign/ZimConfirmFragment.java b/controller/app/src/main/java/org/appdevforall/k2go/redesign/ZimConfirmFragment.java index b26c80eb8..1d8f6c50e 100644 --- a/controller/app/src/main/java/org/appdevforall/k2go/redesign/ZimConfirmFragment.java +++ b/controller/app/src/main/java/org/appdevforall/k2go/redesign/ZimConfirmFragment.java @@ -106,7 +106,14 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup c if (!(getActivity() instanceof SetupLibraryActivity)) return; SetupLibraryActivity a = (SetupLibraryActivity) getActivity(); if (banks) a.zimWizardConfirm(); // no box yet: bank it - else if (!DashboardRebuild.blockedByUpdate(v)) a.startZimDownload(); // ADFA-5074 / ADFA-5333 + // ADFA-5074 / ADFA-5333: blocked while a dashboard update runs. + // K2GO-395 (ADR-395): gate a metered start behind explicit consent (cost awareness). + // The gate sits at the user commit point, not on ZimProvisioner.drain (that re-hands an + // already-authorized wishlist every ~2 s and would re-prompt). + else if (!DashboardRebuild.blockedByUpdate(v)) { + org.appdevforall.k2go.networkpolicy.presentation.NetworkPolicyGate + .guardHeavyStart(a, a::startZimDownload); + } }); return root; diff --git a/controller/app/src/main/res/values/strings_untranslated.xml b/controller/app/src/main/res/values/strings_untranslated.xml index 85de9d5d7..8ef921124 100644 --- a/controller/app/src/main/res/values/strings_untranslated.xml +++ b/controller/app/src/main/res/values/strings_untranslated.xml @@ -10,7 +10,15 @@ --> - + + You are on mobile data + This download uses mobile data. It can add cost or use up your monthly data. Do you want to continue? + Continue on data + Not now + No network. Connect to Wi-Fi to download. + You switched to mobile data + New downloads now use mobile data. This can add cost or use up your monthly data. + Data cost alerts diff --git a/controller/app/src/test/java/org/appdevforall/k2go/networkpolicy/domain/NetworkClassTest.java b/controller/app/src/test/java/org/appdevforall/k2go/networkpolicy/domain/NetworkClassTest.java new file mode 100644 index 000000000..4b058eda6 --- /dev/null +++ b/controller/app/src/test/java/org/appdevforall/k2go/networkpolicy/domain/NetworkClassTest.java @@ -0,0 +1,30 @@ +package org.appdevforall.k2go.networkpolicy.domain; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +/** + * Pure-JVM tests for the cost-class mapping. The cases mirror the device + * evidence in ADR-395: Wi-Fi "HIKVISION" carried NOT_METERED; the "Bienestar" + * LTE internet APN did not; the cellular IMS PDN carried NOT_METERED but no + * INTERNET, so it never becomes the internet-bearing default that gets classified. + */ +public class NetworkClassTest { + + @Test + public void wifiUnmetered_isUnmetered() { + assertEquals(NetworkClass.UNMETERED, NetworkClass.from(true, true)); + } + + @Test + public void cellularInternetApn_isMetered() { + assertEquals(NetworkClass.METERED, NetworkClass.from(true, false)); + } + + @Test + public void noInternet_isNone_whateverTheMeteredFlag() { + assertEquals(NetworkClass.NONE, NetworkClass.from(false, false)); + assertEquals(NetworkClass.NONE, NetworkClass.from(false, true)); + } +} diff --git a/controller/app/src/test/java/org/appdevforall/k2go/networkpolicy/domain/NetworkPolicyTest.java b/controller/app/src/test/java/org/appdevforall/k2go/networkpolicy/domain/NetworkPolicyTest.java new file mode 100644 index 000000000..fe8bb76c4 --- /dev/null +++ b/controller/app/src/test/java/org/appdevforall/k2go/networkpolicy/domain/NetworkPolicyTest.java @@ -0,0 +1,33 @@ +package org.appdevforall.k2go.networkpolicy.domain; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +/** Pure-JVM tests for the heavy-start decision table. No Android, no network. */ +public class NetworkPolicyTest { + + private final NetworkPolicy policy = new NetworkPolicy(); + + @Test + public void unmetered_alwaysAllows() { + assertEquals(NetworkPolicyDecision.ALLOW, policy.decideHeavyStart(NetworkClass.UNMETERED, false)); + assertEquals(NetworkPolicyDecision.ALLOW, policy.decideHeavyStart(NetworkClass.UNMETERED, true)); + } + + @Test + public void metered_withoutConsent_needsConsent() { + assertEquals(NetworkPolicyDecision.NEEDS_CONSENT, policy.decideHeavyStart(NetworkClass.METERED, false)); + } + + @Test + public void metered_withConsent_allows() { + assertEquals(NetworkPolicyDecision.ALLOW, policy.decideHeavyStart(NetworkClass.METERED, true)); + } + + @Test + public void noNetwork_blocks_regardlessOfConsent() { + assertEquals(NetworkPolicyDecision.BLOCKED_NO_NETWORK, policy.decideHeavyStart(NetworkClass.NONE, false)); + assertEquals(NetworkPolicyDecision.BLOCKED_NO_NETWORK, policy.decideHeavyStart(NetworkClass.NONE, true)); + } +} diff --git a/controller/app/src/test/java/org/appdevforall/k2go/networkpolicy/domain/NetworkTransitionTest.java b/controller/app/src/test/java/org/appdevforall/k2go/networkpolicy/domain/NetworkTransitionTest.java new file mode 100644 index 000000000..13cbeaaa3 --- /dev/null +++ b/controller/app/src/test/java/org/appdevforall/k2go/networkpolicy/domain/NetworkTransitionTest.java @@ -0,0 +1,30 @@ +package org.appdevforall.k2go.networkpolicy.domain; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +/** Pure-JVM tests for the "warn on becoming metered" edge rule. */ +public class NetworkTransitionTest { + + @Test + public void unmeteredToMetered_warns() { + assertTrue(NetworkTransition.shouldWarn(NetworkClass.UNMETERED, NetworkClass.METERED)); + } + + @Test + public void noneToMetered_warns() { + assertTrue(NetworkTransition.shouldWarn(NetworkClass.NONE, NetworkClass.METERED)); + } + + @Test + public void meteredToMetered_doesNotWarn() { + assertFalse(NetworkTransition.shouldWarn(NetworkClass.METERED, NetworkClass.METERED)); + } + + @Test + public void meteredToUnmetered_doesNotWarn() { + assertFalse(NetworkTransition.shouldWarn(NetworkClass.METERED, NetworkClass.UNMETERED)); + } +} diff --git a/controller/docs/ADR-395-network-cost-consent.md b/controller/docs/ADR-395-network-cost-consent.md new file mode 100644 index 000000000..96eb843e9 --- /dev/null +++ b/controller/docs/ADR-395-network-cost-consent.md @@ -0,0 +1,217 @@ +# ADR-395 -- Network cost consent (metered-data gate) + +- Status: Proposed +- Date: 2026-09-06 +- Ticket: K2GO-395 (Relates to K2GO-4 "Resilient download contract") +- Author: AppDevForAll + +## 1. Context + +K2Go downloads very heavy content: the rootfs image is ~2-4 GB, ZIM files reach +tens to hundreds of GB, maps reach tens of GB. A user on a metered link (mobile +data, or a metered Wi-Fi hotspot) can spend real money or burn a monthly data +plan with a single tap, with no warning today. + +K2GO-4 already makes downloads **resilient** -- they survive a network change +mid-download (Wi-Fi to mobile) by pause/resume/reconnect. Resilience is not the +same fact as **cost consent**. Surviving a switch to mobile data is not the same +as asking permission to spend it. This ADR defines the cost-consent mechanism. +There is no ticket and no code for it today (verified: a repo-wide search for +`isActiveNetworkMetered`, `NOT_METERED`, `setAllowedOverMetered` returns zero +hits). + +### 1.1 The three-process reality (why this is not a socket problem) + +Egress does not come from one place. It comes from three, and only some are +under the app's control: + +| What is downloaded | Which process pulls the bytes | App can throttle the socket? | +|---|---|---| +| Rootfs tarball + proot Debian base (GB) | on-device `aria2` (`libaria2c.so`) | Yes (own process) | +| ZIM / Books / Maps / Kolibri content (GB) | the in-proot server (dash-node); device only POSTs + polls | **No** -- not the app's socket | +| OTA APK, portal APK/PDF | Android system `DownloadManager` | Via its own API | +| Manifests, `.meta4` size, catalog ETags (KB) | app process (`HttpURLConnection`) | Yes | + +The key consequence: for content (the biggest cost driver) the app **cannot** +make the transfer "Wi-Fi only" at the network layer, because the in-proot server +holds the socket, not the app. The only honest lever is to **not authorize the +job to start** (gate before the POST), and to cancel/pause it through the +existing REST cancel if the user asks. + +### 1.2 The primitive is metered, not "cellular vs Wi-Fi" + +The user goal is "do not spend costed data without asking". The correct signal +is therefore **metered vs not-metered**, not transport. Two facts force this: + +- A phone hotspot is Wi-Fi but costs data. "Wi-Fi" does not mean "free". +- On real hardware "cellular" is not one network. See the device evidence + (Section 6): the carrier's IMS PDN reports NOT_METERED while its internet APN + does not. Keying on `TRANSPORT_CELLULAR` would misjudge cost. + +So the rule keys on the **active default network's** `NET_CAPABILITY_NOT_METERED`. + +## 2. Decision + +Add one **cost-consent gate** consulted at the START of every heavy download, +plus one process-wide **metered observer** for the proactive alert. Defensive by +design: do not start anything costly on a metered link without consent; do not +attempt fine control of a transfer already in flight (the app cannot). + +### 2.1 Behavior + +1. **Start gate.** Before a heavy download starts, classify the active default + network. If unmetered -> proceed. If metered and the user has not consented + this session -> ask ("You are on mobile data ... continue?"). If they decline, + do not start. If no network -> tell them they are offline. Once consent is + given, it holds for the session: 1 KB or 5 GB, it does not ask again. +2. **Proactive alert.** If the default network crosses INTO metered while the app + runs -- even with nothing pending -- post a notification so the user knows + further activity spends data. +3. **In flight = best effort only.** A transfer already running on a link the app + does not own is left alone. Resilience (K2GO-4) means it can pause/resume, but + this ADR does not add fine control. Offering "cancel or continue" on such a + transfer is a possible follow-up, not part of this contract. + +### 2.2 One source per fact (anti-duplication ledger) + +| Fact | Existing owner | This design | +|---|---|---| +| "network changed" | `sync/transport/NetworkStateLiveData` (the only default-network callback) | REUSED via `observeForever`; no second registration | +| "what is the network" | `DashboardRebuild.hasInternet`, `InstallService.hasValidatedInternet` (two readers today) | New `AndroidNetworkClassifier` becomes the one reader; fold the two existing ones into it as a follow-up | +| "is a heavy transfer running" | `ContentDownloadSession`, `InstallProgressRepository` | READ if needed; never duplicated | +| "did the user consent to spend data" | none (new fact) | `SessionMeteredConsentStore` (in-memory) | + +### 2.3 Lifecycle of the consent grant + +- **Standing preference** (a future "Wi-Fi only" toggle) would be persisted, + default on. Not built yet; the gate already behaves as if it is on. +- **Session grant** is ephemeral, in memory. Set by the consent dialog. Cleared + by the observer when the network leaves metered, and by process death (the only + store keeps it in memory). A persisted "always allow" is deliberately NOT + offered: it would defeat cost awareness and would be the stuck-marker + anti-pattern (a persisted flag nobody clears). +- **If the process dies mid-metered-download**: on restart the grant is gone; if + still metered and a transfer wants to resume, the gate asks again. No stuck + state. + +## 3. Design (layered feature `networkpolicy`) + +New self-contained feature package `org.appdevforall.k2go.networkpolicy`, wired +by hand (no DI), placed beside the existing `network` (DNS) feature. + +``` +networkpolicy/ + domain/ NetworkClass, NetworkPolicyDecision, NetworkPolicy, + NetworkTransition, MeteredConsentStore (pure JVM, unit-tested) + data/ AndroidNetworkClassifier (the one ConnectivityManager reader), + SessionMeteredConsentStore (in-memory grant) + presentation/ NetworkPolicyGate (stateless start gate + consent dialog), + MeteredNetworkObserver (process-wide alert + grant lifecycle) +``` + +- `NetworkPolicyGate` is stateless, in the style of `OpReturnNavigator`: it owns + no "is metered" flag; it reads the live class and the session grant and returns + a decision. +- `MeteredNetworkObserver` is one process-scoped owner started from + `IIABApplication`, in the style of `ServerLifecycleReconciler`. + +## 4. Seams (where the gate is consulted) + +Each download family has a single static start entry point. The gate wraps the +**user commit point**, never the background drain (`ZimProvisioner.drain` runs +every ~2 s and would re-prompt). + +| Family | Start entry point | Commit-point seam | +|---|---|---| +| ZIM | `ZimDownloadService.start` | `ZimConfirmFragment` "Start" click (REFERENCE, landed) | +| Books | `BooksDownloadService.start` | Books confirm click (follow-up) | +| Kolibri | `KolibriSeedService.start` | Kolibri seed confirm (follow-up) | +| Rootfs/modules | `InstallService` via `SetupProgressActivity`/`ModuleProvisioner`/`MapsProvisioner` | install confirm (follow-up) | +| Portal APK/PDF + OTA | `DownloadManager` enqueue in `PortalActivity`, `UpdateController` | gate + `setAllowedOverMetered(false)` (follow-up) | + +## 5. Reference implementation status (this change) + +Landed as a compiling, tested starting point for the implementer: + +- Domain, pure JVM, unit-tested: `NetworkClass`, `NetworkPolicy`, + `NetworkTransition`, `NetworkPolicyDecision`, `MeteredConsentStore` + (`NetworkPolicyTest`, `NetworkClassTest`, `NetworkTransitionTest` -- green). +- Data: `AndroidNetworkClassifier`, `SessionMeteredConsentStore`. +- Presentation: `NetworkPolicyGate` (+ `BrandDialog` consent), `MeteredNetworkObserver`. +- Wiring: observer started in `IIABApplication`; gate wired at the ZIM commit + point (`ZimConfirmFragment`). +- Strings parked in `strings_untranslated.xml` (WIP; translate to 33 locales and + move to a feature file before merge -- l10n policy). + +Remaining to finish the contract: the other four seams, the two-way fold of the +existing `hasInternet` readers into the classifier, and the l10n migration. + +## 6. Device evidence appendix (dark surfaces flattened) + +Measured on Samsung SM-A165M (`RF8Y80CE2DA`), Android 15, SIM "Bienestar" LTE +(25 GB plan), via `adb shell svc wifi disable/enable` + `dumpsys connectivity`. + +| Network | Transport | Has NOT_METERED? | Has INTERNET? | Classifier verdict | +|---|---|---|---|---| +| Wi-Fi "HIKVISION_B7FD" (home router) | WIFI | Yes (Metered hint: false) | Yes | UNMETERED | +| Cellular internet APN (rmnet1, default when Wi-Fi off) | CELLULAR | **No** | Yes | **METERED** | +| Cellular IMS PDN | CELLULAR | Yes | No (IMS only) | never the internet default | +| Phone hotspot "Galaxy A16 4AEC" (Samsung tether), seen by an OPPO CPH2557 client | WIFI | **No** (Metered hint: true) | Yes | **METERED** | + +Conclusions, now empirical, not assumed: + +1. Wi-Fi here is unmetered; the LTE internet APN is metered. The classifier rule + (`NOT_METERED` on the active default) produces the right verdict for both. +2. "Cellular" is not monolithic: the IMS PDN carries NOT_METERED. A + transport-based rule would have called the whole radio unmetered and leaked + the plan. This is the concrete reason the rule keys on metered, not transport. +3. The active default flips correctly on Wi-Fi toggle (Wi-Fi network id when on; + cellular id when off), so `getActiveNetwork()` is the right anchor. +4. A phone hotspot CAN be flagged metered natively: the Samsung A16 tether + advertised the metered bit and the OPPO client's Wi-Fi network dropped + NOT_METERED (Metered hint: true). The classifier read METERED with no special + case -- the "Wi-Fi is not always free" case is caught by the same rule. Caveat: + this is OEM/AP-dependent. A router or hotspot that does not advertise the bit, + or a user who marks the Wi-Fi "unmetered", will read UNMETERED; the manual + override and the proactive alert exist for exactly that residual gap. + +## 7. Test protocol (remaining dark surfaces) + +Run before shipping the full contract: + +1. **Phone-hotspot metered detection (MEASURED -- Section 6, row 4).** Samsung A16 + tether -> OPPO CPH2557 client: the client's Wi-Fi network had Metered hint: + true and no NOT_METERED, so the classifier read METERED with no special case. + The residual case to keep in mind: an AP/router that does not advertise the + metered bit, or a user who marks the Wi-Fi "unmetered", reads UNMETERED -- + covered by the manual override (future toggle) and the proactive alert, not by + auto-detection. Re-run with other AP brands as they appear. +2. **Premise: server content download consumes the SIM.** With the box up and the + device on cellular, start a small ZIM and watch `/proc/net/dev` `rmnet` bytes + climb. Architecturally certain (rmnet is the only uplink; proot has no + independent radio) -- measure once to confirm. +3. **DownloadManager over metered.** Enqueue with `setAllowedOverMetered(false)` + on cellular; confirm it holds until Wi-Fi (and that the gate asks first, so the + hold is a chosen fallback, not a silent stall). +4. **Callback latency.** Time `NetworkStateLiveData` firing after a transport flip + to confirm the proactive alert is prompt. + +## 8. Consequences + +- Positive: one owner for cost policy; reuses the existing change callback; + reduces (does not add) `hasInternet` duplication; empirically grounded rule. +- Cost: new UI strings need 33-locale translation before merge; five seams to + wire; device verification per the protocol. +- Out of scope: fine control of in-flight transfers; a persisted "always allow"; + P2P (rsync clone -- LAN, no cost). + +## 9. Alternatives rejected + +- **Key on `TRANSPORT_CELLULAR`.** Rejected: the IMS PDN evidence shows cellular + is not uniformly metered, and a metered Wi-Fi hotspot would be missed. +- **A per-transfer byte threshold ("ask only above N MB").** Rejected: a global + threshold across every seam adds complexity for little gain. The model is + binary consent. A small-payload exemption (< ~1 MB) may arrive later, per seam, + not as a global rule. +- **Block at the socket / bind the process to Wi-Fi.** Rejected: cannot cover the + in-proot server's egress, and would fight `WifiNetworkBinder` (LAN sync). From 5fdbad77847e753b081c3ac56bad80bbf1dbafb0 Mon Sep 17 00:00:00 2001 From: Luis Guzman Date: Sun, 6 Sep 2026 21:53:46 -0600 Subject: [PATCH 02/18] K2GO-395 docs(networkpolicy): pinpoint remaining seams + recipes in ADR-395 Add exact file:line commit points for the Books/Kolibri/Maps/install/DownloadManager seams, a one-line wrap recipe per seam, and the POST_NOTIFICATIONS deployment note, so the remaining wiring is copy-paste for the next implementer. --- .../docs/ADR-395-network-cost-consent.md | 50 +++++++++++++++---- 1 file changed, 41 insertions(+), 9 deletions(-) diff --git a/controller/docs/ADR-395-network-cost-consent.md b/controller/docs/ADR-395-network-cost-consent.md index 96eb843e9..4d19b1889 100644 --- a/controller/docs/ADR-395-network-cost-consent.md +++ b/controller/docs/ADR-395-network-cost-consent.md @@ -117,17 +117,44 @@ networkpolicy/ ## 4. Seams (where the gate is consulted) -Each download family has a single static start entry point. The gate wraps the -**user commit point**, never the background drain (`ZimProvisioner.drain` runs -every ~2 s and would re-prompt). +Each content family has a `*ConfirmFragment` with a Start/Add button whose click +is the user commit point. The gate wraps THAT click, never the background +`*Provisioner.drain` (it runs every ~2 s to re-hand an already-authorized +wishlist and would re-prompt). Every confirm fragment has the same shape as the +reference (`ZimConfirmFragment`), so each remaining seam is a one-line wrap. -| Family | Start entry point | Commit-point seam | +| Family | Commit-point seam (exact) | Wrap | |---|---|---| -| ZIM | `ZimDownloadService.start` | `ZimConfirmFragment` "Start" click (REFERENCE, landed) | -| Books | `BooksDownloadService.start` | Books confirm click (follow-up) | -| Kolibri | `KolibriSeedService.start` | Kolibri seed confirm (follow-up) | -| Rootfs/modules | `InstallService` via `SetupProgressActivity`/`ModuleProvisioner`/`MapsProvisioner` | install confirm (follow-up) | -| Portal APK/PDF + OTA | `DownloadManager` enqueue in `PortalActivity`, `UpdateController` | gate + `setAllowedOverMetered(false)` (follow-up) | +| ZIM | `ZimConfirmFragment.java:105-110` -> `a.startZimDownload()` | LANDED (reference) | +| Books | `BooksConfirmFragment.java:82` -> `a.startBooksDownload()` | `guardHeavyStart(a, a::startBooksDownload)` | +| Kolibri | `KolibriConfirmFragment.java:227` -> `startLive(chosen)` | `guardHeavyStart(requireActivity(), () -> startLive(chosen))` | +| Maps | `MapsConfirmFragment.java:90` -- the live `else` branch (~:99), NOT the `wizard` branch | wrap the live-download body | +| Rootfs/modules install | `InstallService` started at `SetupProgressActivity.java:1401`; commit point is the wizard "Install" confirmation | wrap that confirm before starting InstallService | + +DownloadManager seams differ -- no `Service.start`; the app enqueues and the +system transfers. Consult the gate first, then honor the decision (proceed on +consent, or set `setAllowedOverMetered(false)` as the fallback): + +| Path | Enqueue site | +|---|---| +| OTA APK | `UpdateController.java:210-223` | +| Portal APK | `PortalActivity.java:464-477` | +| Portal PDF / other box file | `PortalActivity.java:502-513` | + +### 4.1 Recipe (content seam) + +Replace `X.startYDownload()` at the commit click with +`NetworkPolicyGate.guardHeavyStart(activity, activity::startYDownload)`, where +`activity` is the hosting Activity (the consent dialog needs an Activity context). +A seam with no Activity (a pure background start) cannot show the dialog -- but +those are post-authorization drains, correctly left ungated. + +### 4.2 Recipe (DownloadManager seam) + +Before `dm.enqueue(request)`: classify with `AndroidNetworkClassifier`. If metered +and not consented, either ask via the gate or set +`request.setAllowedOverMetered(false)` so the system holds it for Wi-Fi. If +unmetered or already consented, enqueue as today. ## 5. Reference implementation status (this change) @@ -202,6 +229,11 @@ Run before shipping the full contract: reduces (does not add) `hasInternet` duplication; empirically grounded rule. - Cost: new UI strings need 33-locale translation before merge; five seams to wire; device verification per the protocol. +- Deployment detail: the proactive alert posts a notification, so on Android 13+ + it needs the POST_NOTIFICATIONS runtime permission. The observer swallows the + SecurityException when it is not granted, so the start gate (the primary cost + protection) still works with no notification permission. If the app does not + already request POST_NOTIFICATIONS elsewhere, the alert is silent until it does. - Out of scope: fine control of in-flight transfers; a persisted "always allow"; P2P (rsync clone -- LAN, no cost). From bc9ab6be2167c1f1678a0406a66de214befe4f7c Mon Sep 17 00:00:00 2001 From: Luis Guzman Date: Sun, 6 Sep 2026 22:06:40 -0600 Subject: [PATCH 03/18] K2GO-395 l10n(networkpolicy): translate consent strings to 33 locales Move the 8 network-cost consent strings out of strings_untranslated.xml into values*/strings_networkpolicy.xml with all 33 locale values (machine-generated, pending human review), per the l10n policy. strings_untranslated.xml is clear again. ADR-395 updated: l10n done pending review. --- .../main/res/values-ar/strings_networkpolicy.xml | 12 ++++++++++++ .../main/res/values-az/strings_networkpolicy.xml | 12 ++++++++++++ .../main/res/values-bg/strings_networkpolicy.xml | 12 ++++++++++++ .../main/res/values-bn/strings_networkpolicy.xml | 12 ++++++++++++ .../main/res/values-cs/strings_networkpolicy.xml | 12 ++++++++++++ .../main/res/values-de/strings_networkpolicy.xml | 12 ++++++++++++ .../main/res/values-el/strings_networkpolicy.xml | 12 ++++++++++++ .../main/res/values-es/strings_networkpolicy.xml | 12 ++++++++++++ .../main/res/values-fa/strings_networkpolicy.xml | 12 ++++++++++++ .../main/res/values-fr/strings_networkpolicy.xml | 12 ++++++++++++ .../main/res/values-gu/strings_networkpolicy.xml | 12 ++++++++++++ .../main/res/values-hi/strings_networkpolicy.xml | 12 ++++++++++++ .../main/res/values-hu/strings_networkpolicy.xml | 12 ++++++++++++ .../main/res/values-in/strings_networkpolicy.xml | 12 ++++++++++++ .../main/res/values-it/strings_networkpolicy.xml | 12 ++++++++++++ .../main/res/values-ja/strings_networkpolicy.xml | 12 ++++++++++++ .../main/res/values-ko/strings_networkpolicy.xml | 12 ++++++++++++ .../main/res/values-lt/strings_networkpolicy.xml | 12 ++++++++++++ .../main/res/values-nl/strings_networkpolicy.xml | 12 ++++++++++++ .../main/res/values-no/strings_networkpolicy.xml | 12 ++++++++++++ .../main/res/values-pl/strings_networkpolicy.xml | 12 ++++++++++++ .../main/res/values-pt/strings_networkpolicy.xml | 12 ++++++++++++ .../main/res/values-ro/strings_networkpolicy.xml | 12 ++++++++++++ .../res/values-ru-rRU/strings_networkpolicy.xml | 12 ++++++++++++ .../main/res/values-sk/strings_networkpolicy.xml | 12 ++++++++++++ .../main/res/values-sr/strings_networkpolicy.xml | 12 ++++++++++++ .../main/res/values-sw/strings_networkpolicy.xml | 12 ++++++++++++ .../main/res/values-ta/strings_networkpolicy.xml | 12 ++++++++++++ .../main/res/values-tr/strings_networkpolicy.xml | 12 ++++++++++++ .../main/res/values-uk/strings_networkpolicy.xml | 12 ++++++++++++ .../main/res/values-vi/strings_networkpolicy.xml | 12 ++++++++++++ .../main/res/values-yo/strings_networkpolicy.xml | 12 ++++++++++++ .../res/values-zh-rCN/strings_networkpolicy.xml | 12 ++++++++++++ .../src/main/res/values/strings_networkpolicy.xml | 12 ++++++++++++ .../src/main/res/values/strings_untranslated.xml | 14 +++----------- controller/docs/ADR-395-network-cost-consent.md | 13 +++++++------ 36 files changed, 418 insertions(+), 17 deletions(-) create mode 100644 controller/app/src/main/res/values-ar/strings_networkpolicy.xml create mode 100644 controller/app/src/main/res/values-az/strings_networkpolicy.xml create mode 100644 controller/app/src/main/res/values-bg/strings_networkpolicy.xml create mode 100644 controller/app/src/main/res/values-bn/strings_networkpolicy.xml create mode 100644 controller/app/src/main/res/values-cs/strings_networkpolicy.xml create mode 100644 controller/app/src/main/res/values-de/strings_networkpolicy.xml create mode 100644 controller/app/src/main/res/values-el/strings_networkpolicy.xml create mode 100644 controller/app/src/main/res/values-es/strings_networkpolicy.xml create mode 100644 controller/app/src/main/res/values-fa/strings_networkpolicy.xml create mode 100644 controller/app/src/main/res/values-fr/strings_networkpolicy.xml create mode 100644 controller/app/src/main/res/values-gu/strings_networkpolicy.xml create mode 100644 controller/app/src/main/res/values-hi/strings_networkpolicy.xml create mode 100644 controller/app/src/main/res/values-hu/strings_networkpolicy.xml create mode 100644 controller/app/src/main/res/values-in/strings_networkpolicy.xml create mode 100644 controller/app/src/main/res/values-it/strings_networkpolicy.xml create mode 100644 controller/app/src/main/res/values-ja/strings_networkpolicy.xml create mode 100644 controller/app/src/main/res/values-ko/strings_networkpolicy.xml create mode 100644 controller/app/src/main/res/values-lt/strings_networkpolicy.xml create mode 100644 controller/app/src/main/res/values-nl/strings_networkpolicy.xml create mode 100644 controller/app/src/main/res/values-no/strings_networkpolicy.xml create mode 100644 controller/app/src/main/res/values-pl/strings_networkpolicy.xml create mode 100644 controller/app/src/main/res/values-pt/strings_networkpolicy.xml create mode 100644 controller/app/src/main/res/values-ro/strings_networkpolicy.xml create mode 100644 controller/app/src/main/res/values-ru-rRU/strings_networkpolicy.xml create mode 100644 controller/app/src/main/res/values-sk/strings_networkpolicy.xml create mode 100644 controller/app/src/main/res/values-sr/strings_networkpolicy.xml create mode 100644 controller/app/src/main/res/values-sw/strings_networkpolicy.xml create mode 100644 controller/app/src/main/res/values-ta/strings_networkpolicy.xml create mode 100644 controller/app/src/main/res/values-tr/strings_networkpolicy.xml create mode 100644 controller/app/src/main/res/values-uk/strings_networkpolicy.xml create mode 100644 controller/app/src/main/res/values-vi/strings_networkpolicy.xml create mode 100644 controller/app/src/main/res/values-yo/strings_networkpolicy.xml create mode 100644 controller/app/src/main/res/values-zh-rCN/strings_networkpolicy.xml create mode 100644 controller/app/src/main/res/values/strings_networkpolicy.xml diff --git a/controller/app/src/main/res/values-ar/strings_networkpolicy.xml b/controller/app/src/main/res/values-ar/strings_networkpolicy.xml new file mode 100644 index 000000000..70291527c --- /dev/null +++ b/controller/app/src/main/res/values-ar/strings_networkpolicy.xml @@ -0,0 +1,12 @@ + + + + أنت تستخدم بيانات الجوال + يستخدم هذا التنزيل بيانات الجوال. قد يترتب عليه تكلفة أو يستنفد باقتك الشهرية. هل تريد المتابعة؟ + المتابعة عبر البيانات + ليس الآن + لا توجد شبكة. اتصل بشبكة Wi-Fi للتنزيل. + لقد انتقلت إلى بيانات الجوال + تستخدم التنزيلات الجديدة الآن بيانات الجوال. قد يترتب على ذلك تكلفة أو يستنفد باقتك الشهرية. + تنبيهات تكلفة البيانات + diff --git a/controller/app/src/main/res/values-az/strings_networkpolicy.xml b/controller/app/src/main/res/values-az/strings_networkpolicy.xml new file mode 100644 index 000000000..a5398e08b --- /dev/null +++ b/controller/app/src/main/res/values-az/strings_networkpolicy.xml @@ -0,0 +1,12 @@ + + + + Mobil internetdən istifadə edirsiniz + Bu yükləmə mobil internetdən istifadə edir. Bu, xərc yarada və ya aylıq paketinizi bitirə bilər. Davam etmək istəyirsiniz? + Mobil internetlə davam et + İndi yox + Şəbəkə yoxdur. Yükləmək üçün Wi-Fi-a qoşulun. + Mobil internetə keçdiniz + Yeni yükləmələr indi mobil internetdən istifadə edir. Bu, xərc yarada və ya aylıq paketinizi bitirə bilər. + Data xərci bildirişləri + diff --git a/controller/app/src/main/res/values-bg/strings_networkpolicy.xml b/controller/app/src/main/res/values-bg/strings_networkpolicy.xml new file mode 100644 index 000000000..9cc33da70 --- /dev/null +++ b/controller/app/src/main/res/values-bg/strings_networkpolicy.xml @@ -0,0 +1,12 @@ + + + + Използвате мобилни данни + Това изтегляне използва мобилни данни. Може да има такса или да изчерпи месечния ви план. Искате ли да продължите? + Продължи с мобилни данни + Не сега + Няма мрежа. Свържете се с Wi-Fi, за да изтеглите. + Превключихте към мобилни данни + Новите изтегляния вече използват мобилни данни. Това може да има такса или да изчерпи месечния ви план. + Известия за разход на данни + diff --git a/controller/app/src/main/res/values-bn/strings_networkpolicy.xml b/controller/app/src/main/res/values-bn/strings_networkpolicy.xml new file mode 100644 index 000000000..9b7545009 --- /dev/null +++ b/controller/app/src/main/res/values-bn/strings_networkpolicy.xml @@ -0,0 +1,12 @@ + + + + আপনি মোবাইল ডেটা ব্যবহার করছেন + এই ডাউনলোডটি মোবাইল ডেটা ব্যবহার করে। এতে খরচ হতে পারে বা আপনার মাসিক ডেটা শেষ হতে পারে। আপনি কি চালিয়ে যেতে চান? + ডেটা দিয়ে চালিয়ে যান + এখন নয় + কোনো নেটওয়ার্ক নেই। ডাউনলোড করতে Wi-Fi এ সংযোগ করুন। + আপনি মোবাইল ডেটাতে স্যুইচ করেছেন + নতুন ডাউনলোডগুলি এখন মোবাইল ডেটা ব্যবহার করে। এতে খরচ হতে পারে বা আপনার মাসিক ডেটা শেষ হতে পারে। + ডেটা খরচের সতর্কতা + diff --git a/controller/app/src/main/res/values-cs/strings_networkpolicy.xml b/controller/app/src/main/res/values-cs/strings_networkpolicy.xml new file mode 100644 index 000000000..dc3ff4053 --- /dev/null +++ b/controller/app/src/main/res/values-cs/strings_networkpolicy.xml @@ -0,0 +1,12 @@ + + + + Používáte mobilní data + Toto stahování používá mobilní data. Může být zpoplatněno nebo vyčerpat váš měsíční limit. Chcete pokračovat? + Pokračovat na mobilních datech + Teď ne + Žádná síť. Připojte se k Wi-Fi pro stažení. + Přepnuli jste na mobilní data + Nová stahování nyní používají mobilní data. Může být zpoplatněno nebo vyčerpat váš měsíční limit. + Upozornění na náklady za data + diff --git a/controller/app/src/main/res/values-de/strings_networkpolicy.xml b/controller/app/src/main/res/values-de/strings_networkpolicy.xml new file mode 100644 index 000000000..a842c46bb --- /dev/null +++ b/controller/app/src/main/res/values-de/strings_networkpolicy.xml @@ -0,0 +1,12 @@ + + + + Sie nutzen mobile Daten + Dieser Download nutzt mobile Daten. Das kann Kosten verursachen oder Ihr monatliches Datenvolumen aufbrauchen. Möchten Sie fortfahren? + Mit mobilen Daten fortfahren + Nicht jetzt + Kein Netzwerk. Verbinden Sie sich mit WLAN, um herunterzuladen. + Sie haben zu mobilen Daten gewechselt + Neue Downloads nutzen jetzt mobile Daten. Das kann Kosten verursachen oder Ihr monatliches Datenvolumen aufbrauchen. + Warnungen zu Datenkosten + diff --git a/controller/app/src/main/res/values-el/strings_networkpolicy.xml b/controller/app/src/main/res/values-el/strings_networkpolicy.xml new file mode 100644 index 000000000..8827ddb8c --- /dev/null +++ b/controller/app/src/main/res/values-el/strings_networkpolicy.xml @@ -0,0 +1,12 @@ + + + + Χρησιμοποιείτε δεδομένα κινητής + Αυτή η λήψη χρησιμοποιεί δεδομένα κινητής. Μπορεί να έχει κόστος ή να εξαντλήσει το μηνιαίο σας πακέτο. Θέλετε να συνεχίσετε; + Συνέχεια με δεδομένα + Όχι τώρα + Χωρίς δίκτυο. Συνδεθείτε σε Wi-Fi για λήψη. + Μεταβήκατε σε δεδομένα κινητής + Οι νέες λήψεις χρησιμοποιούν τώρα δεδομένα κινητής. Αυτό μπορεί να έχει κόστος ή να εξαντλήσει το μηνιαίο σας πακέτο. + Ειδοποιήσεις κόστους δεδομένων + diff --git a/controller/app/src/main/res/values-es/strings_networkpolicy.xml b/controller/app/src/main/res/values-es/strings_networkpolicy.xml new file mode 100644 index 000000000..7b5d790c0 --- /dev/null +++ b/controller/app/src/main/res/values-es/strings_networkpolicy.xml @@ -0,0 +1,12 @@ + + + + Estás usando datos móviles + Esta descarga usa datos móviles. Puede tener costo o agotar tu plan mensual. ¿Deseas continuar? + Continuar con datos + Ahora no + Sin red. Conéctate a Wi-Fi para descargar. + Cambiaste a datos móviles + Las nuevas descargas ahora usan datos móviles. Esto puede tener costo o agotar tu plan mensual. + Alertas de costo de datos + diff --git a/controller/app/src/main/res/values-fa/strings_networkpolicy.xml b/controller/app/src/main/res/values-fa/strings_networkpolicy.xml new file mode 100644 index 000000000..23f892f95 --- /dev/null +++ b/controller/app/src/main/res/values-fa/strings_networkpolicy.xml @@ -0,0 +1,12 @@ + + + + شما در حال استفاده از داده تلفن همراه هستید + این دانلود از داده تلفن همراه استفاده می‌کند. ممکن است هزینه داشته باشد یا بسته ماهانه شما را تمام کند. آیا می‌خواهید ادامه دهید؟ + ادامه با داده تلفن همراه + الان نه + شبکه‌ای وجود ندارد. برای دانلود به Wi-Fi متصل شوید. + به داده تلفن همراه تغییر دادید + دانلودهای جدید اکنون از داده تلفن همراه استفاده می‌کنند. این ممکن است هزینه داشته باشد یا بسته ماهانه شما را تمام کند. + هشدارهای هزینه داده + diff --git a/controller/app/src/main/res/values-fr/strings_networkpolicy.xml b/controller/app/src/main/res/values-fr/strings_networkpolicy.xml new file mode 100644 index 000000000..2a816ade2 --- /dev/null +++ b/controller/app/src/main/res/values-fr/strings_networkpolicy.xml @@ -0,0 +1,12 @@ + + + + Vous utilisez les données mobiles + Ce téléchargement utilise les données mobiles. Il peut coûter cher ou épuiser votre forfait mensuel. Voulez-vous continuer ? + Continuer avec les données + Pas maintenant + Aucun réseau. Connectez-vous au Wi-Fi pour télécharger. + Vous êtes passé aux données mobiles + Les nouveaux téléchargements utilisent désormais les données mobiles. Cela peut coûter cher ou épuiser votre forfait mensuel. + Alertes de coût des données + diff --git a/controller/app/src/main/res/values-gu/strings_networkpolicy.xml b/controller/app/src/main/res/values-gu/strings_networkpolicy.xml new file mode 100644 index 000000000..2b692c002 --- /dev/null +++ b/controller/app/src/main/res/values-gu/strings_networkpolicy.xml @@ -0,0 +1,12 @@ + + + + તમે મોબાઇલ ડેટા વાપરી રહ્યા છો + આ ડાઉનલોડ મોબાઇલ ડેટા વાપરે છે. તેનાથી ખર્ચ થઈ શકે અથવા તમારો માસિક ડેટા ખતમ થઈ શકે. શું તમે ચાલુ રાખવા માંગો છો? + ડેટા પર ચાલુ રાખો + હમણાં નહીં + કોઈ નેટવર્ક નથી. ડાઉનલોડ કરવા માટે Wi-Fi સાથે કનેક્ટ કરો. + તમે મોબાઇલ ડેટા પર સ્વિચ કર્યું + નવા ડાઉનલોડ હવે મોબાઇલ ડેટા વાપરે છે. તેનાથી ખર્ચ થઈ શકે અથવા તમારો માસિક ડેટા ખતમ થઈ શકે. + ડેટા ખર્ચ ચેતવણીઓ + diff --git a/controller/app/src/main/res/values-hi/strings_networkpolicy.xml b/controller/app/src/main/res/values-hi/strings_networkpolicy.xml new file mode 100644 index 000000000..842be643f --- /dev/null +++ b/controller/app/src/main/res/values-hi/strings_networkpolicy.xml @@ -0,0 +1,12 @@ + + + + आप मोबाइल डेटा का उपयोग कर रहे हैं + यह डाउनलोड मोबाइल डेटा का उपयोग करता है. इससे शुल्क लग सकता है या आपका मासिक डेटा खत्म हो सकता है. क्या आप जारी रखना चाहते हैं? + डेटा पर जारी रखें + अभी नहीं + कोई नेटवर्क नहीं. डाउनलोड करने के लिए Wi-Fi से कनेक्ट करें. + आप मोबाइल डेटा पर स्विच हो गए + नए डाउनलोड अब मोबाइल डेटा का उपयोग करते हैं. इससे शुल्क लग सकता है या आपका मासिक डेटा खत्म हो सकता है. + डेटा लागत अलर्ट + diff --git a/controller/app/src/main/res/values-hu/strings_networkpolicy.xml b/controller/app/src/main/res/values-hu/strings_networkpolicy.xml new file mode 100644 index 000000000..d00a79caa --- /dev/null +++ b/controller/app/src/main/res/values-hu/strings_networkpolicy.xml @@ -0,0 +1,12 @@ + + + + Mobiladatot használ + Ez a letöltés mobiladatot használ. Ez költséggel járhat, vagy elfogyaszthatja a havi keretét. Folytatja? + Folytatás mobiladaton + Most nem + Nincs hálózat. Csatlakozzon Wi-Fi-hez a letöltéshez. + Átváltott mobiladatra + Az új letöltések most mobiladatot használnak. Ez költséggel járhat, vagy elfogyaszthatja a havi keretét. + Adatköltség-figyelmeztetések + diff --git a/controller/app/src/main/res/values-in/strings_networkpolicy.xml b/controller/app/src/main/res/values-in/strings_networkpolicy.xml new file mode 100644 index 000000000..4956360ea --- /dev/null +++ b/controller/app/src/main/res/values-in/strings_networkpolicy.xml @@ -0,0 +1,12 @@ + + + + Anda menggunakan data seluler + Unduhan ini menggunakan data seluler. Ini dapat menimbulkan biaya atau menghabiskan kuota bulanan Anda. Apakah Anda ingin melanjutkan? + Lanjutkan dengan data + Nanti saja + Tidak ada jaringan. Sambungkan ke Wi-Fi untuk mengunduh. + Anda beralih ke data seluler + Unduhan baru kini menggunakan data seluler. Ini dapat menimbulkan biaya atau menghabiskan kuota bulanan Anda. + Peringatan biaya data + diff --git a/controller/app/src/main/res/values-it/strings_networkpolicy.xml b/controller/app/src/main/res/values-it/strings_networkpolicy.xml new file mode 100644 index 000000000..865401da4 --- /dev/null +++ b/controller/app/src/main/res/values-it/strings_networkpolicy.xml @@ -0,0 +1,12 @@ + + + + Stai usando i dati mobili + Questo download usa i dati mobili. Può comportare costi o esaurire il tuo piano mensile. Vuoi continuare? + Continua con i dati + Non ora + Nessuna rete. Connettiti al Wi-Fi per scaricare. + Sei passato ai dati mobili + I nuovi download ora usano i dati mobili. Questo può comportare costi o esaurire il tuo piano mensile. + Avvisi sui costi dei dati + diff --git a/controller/app/src/main/res/values-ja/strings_networkpolicy.xml b/controller/app/src/main/res/values-ja/strings_networkpolicy.xml new file mode 100644 index 000000000..f528cf56c --- /dev/null +++ b/controller/app/src/main/res/values-ja/strings_networkpolicy.xml @@ -0,0 +1,12 @@ + + + + モバイルデータを使用しています + このダウンロードはモバイルデータを使用します。料金が発生したり、月間データを使い切ったりする可能性があります。続行しますか? + モバイルデータで続行 + 今はしない + ネットワークがありません。ダウンロードするには Wi-Fi に接続してください。 + モバイルデータに切り替わりました + 新しいダウンロードはモバイルデータを使用します。料金が発生したり、月間データを使い切ったりする可能性があります。 + データ料金の通知 + diff --git a/controller/app/src/main/res/values-ko/strings_networkpolicy.xml b/controller/app/src/main/res/values-ko/strings_networkpolicy.xml new file mode 100644 index 000000000..4d9dfc327 --- /dev/null +++ b/controller/app/src/main/res/values-ko/strings_networkpolicy.xml @@ -0,0 +1,12 @@ + + + + 모바일 데이터를 사용 중입니다 + 이 다운로드는 모바일 데이터를 사용합니다. 요금이 발생하거나 월 데이터를 모두 사용할 수 있습니다. 계속하시겠습니까? + 데이터로 계속 + 나중에 + 네트워크가 없습니다. 다운로드하려면 Wi-Fi에 연결하세요. + 모바일 데이터로 전환되었습니다 + 새 다운로드가 이제 모바일 데이터를 사용합니다. 요금이 발생하거나 월 데이터를 모두 사용할 수 있습니다. + 데이터 요금 알림 + diff --git a/controller/app/src/main/res/values-lt/strings_networkpolicy.xml b/controller/app/src/main/res/values-lt/strings_networkpolicy.xml new file mode 100644 index 000000000..22f137617 --- /dev/null +++ b/controller/app/src/main/res/values-lt/strings_networkpolicy.xml @@ -0,0 +1,12 @@ + + + + Naudojate mobiliuosius duomenis + Šis atsisiuntimas naudoja mobiliuosius duomenis. Tai gali kainuoti arba išnaudoti mėnesio planą. Ar norite tęsti? + Tęsti su mobiliaisiais duomenimis + Ne dabar + Nėra tinklo. Prisijunkite prie Wi-Fi, kad atsisiųstumėte. + Perjungėte į mobiliuosius duomenis + Nauji atsisiuntimai dabar naudoja mobiliuosius duomenis. Tai gali kainuoti arba išnaudoti mėnesio planą. + Duomenų sąnaudų įspėjimai + diff --git a/controller/app/src/main/res/values-nl/strings_networkpolicy.xml b/controller/app/src/main/res/values-nl/strings_networkpolicy.xml new file mode 100644 index 000000000..1b713b9fd --- /dev/null +++ b/controller/app/src/main/res/values-nl/strings_networkpolicy.xml @@ -0,0 +1,12 @@ + + + + Je gebruikt mobiele data + Deze download gebruikt mobiele data. Dit kan kosten opleveren of je maandbundel opmaken. Wil je doorgaan? + Doorgaan met mobiele data + Niet nu + Geen netwerk. Maak verbinding met wifi om te downloaden. + Je bent overgeschakeld naar mobiele data + Nieuwe downloads gebruiken nu mobiele data. Dit kan kosten opleveren of je maandbundel opmaken. + Waarschuwingen datakosten + diff --git a/controller/app/src/main/res/values-no/strings_networkpolicy.xml b/controller/app/src/main/res/values-no/strings_networkpolicy.xml new file mode 100644 index 000000000..721442fa8 --- /dev/null +++ b/controller/app/src/main/res/values-no/strings_networkpolicy.xml @@ -0,0 +1,12 @@ + + + + Du bruker mobildata + Denne nedlastingen bruker mobildata. Det kan koste penger eller bruke opp den månedlige datapakken. Vil du fortsette? + Fortsett med mobildata + Ikke nå + Ingen nettverk. Koble til Wi-Fi for å laste ned. + Du byttet til mobildata + Nye nedlastinger bruker nå mobildata. Det kan koste penger eller bruke opp den månedlige datapakken. + Varsler om datakostnad + diff --git a/controller/app/src/main/res/values-pl/strings_networkpolicy.xml b/controller/app/src/main/res/values-pl/strings_networkpolicy.xml new file mode 100644 index 000000000..60fdeec8a --- /dev/null +++ b/controller/app/src/main/res/values-pl/strings_networkpolicy.xml @@ -0,0 +1,12 @@ + + + + Używasz danych komórkowych + To pobieranie używa danych komórkowych. Może wiązać się z kosztami lub wyczerpać miesięczny pakiet. Czy chcesz kontynuować? + Kontynuuj na danych + Nie teraz + Brak sieci. Połącz się z Wi-Fi, aby pobrać. + Przełączono na dane komórkowe + Nowe pobierania używają teraz danych komórkowych. Może to wiązać się z kosztami lub wyczerpać miesięczny pakiet. + Alerty o kosztach danych + diff --git a/controller/app/src/main/res/values-pt/strings_networkpolicy.xml b/controller/app/src/main/res/values-pt/strings_networkpolicy.xml new file mode 100644 index 000000000..b9fac679e --- /dev/null +++ b/controller/app/src/main/res/values-pt/strings_networkpolicy.xml @@ -0,0 +1,12 @@ + + + + Você está usando dados móveis + Este download usa dados móveis. Pode gerar custos ou esgotar seu plano mensal. Deseja continuar? + Continuar com dados + Agora não + Sem rede. Conecte-se ao Wi-Fi para baixar. + Você mudou para dados móveis + Os novos downloads agora usam dados móveis. Isso pode gerar custos ou esgotar seu plano mensal. + Alertas de custo de dados + diff --git a/controller/app/src/main/res/values-ro/strings_networkpolicy.xml b/controller/app/src/main/res/values-ro/strings_networkpolicy.xml new file mode 100644 index 000000000..c9ec3c08a --- /dev/null +++ b/controller/app/src/main/res/values-ro/strings_networkpolicy.xml @@ -0,0 +1,12 @@ + + + + Folosești date mobile + Această descărcare folosește date mobile. Poate genera costuri sau îți poate epuiza planul lunar. Vrei să continui? + Continuă pe date mobile + Nu acum + Fără rețea. Conectează-te la Wi-Fi pentru a descărca. + Ai trecut la date mobile + Descărcările noi folosesc acum date mobile. Acest lucru poate genera costuri sau îți poate epuiza planul lunar. + Alerte privind costul datelor + diff --git a/controller/app/src/main/res/values-ru-rRU/strings_networkpolicy.xml b/controller/app/src/main/res/values-ru-rRU/strings_networkpolicy.xml new file mode 100644 index 000000000..cb3002e76 --- /dev/null +++ b/controller/app/src/main/res/values-ru-rRU/strings_networkpolicy.xml @@ -0,0 +1,12 @@ + + + + Вы используете мобильные данные + Эта загрузка использует мобильные данные. Это может повлечь расходы или исчерпать месячный пакет. Продолжить? + Продолжить на мобильных данных + Не сейчас + Нет сети. Подключитесь к Wi-Fi для загрузки. + Вы переключились на мобильные данные + Новые загрузки теперь используют мобильные данные. Это может повлечь расходы или исчерпать месячный пакет. + Оповещения о расходе данных + diff --git a/controller/app/src/main/res/values-sk/strings_networkpolicy.xml b/controller/app/src/main/res/values-sk/strings_networkpolicy.xml new file mode 100644 index 000000000..282d50359 --- /dev/null +++ b/controller/app/src/main/res/values-sk/strings_networkpolicy.xml @@ -0,0 +1,12 @@ + + + + Používate mobilné dáta + Toto sťahovanie používa mobilné dáta. Môže byť spoplatnené alebo vyčerpať váš mesačný limit. Chcete pokračovať? + Pokračovať na mobilných dátach + Teraz nie + Žiadna sieť. Pripojte sa k Wi-Fi na stiahnutie. + Prepli ste sa na mobilné dáta + Nové sťahovania teraz používajú mobilné dáta. Môže byť spoplatnené alebo vyčerpať váš mesačný limit. + Upozornenia na náklady za dáta + diff --git a/controller/app/src/main/res/values-sr/strings_networkpolicy.xml b/controller/app/src/main/res/values-sr/strings_networkpolicy.xml new file mode 100644 index 000000000..7a4e8564f --- /dev/null +++ b/controller/app/src/main/res/values-sr/strings_networkpolicy.xml @@ -0,0 +1,12 @@ + + + + Користите мобилне податке + Ово преузимање користи мобилне податке. Може да има трошак или да потроши ваш месечни план. Желите ли да наставите? + Настави на мобилним подацима + Не сада + Нема мреже. Повежите се на Wi-Fi да бисте преузели. + Прешли сте на мобилне податке + Нова преузимања сада користе мобилне податке. Ово може да има трошак или да потроши ваш месечни план. + Обавештења о трошку података + diff --git a/controller/app/src/main/res/values-sw/strings_networkpolicy.xml b/controller/app/src/main/res/values-sw/strings_networkpolicy.xml new file mode 100644 index 000000000..740e138f4 --- /dev/null +++ b/controller/app/src/main/res/values-sw/strings_networkpolicy.xml @@ -0,0 +1,12 @@ + + + + Unatumia data ya simu + Upakuaji huu unatumia data ya simu. Inaweza kugharimu au kumaliza kifurushi chako cha mwezi. Ungependa kuendelea? + Endelea kwa data + Si sasa + Hakuna mtandao. Unganisha kwa Wi-Fi ili kupakua. + Umebadili hadi data ya simu + Vipakuliwa vipya sasa vinatumia data ya simu. Hii inaweza kugharimu au kumaliza kifurushi chako cha mwezi. + Arifa za gharama ya data + diff --git a/controller/app/src/main/res/values-ta/strings_networkpolicy.xml b/controller/app/src/main/res/values-ta/strings_networkpolicy.xml new file mode 100644 index 000000000..25382c5a1 --- /dev/null +++ b/controller/app/src/main/res/values-ta/strings_networkpolicy.xml @@ -0,0 +1,12 @@ + + + + நீங்கள் மொபைல் டேட்டாவைப் பயன்படுத்துகிறீர்கள் + இந்தப் பதிவிறக்கம் மொபைல் டேட்டாவைப் பயன்படுத்துகிறது. இதனால் கட்டணம் ஏற்படலாம் அல்லது உங்கள் மாதத் திட்டம் தீர்ந்துபோகலாம். தொடர விரும்புகிறீர்களா? + டேட்டாவில் தொடரவும் + இப்போது வேண்டாம் + நெட்வொர்க் இல்லை. பதிவிறக்க Wi-Fi உடன் இணைக்கவும். + நீங்கள் மொபைல் டேட்டாவிற்கு மாறினீர்கள் + புதிய பதிவிறக்கங்கள் இப்போது மொபைல் டேட்டாவைப் பயன்படுத்துகின்றன. இதனால் கட்டணம் ஏற்படலாம் அல்லது உங்கள் மாதத் திட்டம் தீர்ந்துபோகலாம். + டேட்டா செலவு எச்சரிக்கைகள் + diff --git a/controller/app/src/main/res/values-tr/strings_networkpolicy.xml b/controller/app/src/main/res/values-tr/strings_networkpolicy.xml new file mode 100644 index 000000000..c8f2ef75e --- /dev/null +++ b/controller/app/src/main/res/values-tr/strings_networkpolicy.xml @@ -0,0 +1,12 @@ + + + + Mobil veri kullanıyorsunuz + Bu indirme mobil veri kullanıyor. Ücretlendirilebilir veya aylık paketinizi tüketebilir. Devam etmek istiyor musunuz? + Mobil veriyle devam et + Şimdi değil + Ağ yok. İndirmek için Wi-Fi\'ye bağlanın. + Mobil veriye geçtiniz + Yeni indirmeler artık mobil veri kullanıyor. Bu, ücretlendirilebilir veya aylık paketinizi tüketebilir. + Veri maliyeti uyarıları + diff --git a/controller/app/src/main/res/values-uk/strings_networkpolicy.xml b/controller/app/src/main/res/values-uk/strings_networkpolicy.xml new file mode 100644 index 000000000..27dcb008e --- /dev/null +++ b/controller/app/src/main/res/values-uk/strings_networkpolicy.xml @@ -0,0 +1,12 @@ + + + + Ви використовуєте мобільні дані + Це завантаження використовує мобільні дані. Це може призвести до витрат або вичерпати місячний пакет. Продовжити? + Продовжити на мобільних даних + Не зараз + Немає мережі. Підключіться до Wi-Fi, щоб завантажити. + Ви перейшли на мобільні дані + Нові завантаження тепер використовують мобільні дані. Це може призвести до витрат або вичерпати місячний пакет. + Сповіщення про витрати даних + diff --git a/controller/app/src/main/res/values-vi/strings_networkpolicy.xml b/controller/app/src/main/res/values-vi/strings_networkpolicy.xml new file mode 100644 index 000000000..35367cf1d --- /dev/null +++ b/controller/app/src/main/res/values-vi/strings_networkpolicy.xml @@ -0,0 +1,12 @@ + + + + Bạn đang dùng dữ liệu di động + Bản tải xuống này dùng dữ liệu di động. Có thể phát sinh chi phí hoặc dùng hết gói hàng tháng của bạn. Bạn có muốn tiếp tục không? + Tiếp tục bằng dữ liệu + Để sau + Không có mạng. Kết nối Wi-Fi để tải xuống. + Bạn đã chuyển sang dữ liệu di động + Các bản tải xuống mới hiện dùng dữ liệu di động. Điều này có thể phát sinh chi phí hoặc dùng hết gói hàng tháng của bạn. + Cảnh báo chi phí dữ liệu + diff --git a/controller/app/src/main/res/values-yo/strings_networkpolicy.xml b/controller/app/src/main/res/values-yo/strings_networkpolicy.xml new file mode 100644 index 000000000..11bdb0461 --- /dev/null +++ b/controller/app/src/main/res/values-yo/strings_networkpolicy.xml @@ -0,0 +1,12 @@ + + + + O n lo data alagbeka + Igbasilẹ yii n lo data alagbeka. O le san owo tabi lo package oṣooṣu rẹ tan. Ṣe o fẹ tẹsiwaju? + Tẹsiwaju pẹlu data + Kii ṣe bayii + Ko si nẹtiwọọki. Sopọ si Wi-Fi lati gbaa wọle. + O ti yipada si data alagbeka + Awọn igbasilẹ tuntun n lo data alagbeka bayii. Eyi le san owo tabi lo package oṣooṣu rẹ tan. + Awọn ikilọ iye owo data + diff --git a/controller/app/src/main/res/values-zh-rCN/strings_networkpolicy.xml b/controller/app/src/main/res/values-zh-rCN/strings_networkpolicy.xml new file mode 100644 index 000000000..92acaec2c --- /dev/null +++ b/controller/app/src/main/res/values-zh-rCN/strings_networkpolicy.xml @@ -0,0 +1,12 @@ + + + + 您正在使用移动数据 + 此下载会使用移动数据。可能产生费用或用尽您的每月流量。是否继续? + 使用数据继续 + 暂不 + 无网络。请连接 Wi-Fi 以下载。 + 您已切换到移动数据 + 新的下载现在会使用移动数据。这可能产生费用或用尽您的每月流量。 + 数据费用提醒 + diff --git a/controller/app/src/main/res/values/strings_networkpolicy.xml b/controller/app/src/main/res/values/strings_networkpolicy.xml new file mode 100644 index 000000000..a5e3bc840 --- /dev/null +++ b/controller/app/src/main/res/values/strings_networkpolicy.xml @@ -0,0 +1,12 @@ + + + + You are on mobile data + This download uses mobile data. It can add cost or use up your monthly data. Do you want to continue? + Continue on data + Not now + No network. Connect to Wi-Fi to download. + You switched to mobile data + New downloads now use mobile data. This can add cost or use up your monthly data. + Data cost alerts + diff --git a/controller/app/src/main/res/values/strings_untranslated.xml b/controller/app/src/main/res/values/strings_untranslated.xml index 8ef921124..0470a548a 100644 --- a/controller/app/src/main/res/values/strings_untranslated.xml +++ b/controller/app/src/main/res/values/strings_untranslated.xml @@ -10,15 +10,7 @@ --> - - You are on mobile data - This download uses mobile data. It can add cost or use up your monthly data. Do you want to continue? - Continue on data - Not now - No network. Connect to Wi-Fi to download. - You switched to mobile data - New downloads now use mobile data. This can add cost or use up your monthly data. - Data cost alerts - + diff --git a/controller/docs/ADR-395-network-cost-consent.md b/controller/docs/ADR-395-network-cost-consent.md index 4d19b1889..8ba7117b7 100644 --- a/controller/docs/ADR-395-network-cost-consent.md +++ b/controller/docs/ADR-395-network-cost-consent.md @@ -167,11 +167,11 @@ Landed as a compiling, tested starting point for the implementer: - Presentation: `NetworkPolicyGate` (+ `BrandDialog` consent), `MeteredNetworkObserver`. - Wiring: observer started in `IIABApplication`; gate wired at the ZIM commit point (`ZimConfirmFragment`). -- Strings parked in `strings_untranslated.xml` (WIP; translate to 33 locales and - move to a feature file before merge -- l10n policy). +- Strings translated to all 33 locales (machine-generated, pending human review) + in `values*/strings_networkpolicy.xml`; `strings_untranslated.xml` is clear. -Remaining to finish the contract: the other four seams, the two-way fold of the -existing `hasInternet` readers into the classifier, and the l10n migration. +Remaining to finish the contract: the other four seams and the two-way fold of the +existing `hasInternet` readers into the classifier. (l10n is done pending review.) ## 6. Device evidence appendix (dark surfaces flattened) @@ -227,8 +227,9 @@ Run before shipping the full contract: - Positive: one owner for cost policy; reuses the existing change callback; reduces (does not add) `hasInternet` duplication; empirically grounded rule. -- Cost: new UI strings need 33-locale translation before merge; five seams to - wire; device verification per the protocol. +- Cost: four seams still to wire; device verification per the protocol. (The UI + strings are already translated to 33 locales, machine-generated, pending human + review.) - Deployment detail: the proactive alert posts a notification, so on Android 13+ it needs the POST_NOTIFICATIONS runtime permission. The observer swallows the SecurityException when it is not granted, so the start gate (the primary cost From 0362edee0b561751b28ea70f04eb171cceadb2c6 Mon Sep 17 00:00:00 2001 From: Luis Guzman Date: Sun, 6 Sep 2026 23:11:03 -0600 Subject: [PATCH 04/18] K2GO-395 docs(networkpolicy): record code-review second-pass design questions The two-pass review found that gating only the UI commit point is not fully defensive: the wishlist is a durable queue drained by an ungated background pass, so banked items (incl. the wizard-bank path) download on any network without consent, and wrapping startZimDownload() also discards the cart when offline. ADR sec.10 records these open design questions to resolve before wiring the rest. --- .../docs/ADR-395-network-cost-consent.md | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/controller/docs/ADR-395-network-cost-consent.md b/controller/docs/ADR-395-network-cost-consent.md index 8ba7117b7..d4d01bc95 100644 --- a/controller/docs/ADR-395-network-cost-consent.md +++ b/controller/docs/ADR-395-network-cost-consent.md @@ -248,3 +248,36 @@ Run before shipping the full contract: not as a global rule. - **Block at the socket / bind the process to Wi-Fi.** Rejected: cannot cover the in-proot server's egress, and would fight `WifiNetworkBinder` (LAN sync). + +## 10. Open design questions (from the code-review second pass) + +The reference wiring gates the immediate commit point. The two-pass review found +that this alone is not fully defensive, because the wishlist is a durable queue +drained by an UNGATED background pass. These must be resolved before the feature +is complete: + +1. **The commit point both banks and drains.** `SetupLibraryActivity.startZimDownload()` + calls `ZimWishlist.add(cart)` (the durable queue) and then `ZimProvisioner.drain()`. + Wrapping the whole method means a BLOCKED (offline) or declined start also skips + the banking, so the selection is lost instead of queued. Offline is not a cost + decision -- it should still bank for a later drain. Fix direction: bank + unconditionally; gate only the drain. + +2. **Banked items drain ungated.** `ZimProvisioner.drain` runs every ~2 s from + Home/SetupProgress and starts the real download with no gate (by design, to + avoid re-prompting). So any banked item downloads on whatever network is + active. The wizard-bank path (`zimWizardConfirm`, `banks == true`) banks + without ever passing the gate, so those ZIMs can download on metered data with + no consent. Gating the UI commit point does not cover them. + +3. **Consequence:** to be truly defensive the provisioner drain must be + consent-aware -- HOLD a banked item on metered-without-consent instead of + downloading, and surface the consent prompt when an Activity is next + foreground (the drain itself has no UI). The proactive alert only informs; it + does not hold the transfer. This is the real depth of the feature and should + be designed before wiring the remaining seams, not after. + +Decision needed (consult, do not default): keep the commit-point gate as a first +layer and add drain-level consent enforcement, or move enforcement entirely into +the provisioner/session start. The reference ZIM seam is left as-is pending this +decision so the trade-off is visible, not silently patched. From f288b7590c35ead3a9e46b4de0c22c0dcfb92df8 Mon Sep 17 00:00:00 2001 From: Luis Guzman Date: Wed, 16 Sep 2026 12:40:15 -0600 Subject: [PATCH 05/18] K2GO-395 feat(networkpolicy): enforce cost gate in ContentAdmission; bank-before-gate for ZIM Resolves ADR-395 sec.10. Gating only the UI commit point was not defensive: the wishlist is a durable queue drained by an ungated background pass, so banked orders (incl. the wizard-bank path) could download on metered data with no consent. - New NetworkCostAdmission (networkpolicy/data): the single classify+consent+policy decision, used by both the UI gate and the headless admission. - ContentAdmission.canStart now defers on metered-without-consent, so the ZIM, Books and Kolibri drains all HOLD a banked order -- one choke, three streams, no per-provisioner edit, no new persistent state (a held order stays banked). - startZimDownload banks the wishlist BEFORE the gate and gates only the drain, so a declined/offline order is queued, not lost; ZimConfirmFragment calls it directly. - MeteredNetworkObserver.start is now truly idempotent (started guard). Fixes the two-pass review findings (cart loss on offline/decline; false idempotent claim). Build + networkpolicy unit tests green. --- .../data/NetworkCostAdmission.java | 42 +++++++++++++++++ .../presentation/MeteredNetworkObserver.java | 3 ++ .../presentation/NetworkPolicyGate.java | 36 ++++++--------- .../k2go/redesign/SetupLibraryActivity.java | 11 ++++- .../k2go/redesign/ZimConfirmFragment.java | 12 ++--- .../k2go/system/data/ContentAdmission.java | 10 ++++ .../docs/ADR-395-network-cost-consent.md | 46 +++++++++++++++---- 7 files changed, 118 insertions(+), 42 deletions(-) create mode 100644 controller/app/src/main/java/org/appdevforall/k2go/networkpolicy/data/NetworkCostAdmission.java diff --git a/controller/app/src/main/java/org/appdevforall/k2go/networkpolicy/data/NetworkCostAdmission.java b/controller/app/src/main/java/org/appdevforall/k2go/networkpolicy/data/NetworkCostAdmission.java new file mode 100644 index 000000000..a04ecc9a4 --- /dev/null +++ b/controller/app/src/main/java/org/appdevforall/k2go/networkpolicy/data/NetworkCostAdmission.java @@ -0,0 +1,42 @@ +package org.appdevforall.k2go.networkpolicy.data; + +import android.content.Context; + +import androidx.annotation.NonNull; + +import org.appdevforall.k2go.networkpolicy.domain.NetworkPolicy; +import org.appdevforall.k2go.networkpolicy.domain.NetworkPolicyDecision; + +/** + * The one headless "may a heavy transfer start now, cost-wise?" decision (ADR-395). + * + *

It is the single source of the classify + consent + policy glue, used by both + * the UI gate ({@code NetworkPolicyGate}, which needs the full decision to choose + * dialog vs snackbar) and the content-stream admission ({@code ContentAdmission}, + * which needs only the boolean). Data-layer, so the system-side admission can call + * it without depending on presentation. + */ +public final class NetworkCostAdmission { + + private NetworkCostAdmission() {} + + private static final NetworkPolicy POLICY = new NetworkPolicy(); + + /** The full decision for the active default network and the current session consent. */ + @NonNull + public static NetworkPolicyDecision decideNow(@NonNull Context ctx) { + return POLICY.decideHeavyStart( + AndroidNetworkClassifier.classify(ctx), + SessionMeteredConsentStore.get().isGranted()); + } + + /** + * True when a heavy transfer may start now on cost grounds (unmetered, or the + * user consented this session). False means HOLD: leave the order banked, a + * later pass takes it once the network is free or consent is given -- the same + * "deferred is not a failure" contract the other admission checks use. + */ + public static boolean allowsHeavyStartNow(@NonNull Context ctx) { + return decideNow(ctx) == NetworkPolicyDecision.ALLOW; + } +} diff --git a/controller/app/src/main/java/org/appdevforall/k2go/networkpolicy/presentation/MeteredNetworkObserver.java b/controller/app/src/main/java/org/appdevforall/k2go/networkpolicy/presentation/MeteredNetworkObserver.java index 50d1f31c5..fa760f12b 100644 --- a/controller/app/src/main/java/org/appdevforall/k2go/networkpolicy/presentation/MeteredNetworkObserver.java +++ b/controller/app/src/main/java/org/appdevforall/k2go/networkpolicy/presentation/MeteredNetworkObserver.java @@ -42,9 +42,12 @@ public final class MeteredNetworkObserver { private MeteredNetworkObserver() {} private static NetworkClass last = null; + private static boolean started = false; /** Idempotent; call once from Application.onCreate on the main thread. */ public static void start(@NonNull Application app) { + if (started) return; // guard: a second call must not add a second observeForever + started = true; ensureChannel(app); last = AndroidNetworkClassifier.classify(app); // observeForever keeps NetworkStateLiveData active for the process lifetime, diff --git a/controller/app/src/main/java/org/appdevforall/k2go/networkpolicy/presentation/NetworkPolicyGate.java b/controller/app/src/main/java/org/appdevforall/k2go/networkpolicy/presentation/NetworkPolicyGate.java index 76f6eda7b..334ed0bee 100644 --- a/controller/app/src/main/java/org/appdevforall/k2go/networkpolicy/presentation/NetworkPolicyGate.java +++ b/controller/app/src/main/java/org/appdevforall/k2go/networkpolicy/presentation/NetworkPolicyGate.java @@ -6,43 +6,35 @@ import androidx.annotation.NonNull; import org.appdevforall.k2go.R; -import org.appdevforall.k2go.networkpolicy.data.AndroidNetworkClassifier; +import org.appdevforall.k2go.networkpolicy.data.NetworkCostAdmission; import org.appdevforall.k2go.networkpolicy.data.SessionMeteredConsentStore; -import org.appdevforall.k2go.networkpolicy.domain.MeteredConsentStore; -import org.appdevforall.k2go.networkpolicy.domain.NetworkClass; -import org.appdevforall.k2go.networkpolicy.domain.NetworkPolicy; -import org.appdevforall.k2go.networkpolicy.domain.NetworkPolicyDecision; import org.appdevforall.k2go.ui.dialog.BrandDialog; import org.appdevforall.k2go.util.Snackbars; /** - * The single consult point before any heavy download starts (ADR-395). A caller - * wraps its existing start call: + * The user-facing PROMPT for a costed start (ADR-395). A commit point (a Download + * button) wraps its start so the user is asked before spending metered data: * *

NetworkPolicyGate.guardHeavyStart(activity, () -> a.startZimDownload());
* *

Stateless, like {@code OpReturnNavigator}: it owns no "is metered" flag. It - * reads the live class off {@link AndroidNetworkClassifier} and the session - * consent off {@link SessionMeteredConsentStore}, applies the pure - * {@link NetworkPolicy}, and either proceeds, asks, or blocks. + * reads the decision from {@link NetworkCostAdmission} (the one classify + consent + * + policy source) and either proceeds, asks, or reports offline. * - *

It gates the START only. It does NOT control a transfer already in flight -- - * the content bytes are pulled by the in-proot server over the device default - * network, which Android gives the app no handle to throttle (ADR-395). Callers - * put this at the user's commit point (the Download button), never on the - * background drain that re-hands an already-authorized wishlist. + *

This gate is only the PROMPT. The actual hold is enforced headless in + * {@code ContentAdmission} (via {@link NetworkCostAdmission}), which every content + * drain already consults -- so a banked order never starts on metered data without + * consent even if it was never routed through a commit point (the wizard-bank path, + * a background re-drain). Banking must therefore happen BEFORE this gate: an order + * declined or offline here stays queued and drains once the network is free or + * consent is given, rather than being lost (ADR-395 sec.10). */ public final class NetworkPolicyGate { private NetworkPolicyGate() {} - private static final NetworkPolicy POLICY = new NetworkPolicy(); - public static void guardHeavyStart(@NonNull Activity activity, @NonNull Runnable onProceed) { - MeteredConsentStore consent = SessionMeteredConsentStore.get(); - NetworkClass net = AndroidNetworkClassifier.classify(activity); - NetworkPolicyDecision decision = POLICY.decideHeavyStart(net, consent.isGranted()); - switch (decision) { + switch (NetworkCostAdmission.decideNow(activity)) { case ALLOW: onProceed.run(); return; @@ -51,7 +43,7 @@ public static void guardHeavyStart(@NonNull Activity activity, @NonNull Runnable .setTitle(R.string.k2go_netpolicy_metered_title) .setMessage(R.string.k2go_netpolicy_metered_msg) .setPositive(R.string.k2go_netpolicy_continue, () -> { - consent.grant(); + SessionMeteredConsentStore.get().grant(); onProceed.run(); }) .setNegative(R.string.k2go_netpolicy_not_now, null) diff --git a/controller/app/src/main/java/org/appdevforall/k2go/redesign/SetupLibraryActivity.java b/controller/app/src/main/java/org/appdevforall/k2go/redesign/SetupLibraryActivity.java index 25300cc72..5d7a896d6 100644 --- a/controller/app/src/main/java/org/appdevforall/k2go/redesign/SetupLibraryActivity.java +++ b/controller/app/src/main/java/org/appdevforall/k2go/redesign/SetupLibraryActivity.java @@ -304,8 +304,15 @@ public void openZimConfirm() { public void startZimDownload() { ZimWishlist.add(this, selection().zimCart()); selection().zimCart().clear(); // handed over; keeping it would re-offer the same picks - ZimProvisioner.drain(this); // starts now if the line is free, banks it if not - startActivity(new Intent(this, SetupProgressActivity.class)); + // K2GO-395 (ADR-395 sec.10): the order is banked above, unconditionally, so it is never lost. + // The gate wraps only the drain + navigation: it prompts on a metered network and, on consent + // (or Wi-Fi), drains and opens progress; declined or offline the order stays queued and a later + // pass drains it. The drain is also held headless by ContentAdmission, so a banked order never + // starts on metered data without consent even via the wizard-bank path or a background re-drain. + org.appdevforall.k2go.networkpolicy.presentation.NetworkPolicyGate.guardHeavyStart(this, () -> { + ZimProvisioner.drain(this); // starts now if the line is free, banks it if not + startActivity(new Intent(this, SetupProgressActivity.class)); + }); } /** ADFA-4853: the wizard's "Continue" — install the system now; content (Books/ZIM) is banked diff --git a/controller/app/src/main/java/org/appdevforall/k2go/redesign/ZimConfirmFragment.java b/controller/app/src/main/java/org/appdevforall/k2go/redesign/ZimConfirmFragment.java index 1d8f6c50e..d055e452e 100644 --- a/controller/app/src/main/java/org/appdevforall/k2go/redesign/ZimConfirmFragment.java +++ b/controller/app/src/main/java/org/appdevforall/k2go/redesign/ZimConfirmFragment.java @@ -106,14 +106,10 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup c if (!(getActivity() instanceof SetupLibraryActivity)) return; SetupLibraryActivity a = (SetupLibraryActivity) getActivity(); if (banks) a.zimWizardConfirm(); // no box yet: bank it - // ADFA-5074 / ADFA-5333: blocked while a dashboard update runs. - // K2GO-395 (ADR-395): gate a metered start behind explicit consent (cost awareness). - // The gate sits at the user commit point, not on ZimProvisioner.drain (that re-hands an - // already-authorized wishlist every ~2 s and would re-prompt). - else if (!DashboardRebuild.blockedByUpdate(v)) { - org.appdevforall.k2go.networkpolicy.presentation.NetworkPolicyGate - .guardHeavyStart(a, a::startZimDownload); - } + // ADFA-5074 / ADFA-5333: blocked while a dashboard update runs. The K2GO-395 metered-cost + // gate lives inside startZimDownload (bank first, then gate the drain), so the order is + // never lost on decline/offline; see ADR-395 sec.10. + else if (!DashboardRebuild.blockedByUpdate(v)) a.startZimDownload(); }); return root; diff --git a/controller/app/src/main/java/org/appdevforall/k2go/system/data/ContentAdmission.java b/controller/app/src/main/java/org/appdevforall/k2go/system/data/ContentAdmission.java index da134b130..32a0cb052 100644 --- a/controller/app/src/main/java/org/appdevforall/k2go/system/data/ContentAdmission.java +++ b/controller/app/src/main/java/org/appdevforall/k2go/system/data/ContentAdmission.java @@ -15,6 +15,7 @@ import android.util.Log; import org.appdevforall.k2go.install.presentation.ModuleQueueRepository; +import org.appdevforall.k2go.networkpolicy.data.NetworkCostAdmission; import org.appdevforall.k2go.redesign.DashboardRebuildService; import org.appdevforall.k2go.redesign.MapsProvisioner; import org.appdevforall.k2go.system.domain.ContentType; @@ -73,6 +74,15 @@ public static boolean canStart(Context ctx, ContentType stream) { Log.d(TAG, stream.key() + " drain deferred: a content stream still has work to do"); return false; } + // ADR-395: cost gate. A heavy content download must not start on a metered network without + // the user's consent. This is the single headless hold that covers every drain path -- the + // commit point, the wizard-bank path, and every background re-drain -- so a banked order + // waits for Wi-Fi or consent rather than silently spending mobile data. Prompting happens at + // the UI commit point (NetworkPolicyGate); here we only defer. + if (!NetworkCostAdmission.allowsHeavyStartNow(ctx)) { + Log.d(TAG, stream.key() + " drain deferred: metered network without consent"); + return false; + } return true; } } diff --git a/controller/docs/ADR-395-network-cost-consent.md b/controller/docs/ADR-395-network-cost-consent.md index d4d01bc95..533731bda 100644 --- a/controller/docs/ADR-395-network-cost-consent.md +++ b/controller/docs/ADR-395-network-cost-consent.md @@ -163,15 +163,22 @@ Landed as a compiling, tested starting point for the implementer: - Domain, pure JVM, unit-tested: `NetworkClass`, `NetworkPolicy`, `NetworkTransition`, `NetworkPolicyDecision`, `MeteredConsentStore` (`NetworkPolicyTest`, `NetworkClassTest`, `NetworkTransitionTest` -- green). -- Data: `AndroidNetworkClassifier`, `SessionMeteredConsentStore`. -- Presentation: `NetworkPolicyGate` (+ `BrandDialog` consent), `MeteredNetworkObserver`. -- Wiring: observer started in `IIABApplication`; gate wired at the ZIM commit - point (`ZimConfirmFragment`). +- Data: `AndroidNetworkClassifier`, `SessionMeteredConsentStore`, + `NetworkCostAdmission` (the one classify + consent + policy decision source). +- Presentation: `NetworkPolicyGate` (the UI prompt, + `BrandDialog`), + `MeteredNetworkObserver`. +- Enforcement (headless): `ContentAdmission.canStart` defers when + `NetworkCostAdmission.allowsHeavyStartNow` is false, so the ZIM, Books and + Kolibri drains all HOLD a banked order on metered-without-consent (sec.10). +- Wiring: observer started in `IIABApplication`; the ZIM commit point + (`SetupLibraryActivity.startZimDownload`) banks first, then gates the drain, so + a declined/offline order is queued, not lost. - Strings translated to all 33 locales (machine-generated, pending human review) in `values*/strings_networkpolicy.xml`; `strings_untranslated.xml` is clear. -Remaining to finish the contract: the other four seams and the two-way fold of the -existing `hasInternet` readers into the classifier. (l10n is done pending review.) +Remaining to finish the contract: the Books/Kolibri commit-point prompts (their +drains are already held), the Maps seam (re-locate post K2GO-394), the +DownloadManager seam, and the two-`hasInternet` fold. (l10n done pending review.) ## 6. Device evidence appendix (dark surfaces flattened) @@ -277,7 +284,26 @@ is complete: does not hold the transfer. This is the real depth of the feature and should be designed before wiring the remaining seams, not after. -Decision needed (consult, do not default): keep the commit-point gate as a first -layer and add drain-level consent enforcement, or move enforcement entirely into -the provisioner/session start. The reference ZIM seam is left as-is pending this -decision so the trade-off is visible, not silently patched. +### Resolution (implemented) + +Enforcement moved to the single choke every content drain already consults: +`ContentAdmission.canStart` (system/data) -- "the one answer to may a REST content +stream start now". It now also defers when `NetworkCostAdmission.allowsHeavyStartNow` +is false (metered without consent), so ZIM, Books and Kolibri drains all HOLD a +banked order rather than spend mobile data -- the commit point, the wizard-bank +path and every background re-drain, covered in one place, with no per-provisioner +edit and no new persistent state (a held order is simply left banked, the existing +"deferred is not a failure" contract). + +`NetworkCostAdmission` (networkpolicy/data) is the single source of the classify + +consent + policy decision, used both by that headless admission and by the UI gate. + +The prompt stays at the UI commit point (`NetworkPolicyGate`), but banking now +happens BEFORE the gate (`SetupLibraryActivity.startZimDownload` banks, then gates +the drain), so a declined or offline order is queued, not lost. This removes the +commit-point special case rather than adding one. + +Still open as follow-ups: the Books/Kolibri commit-point prompts (their drains are +already held by the ContentAdmission change; only the interactive ask is missing), +the Maps seam (K2GO-394 reworked Maps; re-locate it), DownloadManager (OTA/portal), +and the two-`hasInternet` fold. From af71b309672aaf31a620a5e8308967a0d5fae205 Mon Sep 17 00:00:00 2001 From: Luis Guzman Date: Wed, 16 Sep 2026 12:42:40 -0600 Subject: [PATCH 06/18] K2GO-395 feat(networkpolicy): bank-before-gate prompt at the Books commit point Same shape as startZimDownload: startBooksDownload banks the wishlist first, then gates only the drain + navigation, so a declined/offline order is queued not lost. Its drain was already held by the ContentAdmission cost gate; this adds the interactive prompt. ADR-395 updated (Kolibri prompt remains -- async flow). --- .../k2go/redesign/SetupLibraryActivity.java | 33 +++++++++++-------- .../docs/ADR-395-network-cost-consent.md | 21 ++++++------ 2 files changed, 30 insertions(+), 24 deletions(-) diff --git a/controller/app/src/main/java/org/appdevforall/k2go/redesign/SetupLibraryActivity.java b/controller/app/src/main/java/org/appdevforall/k2go/redesign/SetupLibraryActivity.java index 5d7a896d6..a8e6b7f55 100644 --- a/controller/app/src/main/java/org/appdevforall/k2go/redesign/SetupLibraryActivity.java +++ b/controller/app/src/main/java/org/appdevforall/k2go/redesign/SetupLibraryActivity.java @@ -524,20 +524,25 @@ public void startBooksDownload() { v != null && v.length > 2 ? v[2] : ""); } selection().booksCart().clear(); - // ADFA-5074: through the wishlist, like ZIM and Courses. Books was the last door still - // calling its service directly, and that had a real consequence beyond symmetry: the - // service registers its session asynchronously in onStartCommand, so for a moment nothing - // was pending and nothing was in session. The index reads exactly that pair to decide the - // run is over — nothingToStart() plus an empty orchestrateStep — and could declare a - // just-started download complete and count down to the Library. Writing the wishlist first - // makes hasPending true synchronously, before the index is even launched, so that window - // does not exist. It also makes Books queue behind a busy line instead of overwriting. - BooksProvisioner.drain(this); - // ADFA-4988: go to the progress screen instead of returning to Get More and downloading - // invisibly. ADFA-5074: to the index, not the books detail. The hint that used to open the - // detail "when books is the only stream" made the landing depend on state the user cannot - // see, and the index is what ends the run. - startActivity(new Intent(this, SetupProgressActivity.class)); + // K2GO-395 (ADR-395 sec.10): the order is banked above; gate only the drain + navigation, so + // a declined or offline order stays queued (ContentAdmission also holds it on metered-without- + // consent). Same shape as startZimDownload. + org.appdevforall.k2go.networkpolicy.presentation.NetworkPolicyGate.guardHeavyStart(this, () -> { + // ADFA-5074: through the wishlist, like ZIM and Courses. Books was the last door still + // calling its service directly, and that had a real consequence beyond symmetry: the + // service registers its session asynchronously in onStartCommand, so for a moment nothing + // was pending and nothing was in session. The index reads exactly that pair to decide the + // run is over — nothingToStart() plus an empty orchestrateStep — and could declare a + // just-started download complete and count down to the Library. Writing the wishlist first + // makes hasPending true synchronously, before the index is even launched, so that window + // does not exist. It also makes Books queue behind a busy line instead of overwriting. + BooksProvisioner.drain(this); + // ADFA-4988: go to the progress screen instead of returning to Get More and downloading + // invisibly. ADFA-5074: to the index, not the books detail. The hint that used to open the + // detail "when books is the only stream" made the landing depend on state the user cannot + // see, and the index is what ends the run. + startActivity(new Intent(this, SetupProgressActivity.class)); + }); } /** ADFA-4850: Books landing -> the download manager screen (per-book checklist + retry). */ diff --git a/controller/docs/ADR-395-network-cost-consent.md b/controller/docs/ADR-395-network-cost-consent.md index 533731bda..2576f0fbb 100644 --- a/controller/docs/ADR-395-network-cost-consent.md +++ b/controller/docs/ADR-395-network-cost-consent.md @@ -170,15 +170,16 @@ Landed as a compiling, tested starting point for the implementer: - Enforcement (headless): `ContentAdmission.canStart` defers when `NetworkCostAdmission.allowsHeavyStartNow` is false, so the ZIM, Books and Kolibri drains all HOLD a banked order on metered-without-consent (sec.10). -- Wiring: observer started in `IIABApplication`; the ZIM commit point - (`SetupLibraryActivity.startZimDownload`) banks first, then gates the drain, so - a declined/offline order is queued, not lost. +- Wiring: observer started in `IIABApplication`; the ZIM and Books commit points + (`SetupLibraryActivity.startZimDownload` / `startBooksDownload`) bank first, then + gate the drain, so a declined/offline order is queued, not lost. - Strings translated to all 33 locales (machine-generated, pending human review) in `values*/strings_networkpolicy.xml`; `strings_untranslated.xml` is clear. -Remaining to finish the contract: the Books/Kolibri commit-point prompts (their -drains are already held), the Maps seam (re-locate post K2GO-394), the -DownloadManager seam, and the two-`hasInternet` fold. (l10n done pending review.) +Remaining to finish the contract: the Kolibri commit-point prompt (its drain is +already held; the ask needs care because the flow is async), the Maps seam +(re-locate post K2GO-394), the DownloadManager seam, and the two-`hasInternet` +fold. (l10n done pending review.) ## 6. Device evidence appendix (dark surfaces flattened) @@ -303,7 +304,7 @@ happens BEFORE the gate (`SetupLibraryActivity.startZimDownload` banks, then gat the drain), so a declined or offline order is queued, not lost. This removes the commit-point special case rather than adding one. -Still open as follow-ups: the Books/Kolibri commit-point prompts (their drains are -already held by the ContentAdmission change; only the interactive ask is missing), -the Maps seam (K2GO-394 reworked Maps; re-locate it), DownloadManager (OTA/portal), -and the two-`hasInternet` fold. +The ZIM and Books commit points now bank-then-prompt. Still open as follow-ups: +the Kolibri commit-point prompt (its drain is already held; the ask needs care +because the flow banks on a background thread), the Maps seam (K2GO-394 reworked +Maps; re-locate it), DownloadManager (OTA/portal), and the two-`hasInternet` fold. From 7f6bd7e7574d9a4601619f28c77d320c842a6b87 Mon Sep 17 00:00:00 2001 From: Luis Guzman Date: Wed, 16 Sep 2026 16:15:17 -0600 Subject: [PATCH 07/18] K2GO-395 feat(networkpolicy): gate FQR maps region download on metered cost FQR (Full-Quality Region) map download is a REST content op but a user-driven Operation, not a banked ContentType, so ContentAdmission does not cover it. Add a guardHeavyStart at its commit point (after the storage-consent dialog, wrapping the actual MapsRegionClient start). New three-arg guardHeavyStart overload runs an onDeclined callback so FQR resets the drawn map selection on decline/offline (no queue to fall back on). Device-verified on metered hotspot: dialog shows, Continue starts the extraction, Not now resets and starts nothing. --- .../presentation/NetworkPolicyGate.java | 28 +++++++++++++------ .../k2go/redesign/FqrController.java | 8 +++++- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/controller/app/src/main/java/org/appdevforall/k2go/networkpolicy/presentation/NetworkPolicyGate.java b/controller/app/src/main/java/org/appdevforall/k2go/networkpolicy/presentation/NetworkPolicyGate.java index 334ed0bee..a8a468dde 100644 --- a/controller/app/src/main/java/org/appdevforall/k2go/networkpolicy/presentation/NetworkPolicyGate.java +++ b/controller/app/src/main/java/org/appdevforall/k2go/networkpolicy/presentation/NetworkPolicyGate.java @@ -21,19 +21,29 @@ * reads the decision from {@link NetworkCostAdmission} (the one classify + consent * + policy source) and either proceeds, asks, or reports offline. * - *

This gate is only the PROMPT. The actual hold is enforced headless in - * {@code ContentAdmission} (via {@link NetworkCostAdmission}), which every content - * drain already consults -- so a banked order never starts on metered data without - * consent even if it was never routed through a commit point (the wizard-bank path, - * a background re-drain). Banking must therefore happen BEFORE this gate: an order - * declined or offline here stays queued and drains once the network is free or - * consent is given, rather than being lost (ADR-395 sec.10). + *

For the banked content streams (ZIM/Books/Kolibri) the actual HOLD is enforced + * headless in {@code ContentAdmission}; declining here just leaves the order queued, + * so the two-arg form takes no decline action. A direct, non-banked start (FQR maps, + * which is a user-driven Operation, not a banked ContentType, and so is NOT covered + * by ContentAdmission) uses the three-arg form to undo its own UI on decline -- e.g. + * clear the drawn map region -- since there is no queue to fall back on. */ public final class NetworkPolicyGate { private NetworkPolicyGate() {} + /** Two-arg form: decline/offline is a no-op (the order stays banked and drains later). */ public static void guardHeavyStart(@NonNull Activity activity, @NonNull Runnable onProceed) { + guardHeavyStart(activity, onProceed, () -> {}); + } + + /** + * Three-arg form: {@code onDeclined} runs when the user declines the metered + * prompt (or dismisses it) or when there is no network -- for callers with no + * queue, so they can undo the UI they were about to commit. + */ + public static void guardHeavyStart(@NonNull Activity activity, @NonNull Runnable onProceed, + @NonNull Runnable onDeclined) { switch (NetworkCostAdmission.decideNow(activity)) { case ALLOW: onProceed.run(); @@ -46,7 +56,8 @@ public static void guardHeavyStart(@NonNull Activity activity, @NonNull Runnable SessionMeteredConsentStore.get().grant(); onProceed.run(); }) - .setNegative(R.string.k2go_netpolicy_not_now, null) + .setNegative(R.string.k2go_netpolicy_not_now, onDeclined::run) + .setOnCancel(onDeclined::run) .show(); return; case BLOCKED_NO_NETWORK: @@ -55,6 +66,7 @@ public static void guardHeavyStart(@NonNull Activity activity, @NonNull Runnable if (root != null) { Snackbars.make(root, R.string.k2go_netpolicy_offline).show(); } + onDeclined.run(); } } } diff --git a/controller/app/src/main/java/org/appdevforall/k2go/redesign/FqrController.java b/controller/app/src/main/java/org/appdevforall/k2go/redesign/FqrController.java index 192d9b98b..718a2c230 100644 --- a/controller/app/src/main/java/org/appdevforall/k2go/redesign/FqrController.java +++ b/controller/app/src/main/java/org/appdevforall/k2go/redesign/FqrController.java @@ -60,6 +60,7 @@ import com.google.android.material.progressindicator.LinearProgressIndicator; import org.appdevforall.k2go.R; +import org.appdevforall.k2go.networkpolicy.presentation.NetworkPolicyGate; import org.appdevforall.k2go.ui.dialog.BrandDialog; import org.appdevforall.k2go.util.M3Text; import org.json.JSONArray; @@ -348,7 +349,12 @@ private void showConsent(String name, String box, long transfer, long archive, l dialog = new BrandDialog(themed) .setTitle(R.string.k2go_fqr_consent_title) .setContentView(body) - .setPositive(R.string.k2go_fqr_download, () -> startDownload(name)) + // K2GO-395 (ADR-395): after the storage consent, gate the actual REST start on metered + // cost. FQR is a user-driven Operation, not a banked ContentType, so ContentAdmission + // does not cover it -- the gate goes here. Declined/offline resets the map selection + // (there is no queue to fall back on), matching the negative/cancel below. + .setPositive(R.string.k2go_fqr_download, () -> NetworkPolicyGate.guardHeavyStart( + activity, () -> startDownload(name), this::resetMapSelection)) .setNegative(R.string.k2go_fqr_not_now, () -> resetMapSelection()) .setOnCancel(() -> resetMapSelection()) .setCancelable(true) From fd60c54a5d5c5c4a8b8b94b033372965299cf10b Mon Sep 17 00:00:00 2001 From: Luis Guzman Date: Wed, 16 Sep 2026 16:15:17 -0600 Subject: [PATCH 08/18] K2GO-395 style(l10n): soften metered wording (seem to be / data plan) Two copy changes across all 33 locales + default, per review: the assertive titles become hedged ('You seem to be on mobile data', 'You seem to have switched...') so the app does not claim certainty about a heuristic metered detection; and 'monthly data' becomes 'your data plan' to avoid assuming a monthly billing period. Machine translations, pending human review. --- .../app/src/main/res/values-ar/strings_networkpolicy.xml | 8 ++++---- .../app/src/main/res/values-az/strings_networkpolicy.xml | 8 ++++---- .../app/src/main/res/values-bg/strings_networkpolicy.xml | 8 ++++---- .../app/src/main/res/values-bn/strings_networkpolicy.xml | 8 ++++---- .../app/src/main/res/values-cs/strings_networkpolicy.xml | 8 ++++---- .../app/src/main/res/values-de/strings_networkpolicy.xml | 8 ++++---- .../app/src/main/res/values-el/strings_networkpolicy.xml | 8 ++++---- .../app/src/main/res/values-es/strings_networkpolicy.xml | 8 ++++---- .../app/src/main/res/values-fa/strings_networkpolicy.xml | 8 ++++---- .../app/src/main/res/values-fr/strings_networkpolicy.xml | 8 ++++---- .../app/src/main/res/values-gu/strings_networkpolicy.xml | 8 ++++---- .../app/src/main/res/values-hi/strings_networkpolicy.xml | 8 ++++---- .../app/src/main/res/values-hu/strings_networkpolicy.xml | 8 ++++---- .../app/src/main/res/values-in/strings_networkpolicy.xml | 8 ++++---- .../app/src/main/res/values-it/strings_networkpolicy.xml | 8 ++++---- .../app/src/main/res/values-ja/strings_networkpolicy.xml | 8 ++++---- .../app/src/main/res/values-ko/strings_networkpolicy.xml | 8 ++++---- .../app/src/main/res/values-lt/strings_networkpolicy.xml | 8 ++++---- .../app/src/main/res/values-nl/strings_networkpolicy.xml | 8 ++++---- .../app/src/main/res/values-no/strings_networkpolicy.xml | 8 ++++---- .../app/src/main/res/values-pl/strings_networkpolicy.xml | 8 ++++---- .../app/src/main/res/values-pt/strings_networkpolicy.xml | 8 ++++---- .../app/src/main/res/values-ro/strings_networkpolicy.xml | 8 ++++---- .../src/main/res/values-ru-rRU/strings_networkpolicy.xml | 8 ++++---- .../app/src/main/res/values-sk/strings_networkpolicy.xml | 8 ++++---- .../app/src/main/res/values-sr/strings_networkpolicy.xml | 8 ++++---- .../app/src/main/res/values-sw/strings_networkpolicy.xml | 8 ++++---- .../app/src/main/res/values-ta/strings_networkpolicy.xml | 8 ++++---- .../app/src/main/res/values-tr/strings_networkpolicy.xml | 8 ++++---- .../app/src/main/res/values-uk/strings_networkpolicy.xml | 8 ++++---- .../app/src/main/res/values-vi/strings_networkpolicy.xml | 8 ++++---- .../app/src/main/res/values-yo/strings_networkpolicy.xml | 8 ++++---- .../src/main/res/values-zh-rCN/strings_networkpolicy.xml | 8 ++++---- .../app/src/main/res/values/strings_networkpolicy.xml | 8 ++++---- 34 files changed, 136 insertions(+), 136 deletions(-) diff --git a/controller/app/src/main/res/values-ar/strings_networkpolicy.xml b/controller/app/src/main/res/values-ar/strings_networkpolicy.xml index 70291527c..937e29f0a 100644 --- a/controller/app/src/main/res/values-ar/strings_networkpolicy.xml +++ b/controller/app/src/main/res/values-ar/strings_networkpolicy.xml @@ -1,12 +1,12 @@ - أنت تستخدم بيانات الجوال - يستخدم هذا التنزيل بيانات الجوال. قد يترتب عليه تكلفة أو يستنفد باقتك الشهرية. هل تريد المتابعة؟ + يبدو أنك تستخدم بيانات الجوال + يستخدم هذا التنزيل بيانات الجوال. قد يترتب عليه تكلفة أو يستنفد باقة بياناتك. هل تريد المتابعة؟ المتابعة عبر البيانات ليس الآن لا توجد شبكة. اتصل بشبكة Wi-Fi للتنزيل. - لقد انتقلت إلى بيانات الجوال - تستخدم التنزيلات الجديدة الآن بيانات الجوال. قد يترتب على ذلك تكلفة أو يستنفد باقتك الشهرية. + يبدو أنك انتقلت إلى بيانات الجوال + تستخدم التنزيلات الجديدة الآن بيانات الجوال. قد يترتب على ذلك تكلفة أو يستنفد باقة بياناتك. تنبيهات تكلفة البيانات diff --git a/controller/app/src/main/res/values-az/strings_networkpolicy.xml b/controller/app/src/main/res/values-az/strings_networkpolicy.xml index a5398e08b..f2006938b 100644 --- a/controller/app/src/main/res/values-az/strings_networkpolicy.xml +++ b/controller/app/src/main/res/values-az/strings_networkpolicy.xml @@ -1,12 +1,12 @@ - Mobil internetdən istifadə edirsiniz - Bu yükləmə mobil internetdən istifadə edir. Bu, xərc yarada və ya aylıq paketinizi bitirə bilər. Davam etmək istəyirsiniz? + Görünür, mobil internetdən istifadə edirsiniz + Bu yükləmə mobil internetdən istifadə edir. Bu, xərc yarada və ya data paketinizi bitirə bilər. Davam etmək istəyirsiniz? Mobil internetlə davam et İndi yox Şəbəkə yoxdur. Yükləmək üçün Wi-Fi-a qoşulun. - Mobil internetə keçdiniz - Yeni yükləmələr indi mobil internetdən istifadə edir. Bu, xərc yarada və ya aylıq paketinizi bitirə bilər. + Deyəsən, mobil internetə keçdiniz + Yeni yükləmələr indi mobil internetdən istifadə edir. Bu, xərc yarada və ya data paketinizi bitirə bilər. Data xərci bildirişləri diff --git a/controller/app/src/main/res/values-bg/strings_networkpolicy.xml b/controller/app/src/main/res/values-bg/strings_networkpolicy.xml index 9cc33da70..1905d195d 100644 --- a/controller/app/src/main/res/values-bg/strings_networkpolicy.xml +++ b/controller/app/src/main/res/values-bg/strings_networkpolicy.xml @@ -1,12 +1,12 @@ - Използвате мобилни данни - Това изтегляне използва мобилни данни. Може да има такса или да изчерпи месечния ви план. Искате ли да продължите? + Изглежда използвате мобилни данни + Това изтегляне използва мобилни данни. Може да има такса или да изчерпи плана ви за данни. Искате ли да продължите? Продължи с мобилни данни Не сега Няма мрежа. Свържете се с Wi-Fi, за да изтеглите. - Превключихте към мобилни данни - Новите изтегляния вече използват мобилни данни. Това може да има такса или да изчерпи месечния ви план. + Изглежда превключихте към мобилни данни + Новите изтегляния вече използват мобилни данни. Това може да има такса или да изчерпи плана ви за данни. Известия за разход на данни diff --git a/controller/app/src/main/res/values-bn/strings_networkpolicy.xml b/controller/app/src/main/res/values-bn/strings_networkpolicy.xml index 9b7545009..ccf96117d 100644 --- a/controller/app/src/main/res/values-bn/strings_networkpolicy.xml +++ b/controller/app/src/main/res/values-bn/strings_networkpolicy.xml @@ -1,12 +1,12 @@ - আপনি মোবাইল ডেটা ব্যবহার করছেন - এই ডাউনলোডটি মোবাইল ডেটা ব্যবহার করে। এতে খরচ হতে পারে বা আপনার মাসিক ডেটা শেষ হতে পারে। আপনি কি চালিয়ে যেতে চান? + মনে হচ্ছে আপনি মোবাইল ডেটা ব্যবহার করছেন + এই ডাউনলোডটি মোবাইল ডেটা ব্যবহার করে। এতে খরচ হতে পারে বা আপনার ডেটা প্ল্যান শেষ হতে পারে। আপনি কি চালিয়ে যেতে চান? ডেটা দিয়ে চালিয়ে যান এখন নয় কোনো নেটওয়ার্ক নেই। ডাউনলোড করতে Wi-Fi এ সংযোগ করুন। - আপনি মোবাইল ডেটাতে স্যুইচ করেছেন - নতুন ডাউনলোডগুলি এখন মোবাইল ডেটা ব্যবহার করে। এতে খরচ হতে পারে বা আপনার মাসিক ডেটা শেষ হতে পারে। + মনে হচ্ছে আপনি মোবাইল ডেটাতে স্যুইচ করেছেন + নতুন ডাউনলোডগুলি এখন মোবাইল ডেটা ব্যবহার করে। এতে খরচ হতে পারে বা আপনার ডেটা প্ল্যান শেষ হতে পারে। ডেটা খরচের সতর্কতা diff --git a/controller/app/src/main/res/values-cs/strings_networkpolicy.xml b/controller/app/src/main/res/values-cs/strings_networkpolicy.xml index dc3ff4053..4321d35de 100644 --- a/controller/app/src/main/res/values-cs/strings_networkpolicy.xml +++ b/controller/app/src/main/res/values-cs/strings_networkpolicy.xml @@ -1,12 +1,12 @@ - Používáte mobilní data - Toto stahování používá mobilní data. Může být zpoplatněno nebo vyčerpat váš měsíční limit. Chcete pokračovat? + Zdá se, že používáte mobilní data + Toto stahování používá mobilní data. Může být zpoplatněno nebo vyčerpat váš datový limit. Chcete pokračovat? Pokračovat na mobilních datech Teď ne Žádná síť. Připojte se k Wi-Fi pro stažení. - Přepnuli jste na mobilní data - Nová stahování nyní používají mobilní data. Může být zpoplatněno nebo vyčerpat váš měsíční limit. + Zdá se, že jste přepnuli na mobilní data + Nová stahování nyní používají mobilní data. Může být zpoplatněno nebo vyčerpat váš datový limit. Upozornění na náklady za data diff --git a/controller/app/src/main/res/values-de/strings_networkpolicy.xml b/controller/app/src/main/res/values-de/strings_networkpolicy.xml index a842c46bb..be4c16dfe 100644 --- a/controller/app/src/main/res/values-de/strings_networkpolicy.xml +++ b/controller/app/src/main/res/values-de/strings_networkpolicy.xml @@ -1,12 +1,12 @@ - Sie nutzen mobile Daten - Dieser Download nutzt mobile Daten. Das kann Kosten verursachen oder Ihr monatliches Datenvolumen aufbrauchen. Möchten Sie fortfahren? + Sie scheinen mobile Daten zu nutzen + Dieser Download nutzt mobile Daten. Das kann Kosten verursachen oder Ihr Datenvolumen aufbrauchen. Möchten Sie fortfahren? Mit mobilen Daten fortfahren Nicht jetzt Kein Netzwerk. Verbinden Sie sich mit WLAN, um herunterzuladen. - Sie haben zu mobilen Daten gewechselt - Neue Downloads nutzen jetzt mobile Daten. Das kann Kosten verursachen oder Ihr monatliches Datenvolumen aufbrauchen. + Sie scheinen zu mobilen Daten gewechselt zu haben + Neue Downloads nutzen jetzt mobile Daten. Das kann Kosten verursachen oder Ihr Datenvolumen aufbrauchen. Warnungen zu Datenkosten diff --git a/controller/app/src/main/res/values-el/strings_networkpolicy.xml b/controller/app/src/main/res/values-el/strings_networkpolicy.xml index 8827ddb8c..5aae3107d 100644 --- a/controller/app/src/main/res/values-el/strings_networkpolicy.xml +++ b/controller/app/src/main/res/values-el/strings_networkpolicy.xml @@ -1,12 +1,12 @@ - Χρησιμοποιείτε δεδομένα κινητής - Αυτή η λήψη χρησιμοποιεί δεδομένα κινητής. Μπορεί να έχει κόστος ή να εξαντλήσει το μηνιαίο σας πακέτο. Θέλετε να συνεχίσετε; + Φαίνεται ότι χρησιμοποιείτε δεδομένα κινητής + Αυτή η λήψη χρησιμοποιεί δεδομένα κινητής. Μπορεί να έχει κόστος ή να εξαντλήσει το πρόγραμμα δεδομένων σας. Θέλετε να συνεχίσετε; Συνέχεια με δεδομένα Όχι τώρα Χωρίς δίκτυο. Συνδεθείτε σε Wi-Fi για λήψη. - Μεταβήκατε σε δεδομένα κινητής - Οι νέες λήψεις χρησιμοποιούν τώρα δεδομένα κινητής. Αυτό μπορεί να έχει κόστος ή να εξαντλήσει το μηνιαίο σας πακέτο. + Φαίνεται ότι μεταβήκατε σε δεδομένα κινητής + Οι νέες λήψεις χρησιμοποιούν τώρα δεδομένα κινητής. Αυτό μπορεί να έχει κόστος ή να εξαντλήσει το πρόγραμμα δεδομένων σας. Ειδοποιήσεις κόστους δεδομένων diff --git a/controller/app/src/main/res/values-es/strings_networkpolicy.xml b/controller/app/src/main/res/values-es/strings_networkpolicy.xml index 7b5d790c0..ceddeae83 100644 --- a/controller/app/src/main/res/values-es/strings_networkpolicy.xml +++ b/controller/app/src/main/res/values-es/strings_networkpolicy.xml @@ -1,12 +1,12 @@ - Estás usando datos móviles - Esta descarga usa datos móviles. Puede tener costo o agotar tu plan mensual. ¿Deseas continuar? + Parece que estás usando datos móviles + Esta descarga usa datos móviles. Puede tener costo o agotar tu plan de datos. ¿Deseas continuar? Continuar con datos Ahora no Sin red. Conéctate a Wi-Fi para descargar. - Cambiaste a datos móviles - Las nuevas descargas ahora usan datos móviles. Esto puede tener costo o agotar tu plan mensual. + Parece que cambiaste a datos móviles + Las nuevas descargas ahora usan datos móviles. Esto puede tener costo o agotar tu plan de datos. Alertas de costo de datos diff --git a/controller/app/src/main/res/values-fa/strings_networkpolicy.xml b/controller/app/src/main/res/values-fa/strings_networkpolicy.xml index 23f892f95..aa304ea79 100644 --- a/controller/app/src/main/res/values-fa/strings_networkpolicy.xml +++ b/controller/app/src/main/res/values-fa/strings_networkpolicy.xml @@ -1,12 +1,12 @@ - شما در حال استفاده از داده تلفن همراه هستید - این دانلود از داده تلفن همراه استفاده می‌کند. ممکن است هزینه داشته باشد یا بسته ماهانه شما را تمام کند. آیا می‌خواهید ادامه دهید؟ + به نظر می‌رسد از داده تلفن همراه استفاده می‌کنید + این دانلود از داده تلفن همراه استفاده می‌کند. ممکن است هزینه داشته باشد یا بسته داده شما را تمام کند. آیا می‌خواهید ادامه دهید؟ ادامه با داده تلفن همراه الان نه شبکه‌ای وجود ندارد. برای دانلود به Wi-Fi متصل شوید. - به داده تلفن همراه تغییر دادید - دانلودهای جدید اکنون از داده تلفن همراه استفاده می‌کنند. این ممکن است هزینه داشته باشد یا بسته ماهانه شما را تمام کند. + به نظر می‌رسد به داده تلفن همراه تغییر داده‌اید + دانلودهای جدید اکنون از داده تلفن همراه استفاده می‌کنند. این ممکن است هزینه داشته باشد یا بسته داده شما را تمام کند. هشدارهای هزینه داده diff --git a/controller/app/src/main/res/values-fr/strings_networkpolicy.xml b/controller/app/src/main/res/values-fr/strings_networkpolicy.xml index 2a816ade2..9135fe4bd 100644 --- a/controller/app/src/main/res/values-fr/strings_networkpolicy.xml +++ b/controller/app/src/main/res/values-fr/strings_networkpolicy.xml @@ -1,12 +1,12 @@ - Vous utilisez les données mobiles - Ce téléchargement utilise les données mobiles. Il peut coûter cher ou épuiser votre forfait mensuel. Voulez-vous continuer ? + Vous semblez utiliser les données mobiles + Ce téléchargement utilise les données mobiles. Il peut coûter cher ou épuiser votre forfait de données. Voulez-vous continuer ? Continuer avec les données Pas maintenant Aucun réseau. Connectez-vous au Wi-Fi pour télécharger. - Vous êtes passé aux données mobiles - Les nouveaux téléchargements utilisent désormais les données mobiles. Cela peut coûter cher ou épuiser votre forfait mensuel. + Vous semblez être passé aux données mobiles + Les nouveaux téléchargements utilisent désormais les données mobiles. Cela peut coûter cher ou épuiser votre forfait de données. Alertes de coût des données diff --git a/controller/app/src/main/res/values-gu/strings_networkpolicy.xml b/controller/app/src/main/res/values-gu/strings_networkpolicy.xml index 2b692c002..5abd087e0 100644 --- a/controller/app/src/main/res/values-gu/strings_networkpolicy.xml +++ b/controller/app/src/main/res/values-gu/strings_networkpolicy.xml @@ -1,12 +1,12 @@ - તમે મોબાઇલ ડેટા વાપરી રહ્યા છો - આ ડાઉનલોડ મોબાઇલ ડેટા વાપરે છે. તેનાથી ખર્ચ થઈ શકે અથવા તમારો માસિક ડેટા ખતમ થઈ શકે. શું તમે ચાલુ રાખવા માંગો છો? + એવું લાગે છે કે તમે મોબાઇલ ડેટા વાપરી રહ્યા છો + આ ડાઉનલોડ મોબાઇલ ડેટા વાપરે છે. તેનાથી ખર્ચ થઈ શકે અથવા તમારો ડેટા પ્લાન ખતમ થઈ શકે. શું તમે ચાલુ રાખવા માંગો છો? ડેટા પર ચાલુ રાખો હમણાં નહીં કોઈ નેટવર્ક નથી. ડાઉનલોડ કરવા માટે Wi-Fi સાથે કનેક્ટ કરો. - તમે મોબાઇલ ડેટા પર સ્વિચ કર્યું - નવા ડાઉનલોડ હવે મોબાઇલ ડેટા વાપરે છે. તેનાથી ખર્ચ થઈ શકે અથવા તમારો માસિક ડેટા ખતમ થઈ શકે. + એવું લાગે છે કે તમે મોબાઇલ ડેટા પર સ્વિચ કર્યું + નવા ડાઉનલોડ હવે મોબાઇલ ડેટા વાપરે છે. તેનાથી ખર્ચ થઈ શકે અથવા તમારો ડેટા પ્લાન ખતમ થઈ શકે. ડેટા ખર્ચ ચેતવણીઓ diff --git a/controller/app/src/main/res/values-hi/strings_networkpolicy.xml b/controller/app/src/main/res/values-hi/strings_networkpolicy.xml index 842be643f..6d14e350c 100644 --- a/controller/app/src/main/res/values-hi/strings_networkpolicy.xml +++ b/controller/app/src/main/res/values-hi/strings_networkpolicy.xml @@ -1,12 +1,12 @@ - आप मोबाइल डेटा का उपयोग कर रहे हैं - यह डाउनलोड मोबाइल डेटा का उपयोग करता है. इससे शुल्क लग सकता है या आपका मासिक डेटा खत्म हो सकता है. क्या आप जारी रखना चाहते हैं? + ऐसा लगता है कि आप मोबाइल डेटा का उपयोग कर रहे हैं + यह डाउनलोड मोबाइल डेटा का उपयोग करता है. इससे शुल्क लग सकता है या आपका डेटा प्लान खत्म हो सकता है. क्या आप जारी रखना चाहते हैं? डेटा पर जारी रखें अभी नहीं कोई नेटवर्क नहीं. डाउनलोड करने के लिए Wi-Fi से कनेक्ट करें. - आप मोबाइल डेटा पर स्विच हो गए - नए डाउनलोड अब मोबाइल डेटा का उपयोग करते हैं. इससे शुल्क लग सकता है या आपका मासिक डेटा खत्म हो सकता है. + ऐसा लगता है कि आप मोबाइल डेटा पर स्विच हो गए + नए डाउनलोड अब मोबाइल डेटा का उपयोग करते हैं. इससे शुल्क लग सकता है या आपका डेटा प्लान खत्म हो सकता है. डेटा लागत अलर्ट diff --git a/controller/app/src/main/res/values-hu/strings_networkpolicy.xml b/controller/app/src/main/res/values-hu/strings_networkpolicy.xml index d00a79caa..e20ccf660 100644 --- a/controller/app/src/main/res/values-hu/strings_networkpolicy.xml +++ b/controller/app/src/main/res/values-hu/strings_networkpolicy.xml @@ -1,12 +1,12 @@ - Mobiladatot használ - Ez a letöltés mobiladatot használ. Ez költséggel járhat, vagy elfogyaszthatja a havi keretét. Folytatja? + Úgy tűnik, mobiladatot használ + Ez a letöltés mobiladatot használ. Ez költséggel járhat, vagy elfogyaszthatja az adatkeretét. Folytatja? Folytatás mobiladaton Most nem Nincs hálózat. Csatlakozzon Wi-Fi-hez a letöltéshez. - Átváltott mobiladatra - Az új letöltések most mobiladatot használnak. Ez költséggel járhat, vagy elfogyaszthatja a havi keretét. + Úgy tűnik, átváltott mobiladatra + Az új letöltések most mobiladatot használnak. Ez költséggel járhat, vagy elfogyaszthatja az adatkeretét. Adatköltség-figyelmeztetések diff --git a/controller/app/src/main/res/values-in/strings_networkpolicy.xml b/controller/app/src/main/res/values-in/strings_networkpolicy.xml index 4956360ea..537ea5f98 100644 --- a/controller/app/src/main/res/values-in/strings_networkpolicy.xml +++ b/controller/app/src/main/res/values-in/strings_networkpolicy.xml @@ -1,12 +1,12 @@ - Anda menggunakan data seluler - Unduhan ini menggunakan data seluler. Ini dapat menimbulkan biaya atau menghabiskan kuota bulanan Anda. Apakah Anda ingin melanjutkan? + Anda tampaknya menggunakan data seluler + Unduhan ini menggunakan data seluler. Ini dapat menimbulkan biaya atau menghabiskan paket data Anda. Apakah Anda ingin melanjutkan? Lanjutkan dengan data Nanti saja Tidak ada jaringan. Sambungkan ke Wi-Fi untuk mengunduh. - Anda beralih ke data seluler - Unduhan baru kini menggunakan data seluler. Ini dapat menimbulkan biaya atau menghabiskan kuota bulanan Anda. + Anda tampaknya beralih ke data seluler + Unduhan baru kini menggunakan data seluler. Ini dapat menimbulkan biaya atau menghabiskan paket data Anda. Peringatan biaya data diff --git a/controller/app/src/main/res/values-it/strings_networkpolicy.xml b/controller/app/src/main/res/values-it/strings_networkpolicy.xml index 865401da4..8727dd9e6 100644 --- a/controller/app/src/main/res/values-it/strings_networkpolicy.xml +++ b/controller/app/src/main/res/values-it/strings_networkpolicy.xml @@ -1,12 +1,12 @@ - Stai usando i dati mobili - Questo download usa i dati mobili. Può comportare costi o esaurire il tuo piano mensile. Vuoi continuare? + Sembra che tu stia usando i dati mobili + Questo download usa i dati mobili. Può comportare costi o esaurire il tuo piano dati. Vuoi continuare? Continua con i dati Non ora Nessuna rete. Connettiti al Wi-Fi per scaricare. - Sei passato ai dati mobili - I nuovi download ora usano i dati mobili. Questo può comportare costi o esaurire il tuo piano mensile. + Sembra che tu sia passato ai dati mobili + I nuovi download ora usano i dati mobili. Questo può comportare costi o esaurire il tuo piano dati. Avvisi sui costi dei dati diff --git a/controller/app/src/main/res/values-ja/strings_networkpolicy.xml b/controller/app/src/main/res/values-ja/strings_networkpolicy.xml index f528cf56c..82a214cab 100644 --- a/controller/app/src/main/res/values-ja/strings_networkpolicy.xml +++ b/controller/app/src/main/res/values-ja/strings_networkpolicy.xml @@ -1,12 +1,12 @@ - モバイルデータを使用しています - このダウンロードはモバイルデータを使用します。料金が発生したり、月間データを使い切ったりする可能性があります。続行しますか? + モバイルデータを使用しているようです + このダウンロードはモバイルデータを使用します。料金が発生したり、データプランを使い切ったりする可能性があります。続行しますか? モバイルデータで続行 今はしない ネットワークがありません。ダウンロードするには Wi-Fi に接続してください。 - モバイルデータに切り替わりました - 新しいダウンロードはモバイルデータを使用します。料金が発生したり、月間データを使い切ったりする可能性があります。 + モバイルデータに切り替わったようです + 新しいダウンロードはモバイルデータを使用します。料金が発生したり、データプランを使い切ったりする可能性があります。 データ料金の通知 diff --git a/controller/app/src/main/res/values-ko/strings_networkpolicy.xml b/controller/app/src/main/res/values-ko/strings_networkpolicy.xml index 4d9dfc327..28e8afd5c 100644 --- a/controller/app/src/main/res/values-ko/strings_networkpolicy.xml +++ b/controller/app/src/main/res/values-ko/strings_networkpolicy.xml @@ -1,12 +1,12 @@ - 모바일 데이터를 사용 중입니다 - 이 다운로드는 모바일 데이터를 사용합니다. 요금이 발생하거나 월 데이터를 모두 사용할 수 있습니다. 계속하시겠습니까? + 모바일 데이터를 사용 중인 것 같습니다 + 이 다운로드는 모바일 데이터를 사용합니다. 요금이 발생하거나 데이터 요금제를 모두 사용할 수 있습니다. 계속하시겠습니까? 데이터로 계속 나중에 네트워크가 없습니다. 다운로드하려면 Wi-Fi에 연결하세요. - 모바일 데이터로 전환되었습니다 - 새 다운로드가 이제 모바일 데이터를 사용합니다. 요금이 발생하거나 월 데이터를 모두 사용할 수 있습니다. + 모바일 데이터로 전환된 것 같습니다 + 새 다운로드가 이제 모바일 데이터를 사용합니다. 요금이 발생하거나 데이터 요금제를 모두 사용할 수 있습니다. 데이터 요금 알림 diff --git a/controller/app/src/main/res/values-lt/strings_networkpolicy.xml b/controller/app/src/main/res/values-lt/strings_networkpolicy.xml index 22f137617..d1b0c61cb 100644 --- a/controller/app/src/main/res/values-lt/strings_networkpolicy.xml +++ b/controller/app/src/main/res/values-lt/strings_networkpolicy.xml @@ -1,12 +1,12 @@ - Naudojate mobiliuosius duomenis - Šis atsisiuntimas naudoja mobiliuosius duomenis. Tai gali kainuoti arba išnaudoti mėnesio planą. Ar norite tęsti? + Panašu, kad naudojate mobiliuosius duomenis + Šis atsisiuntimas naudoja mobiliuosius duomenis. Tai gali kainuoti arba išnaudoti duomenų planą. Ar norite tęsti? Tęsti su mobiliaisiais duomenimis Ne dabar Nėra tinklo. Prisijunkite prie Wi-Fi, kad atsisiųstumėte. - Perjungėte į mobiliuosius duomenis - Nauji atsisiuntimai dabar naudoja mobiliuosius duomenis. Tai gali kainuoti arba išnaudoti mėnesio planą. + Panašu, kad perjungėte į mobiliuosius duomenis + Nauji atsisiuntimai dabar naudoja mobiliuosius duomenis. Tai gali kainuoti arba išnaudoti duomenų planą. Duomenų sąnaudų įspėjimai diff --git a/controller/app/src/main/res/values-nl/strings_networkpolicy.xml b/controller/app/src/main/res/values-nl/strings_networkpolicy.xml index 1b713b9fd..c8a2485f1 100644 --- a/controller/app/src/main/res/values-nl/strings_networkpolicy.xml +++ b/controller/app/src/main/res/values-nl/strings_networkpolicy.xml @@ -1,12 +1,12 @@ - Je gebruikt mobiele data - Deze download gebruikt mobiele data. Dit kan kosten opleveren of je maandbundel opmaken. Wil je doorgaan? + Je lijkt mobiele data te gebruiken + Deze download gebruikt mobiele data. Dit kan kosten opleveren of je databundel opmaken. Wil je doorgaan? Doorgaan met mobiele data Niet nu Geen netwerk. Maak verbinding met wifi om te downloaden. - Je bent overgeschakeld naar mobiele data - Nieuwe downloads gebruiken nu mobiele data. Dit kan kosten opleveren of je maandbundel opmaken. + Je lijkt overgeschakeld te zijn naar mobiele data + Nieuwe downloads gebruiken nu mobiele data. Dit kan kosten opleveren of je databundel opmaken. Waarschuwingen datakosten diff --git a/controller/app/src/main/res/values-no/strings_networkpolicy.xml b/controller/app/src/main/res/values-no/strings_networkpolicy.xml index 721442fa8..d31abc714 100644 --- a/controller/app/src/main/res/values-no/strings_networkpolicy.xml +++ b/controller/app/src/main/res/values-no/strings_networkpolicy.xml @@ -1,12 +1,12 @@ - Du bruker mobildata - Denne nedlastingen bruker mobildata. Det kan koste penger eller bruke opp den månedlige datapakken. Vil du fortsette? + Det ser ut til at du bruker mobildata + Denne nedlastingen bruker mobildata. Det kan koste penger eller bruke opp datapakken din. Vil du fortsette? Fortsett med mobildata Ikke nå Ingen nettverk. Koble til Wi-Fi for å laste ned. - Du byttet til mobildata - Nye nedlastinger bruker nå mobildata. Det kan koste penger eller bruke opp den månedlige datapakken. + Det ser ut til at du byttet til mobildata + Nye nedlastinger bruker nå mobildata. Det kan koste penger eller bruke opp datapakken din. Varsler om datakostnad diff --git a/controller/app/src/main/res/values-pl/strings_networkpolicy.xml b/controller/app/src/main/res/values-pl/strings_networkpolicy.xml index 60fdeec8a..3830a43cb 100644 --- a/controller/app/src/main/res/values-pl/strings_networkpolicy.xml +++ b/controller/app/src/main/res/values-pl/strings_networkpolicy.xml @@ -1,12 +1,12 @@ - Używasz danych komórkowych - To pobieranie używa danych komórkowych. Może wiązać się z kosztami lub wyczerpać miesięczny pakiet. Czy chcesz kontynuować? + Wygląda na to, że używasz danych komórkowych + To pobieranie używa danych komórkowych. Może wiązać się z kosztami lub wyczerpać pakiet danych. Czy chcesz kontynuować? Kontynuuj na danych Nie teraz Brak sieci. Połącz się z Wi-Fi, aby pobrać. - Przełączono na dane komórkowe - Nowe pobierania używają teraz danych komórkowych. Może to wiązać się z kosztami lub wyczerpać miesięczny pakiet. + Wygląda na to, że przełączono na dane komórkowe + Nowe pobierania używają teraz danych komórkowych. Może to wiązać się z kosztami lub wyczerpać pakiet danych. Alerty o kosztach danych diff --git a/controller/app/src/main/res/values-pt/strings_networkpolicy.xml b/controller/app/src/main/res/values-pt/strings_networkpolicy.xml index b9fac679e..7797c7f68 100644 --- a/controller/app/src/main/res/values-pt/strings_networkpolicy.xml +++ b/controller/app/src/main/res/values-pt/strings_networkpolicy.xml @@ -1,12 +1,12 @@ - Você está usando dados móveis - Este download usa dados móveis. Pode gerar custos ou esgotar seu plano mensal. Deseja continuar? + Parece que você está usando dados móveis + Este download usa dados móveis. Pode gerar custos ou esgotar seu plano de dados. Deseja continuar? Continuar com dados Agora não Sem rede. Conecte-se ao Wi-Fi para baixar. - Você mudou para dados móveis - Os novos downloads agora usam dados móveis. Isso pode gerar custos ou esgotar seu plano mensal. + Parece que você mudou para dados móveis + Os novos downloads agora usam dados móveis. Isso pode gerar custos ou esgotar seu plano de dados. Alertas de custo de dados diff --git a/controller/app/src/main/res/values-ro/strings_networkpolicy.xml b/controller/app/src/main/res/values-ro/strings_networkpolicy.xml index c9ec3c08a..0cfbf0fd4 100644 --- a/controller/app/src/main/res/values-ro/strings_networkpolicy.xml +++ b/controller/app/src/main/res/values-ro/strings_networkpolicy.xml @@ -1,12 +1,12 @@ - Folosești date mobile - Această descărcare folosește date mobile. Poate genera costuri sau îți poate epuiza planul lunar. Vrei să continui? + Se pare că folosești date mobile + Această descărcare folosește date mobile. Poate genera costuri sau îți poate epuiza planul de date. Vrei să continui? Continuă pe date mobile Nu acum Fără rețea. Conectează-te la Wi-Fi pentru a descărca. - Ai trecut la date mobile - Descărcările noi folosesc acum date mobile. Acest lucru poate genera costuri sau îți poate epuiza planul lunar. + Se pare că ai trecut la date mobile + Descărcările noi folosesc acum date mobile. Acest lucru poate genera costuri sau îți poate epuiza planul de date. Alerte privind costul datelor diff --git a/controller/app/src/main/res/values-ru-rRU/strings_networkpolicy.xml b/controller/app/src/main/res/values-ru-rRU/strings_networkpolicy.xml index cb3002e76..e007ac965 100644 --- a/controller/app/src/main/res/values-ru-rRU/strings_networkpolicy.xml +++ b/controller/app/src/main/res/values-ru-rRU/strings_networkpolicy.xml @@ -1,12 +1,12 @@ - Вы используете мобильные данные - Эта загрузка использует мобильные данные. Это может повлечь расходы или исчерпать месячный пакет. Продолжить? + Похоже, вы используете мобильные данные + Эта загрузка использует мобильные данные. Это может повлечь расходы или исчерпать пакет данных. Продолжить? Продолжить на мобильных данных Не сейчас Нет сети. Подключитесь к Wi-Fi для загрузки. - Вы переключились на мобильные данные - Новые загрузки теперь используют мобильные данные. Это может повлечь расходы или исчерпать месячный пакет. + Похоже, вы переключились на мобильные данные + Новые загрузки теперь используют мобильные данные. Это может повлечь расходы или исчерпать пакет данных. Оповещения о расходе данных diff --git a/controller/app/src/main/res/values-sk/strings_networkpolicy.xml b/controller/app/src/main/res/values-sk/strings_networkpolicy.xml index 282d50359..fa5c85e69 100644 --- a/controller/app/src/main/res/values-sk/strings_networkpolicy.xml +++ b/controller/app/src/main/res/values-sk/strings_networkpolicy.xml @@ -1,12 +1,12 @@ - Používate mobilné dáta - Toto sťahovanie používa mobilné dáta. Môže byť spoplatnené alebo vyčerpať váš mesačný limit. Chcete pokračovať? + Zdá sa, že používate mobilné dáta + Toto sťahovanie používa mobilné dáta. Môže byť spoplatnené alebo vyčerpať váš dátový limit. Chcete pokračovať? Pokračovať na mobilných dátach Teraz nie Žiadna sieť. Pripojte sa k Wi-Fi na stiahnutie. - Prepli ste sa na mobilné dáta - Nové sťahovania teraz používajú mobilné dáta. Môže byť spoplatnené alebo vyčerpať váš mesačný limit. + Zdá sa, že ste sa prepli na mobilné dáta + Nové sťahovania teraz používajú mobilné dáta. Môže byť spoplatnené alebo vyčerpať váš dátový limit. Upozornenia na náklady za dáta diff --git a/controller/app/src/main/res/values-sr/strings_networkpolicy.xml b/controller/app/src/main/res/values-sr/strings_networkpolicy.xml index 7a4e8564f..b7eb523b2 100644 --- a/controller/app/src/main/res/values-sr/strings_networkpolicy.xml +++ b/controller/app/src/main/res/values-sr/strings_networkpolicy.xml @@ -1,12 +1,12 @@ - Користите мобилне податке - Ово преузимање користи мобилне податке. Може да има трошак или да потроши ваш месечни план. Желите ли да наставите? + Изгледа да користите мобилне податке + Ово преузимање користи мобилне податке. Може да има трошак или да потроши ваш план за податке. Желите ли да наставите? Настави на мобилним подацима Не сада Нема мреже. Повежите се на Wi-Fi да бисте преузели. - Прешли сте на мобилне податке - Нова преузимања сада користе мобилне податке. Ово може да има трошак или да потроши ваш месечни план. + Изгледа да сте прешли на мобилне податке + Нова преузимања сада користе мобилне податке. Ово може да има трошак или да потроши ваш план за податке. Обавештења о трошку података diff --git a/controller/app/src/main/res/values-sw/strings_networkpolicy.xml b/controller/app/src/main/res/values-sw/strings_networkpolicy.xml index 740e138f4..2c0a2de1d 100644 --- a/controller/app/src/main/res/values-sw/strings_networkpolicy.xml +++ b/controller/app/src/main/res/values-sw/strings_networkpolicy.xml @@ -1,12 +1,12 @@ - Unatumia data ya simu - Upakuaji huu unatumia data ya simu. Inaweza kugharimu au kumaliza kifurushi chako cha mwezi. Ungependa kuendelea? + Inaonekana unatumia data ya simu + Upakuaji huu unatumia data ya simu. Inaweza kugharimu au kumaliza kifurushi chako cha data. Ungependa kuendelea? Endelea kwa data Si sasa Hakuna mtandao. Unganisha kwa Wi-Fi ili kupakua. - Umebadili hadi data ya simu - Vipakuliwa vipya sasa vinatumia data ya simu. Hii inaweza kugharimu au kumaliza kifurushi chako cha mwezi. + Inaonekana umebadili hadi data ya simu + Vipakuliwa vipya sasa vinatumia data ya simu. Hii inaweza kugharimu au kumaliza kifurushi chako cha data. Arifa za gharama ya data diff --git a/controller/app/src/main/res/values-ta/strings_networkpolicy.xml b/controller/app/src/main/res/values-ta/strings_networkpolicy.xml index 25382c5a1..d54e4f168 100644 --- a/controller/app/src/main/res/values-ta/strings_networkpolicy.xml +++ b/controller/app/src/main/res/values-ta/strings_networkpolicy.xml @@ -1,12 +1,12 @@ - நீங்கள் மொபைல் டேட்டாவைப் பயன்படுத்துகிறீர்கள் - இந்தப் பதிவிறக்கம் மொபைல் டேட்டாவைப் பயன்படுத்துகிறது. இதனால் கட்டணம் ஏற்படலாம் அல்லது உங்கள் மாதத் திட்டம் தீர்ந்துபோகலாம். தொடர விரும்புகிறீர்களா? + நீங்கள் மொபைல் டேட்டாவைப் பயன்படுத்துவது போல் தெரிகிறது + இந்தப் பதிவிறக்கம் மொபைல் டேட்டாவைப் பயன்படுத்துகிறது. இதனால் கட்டணம் ஏற்படலாம் அல்லது உங்கள் தரவுத் திட்டம் தீர்ந்துபோகலாம். தொடர விரும்புகிறீர்களா? டேட்டாவில் தொடரவும் இப்போது வேண்டாம் நெட்வொர்க் இல்லை. பதிவிறக்க Wi-Fi உடன் இணைக்கவும். - நீங்கள் மொபைல் டேட்டாவிற்கு மாறினீர்கள் - புதிய பதிவிறக்கங்கள் இப்போது மொபைல் டேட்டாவைப் பயன்படுத்துகின்றன. இதனால் கட்டணம் ஏற்படலாம் அல்லது உங்கள் மாதத் திட்டம் தீர்ந்துபோகலாம். + நீங்கள் மொபைல் டேட்டாவிற்கு மாறியது போல் தெரிகிறது + புதிய பதிவிறக்கங்கள் இப்போது மொபைல் டேட்டாவைப் பயன்படுத்துகின்றன. இதனால் கட்டணம் ஏற்படலாம் அல்லது உங்கள் தரவுத் திட்டம் தீர்ந்துபோகலாம். டேட்டா செலவு எச்சரிக்கைகள் diff --git a/controller/app/src/main/res/values-tr/strings_networkpolicy.xml b/controller/app/src/main/res/values-tr/strings_networkpolicy.xml index c8f2ef75e..fe363bc98 100644 --- a/controller/app/src/main/res/values-tr/strings_networkpolicy.xml +++ b/controller/app/src/main/res/values-tr/strings_networkpolicy.xml @@ -1,12 +1,12 @@ - Mobil veri kullanıyorsunuz - Bu indirme mobil veri kullanıyor. Ücretlendirilebilir veya aylık paketinizi tüketebilir. Devam etmek istiyor musunuz? + Mobil veri kullanıyor gibisiniz + Bu indirme mobil veri kullanıyor. Ücretlendirilebilir veya veri paketinizi tüketebilir. Devam etmek istiyor musunuz? Mobil veriyle devam et Şimdi değil Ağ yok. İndirmek için Wi-Fi\'ye bağlanın. - Mobil veriye geçtiniz - Yeni indirmeler artık mobil veri kullanıyor. Bu, ücretlendirilebilir veya aylık paketinizi tüketebilir. + Mobil veriye geçmiş gibisiniz + Yeni indirmeler artık mobil veri kullanıyor. Bu, ücretlendirilebilir veya veri paketinizi tüketebilir. Veri maliyeti uyarıları diff --git a/controller/app/src/main/res/values-uk/strings_networkpolicy.xml b/controller/app/src/main/res/values-uk/strings_networkpolicy.xml index 27dcb008e..9071a3a02 100644 --- a/controller/app/src/main/res/values-uk/strings_networkpolicy.xml +++ b/controller/app/src/main/res/values-uk/strings_networkpolicy.xml @@ -1,12 +1,12 @@ - Ви використовуєте мобільні дані - Це завантаження використовує мобільні дані. Це може призвести до витрат або вичерпати місячний пакет. Продовжити? + Схоже, ви використовуєте мобільні дані + Це завантаження використовує мобільні дані. Це може призвести до витрат або вичерпати пакет даних. Продовжити? Продовжити на мобільних даних Не зараз Немає мережі. Підключіться до Wi-Fi, щоб завантажити. - Ви перейшли на мобільні дані - Нові завантаження тепер використовують мобільні дані. Це може призвести до витрат або вичерпати місячний пакет. + Схоже, ви перейшли на мобільні дані + Нові завантаження тепер використовують мобільні дані. Це може призвести до витрат або вичерпати пакет даних. Сповіщення про витрати даних diff --git a/controller/app/src/main/res/values-vi/strings_networkpolicy.xml b/controller/app/src/main/res/values-vi/strings_networkpolicy.xml index 35367cf1d..c73e04cdc 100644 --- a/controller/app/src/main/res/values-vi/strings_networkpolicy.xml +++ b/controller/app/src/main/res/values-vi/strings_networkpolicy.xml @@ -1,12 +1,12 @@ - Bạn đang dùng dữ liệu di động - Bản tải xuống này dùng dữ liệu di động. Có thể phát sinh chi phí hoặc dùng hết gói hàng tháng của bạn. Bạn có muốn tiếp tục không? + Có vẻ như bạn đang dùng dữ liệu di động + Bản tải xuống này dùng dữ liệu di động. Có thể phát sinh chi phí hoặc dùng hết gói dữ liệu của bạn. Bạn có muốn tiếp tục không? Tiếp tục bằng dữ liệu Để sau Không có mạng. Kết nối Wi-Fi để tải xuống. - Bạn đã chuyển sang dữ liệu di động - Các bản tải xuống mới hiện dùng dữ liệu di động. Điều này có thể phát sinh chi phí hoặc dùng hết gói hàng tháng của bạn. + Có vẻ như bạn đã chuyển sang dữ liệu di động + Các bản tải xuống mới hiện dùng dữ liệu di động. Điều này có thể phát sinh chi phí hoặc dùng hết gói dữ liệu của bạn. Cảnh báo chi phí dữ liệu diff --git a/controller/app/src/main/res/values-yo/strings_networkpolicy.xml b/controller/app/src/main/res/values-yo/strings_networkpolicy.xml index 11bdb0461..56de0b6a0 100644 --- a/controller/app/src/main/res/values-yo/strings_networkpolicy.xml +++ b/controller/app/src/main/res/values-yo/strings_networkpolicy.xml @@ -1,12 +1,12 @@ - O n lo data alagbeka - Igbasilẹ yii n lo data alagbeka. O le san owo tabi lo package oṣooṣu rẹ tan. Ṣe o fẹ tẹsiwaju? + O dabi pe o n lo data alagbeka + Igbasilẹ yii n lo data alagbeka. O le san owo tabi lo package data rẹ tan. Ṣe o fẹ tẹsiwaju? Tẹsiwaju pẹlu data Kii ṣe bayii Ko si nẹtiwọọki. Sopọ si Wi-Fi lati gbaa wọle. - O ti yipada si data alagbeka - Awọn igbasilẹ tuntun n lo data alagbeka bayii. Eyi le san owo tabi lo package oṣooṣu rẹ tan. + O dabi pe o ti yipada si data alagbeka + Awọn igbasilẹ tuntun n lo data alagbeka bayii. Eyi le san owo tabi lo package data rẹ tan. Awọn ikilọ iye owo data diff --git a/controller/app/src/main/res/values-zh-rCN/strings_networkpolicy.xml b/controller/app/src/main/res/values-zh-rCN/strings_networkpolicy.xml index 92acaec2c..2276686c5 100644 --- a/controller/app/src/main/res/values-zh-rCN/strings_networkpolicy.xml +++ b/controller/app/src/main/res/values-zh-rCN/strings_networkpolicy.xml @@ -1,12 +1,12 @@ - 您正在使用移动数据 - 此下载会使用移动数据。可能产生费用或用尽您的每月流量。是否继续? + 您似乎正在使用移动数据 + 此下载会使用移动数据。可能产生费用或用尽您的套餐流量。是否继续? 使用数据继续 暂不 无网络。请连接 Wi-Fi 以下载。 - 您已切换到移动数据 - 新的下载现在会使用移动数据。这可能产生费用或用尽您的每月流量。 + 您似乎已切换到移动数据 + 新的下载现在会使用移动数据。这可能产生费用或用尽您的套餐流量。 数据费用提醒 diff --git a/controller/app/src/main/res/values/strings_networkpolicy.xml b/controller/app/src/main/res/values/strings_networkpolicy.xml index a5e3bc840..fc91139b2 100644 --- a/controller/app/src/main/res/values/strings_networkpolicy.xml +++ b/controller/app/src/main/res/values/strings_networkpolicy.xml @@ -1,12 +1,12 @@ - You are on mobile data - This download uses mobile data. It can add cost or use up your monthly data. Do you want to continue? + You seem to be on mobile data + This download uses mobile data. It can add cost or use up your data plan. Do you want to continue? Continue on data Not now No network. Connect to Wi-Fi to download. - You switched to mobile data - New downloads now use mobile data. This can add cost or use up your monthly data. + You seem to have switched to mobile data + New downloads now use mobile data. This can add cost or use up your data plan. Data cost alerts From 67aa7a2774039fcefe8aa167095dcc1cfc0d5abc Mon Sep 17 00:00:00 2001 From: Luis Guzman Date: Wed, 16 Sep 2026 16:32:44 -0600 Subject: [PATCH 09/18] K2GO-395 feat(networkpolicy): prompt at the Kolibri commit point Kolibri's start is async (it banks on the IO pool), so the metered prompt is added at the commit point (main thread) and the original body moves to commitLive. Both prompt outcomes proceed: the order is banked either way (offline-first, not lost), and the actual hold is enforced in ContentAdmission -- a declined order shows as Queued on the index and drains once consent or Wi-Fi arrives, matching how Kolibri already defers a busy-line order. Completes the three REST content streams. --- .../kolibri/presentation/KolibriConfirmFragment.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/controller/app/src/main/java/org/appdevforall/k2go/kolibri/presentation/KolibriConfirmFragment.java b/controller/app/src/main/java/org/appdevforall/k2go/kolibri/presentation/KolibriConfirmFragment.java index 873c59d65..f9bb373ab 100644 --- a/controller/app/src/main/java/org/appdevforall/k2go/kolibri/presentation/KolibriConfirmFragment.java +++ b/controller/app/src/main/java/org/appdevforall/k2go/kolibri/presentation/KolibriConfirmFragment.java @@ -383,7 +383,16 @@ private void startLive(List chosen) { refuse(R.string.k2go_kolibri_nothing_to_add); return; } + // K2GO-395 (ADR-395): prompt on a metered network before committing. Both outcomes proceed to + // commit -- the order is banked either way, so a declined order is not lost; the actual HOLD is + // enforced in ContentAdmission (the drain waits for consent or Wi-Fi and the order shows as + // Queued on the index, exactly as Kolibri already defers a busy-line order). The prompt's only + // job is to grant consent (on Continue) so the drain may start now. + org.appdevforall.k2go.networkpolicy.presentation.NetworkPolicyGate.guardHeavyStart( + requireActivity(), () -> commitLive(toDownload), () -> commitLive(toDownload)); + } + private void commitLive(List toDownload) { // The order is read off the view model here, on the main thread, and written // on the IO pool: SharedPreferences plus a foreground service start is small // but it is still disk at the moment of a tap. From 99144575b875abf3b65bc3f582a7d7d2bd3cdd85 Mon Sep 17 00:00:00 2001 From: Luis Guzman Date: Wed, 16 Sep 2026 17:51:02 -0600 Subject: [PATCH 10/18] K2GO-395 refactor(util): extract WebPath.pathOf as the single URL-to-path source isMapsPage, isKiwixPage and (new) isKolibriPage each carried an identical scheme/ query/fragment strip. Extract util/WebPath.pathOf and fold FqrController + Kiwix into it, so the in-WebView page check has one source, not three copies to drift (code-review finding). Pure string parsing, behavior unchanged. --- .../k2go/redesign/FqrController.java | 7 +---- .../k2go/redesign/KiwixManageController.java | 7 +---- .../org/appdevforall/k2go/util/WebPath.java | 26 +++++++++++++++++++ 3 files changed, 28 insertions(+), 12 deletions(-) create mode 100644 controller/app/src/main/java/org/appdevforall/k2go/util/WebPath.java diff --git a/controller/app/src/main/java/org/appdevforall/k2go/redesign/FqrController.java b/controller/app/src/main/java/org/appdevforall/k2go/redesign/FqrController.java index 718a2c230..48dd98a6a 100644 --- a/controller/app/src/main/java/org/appdevforall/k2go/redesign/FqrController.java +++ b/controller/app/src/main/java/org/appdevforall/k2go/redesign/FqrController.java @@ -173,12 +173,7 @@ public void onPageFinished(String url) { /** True when the URL's path is the box's maps page. Pure string parsing (no android.net.Uri) so * it is unit-testable and dependency-free. */ static boolean isMapsPage(String url) { - if (url == null) return false; - String u = url; - int hash = u.indexOf('#'); if (hash >= 0) u = u.substring(0, hash); - int q = u.indexOf('?'); if (q >= 0) u = u.substring(0, q); - int scheme = u.indexOf("://"); - if (scheme >= 0) { int slash = u.indexOf('/', scheme + 3); u = slash >= 0 ? u.substring(slash) : "/"; } + String u = org.appdevforall.k2go.util.WebPath.pathOf(url); // K2GO-395: one URL->path source return u.equals("/maps/") || u.equals("/maps"); } diff --git a/controller/app/src/main/java/org/appdevforall/k2go/redesign/KiwixManageController.java b/controller/app/src/main/java/org/appdevforall/k2go/redesign/KiwixManageController.java index bb20304cf..3489aeba6 100644 --- a/controller/app/src/main/java/org/appdevforall/k2go/redesign/KiwixManageController.java +++ b/controller/app/src/main/java/org/appdevforall/k2go/redesign/KiwixManageController.java @@ -115,12 +115,7 @@ public void detach() { /** True when the URL's path is under the box's kiwix reader. Pure string parsing (no * android.net.Uri) so it is unit-testable and dependency-free. */ static boolean isKiwixPage(String url) { - if (url == null) return false; - String u = url; - int hash = u.indexOf('#'); if (hash >= 0) u = u.substring(0, hash); - int q = u.indexOf('?'); if (q >= 0) u = u.substring(0, q); - int scheme = u.indexOf("://"); - if (scheme >= 0) { int slash = u.indexOf('/', scheme + 3); u = slash >= 0 ? u.substring(slash) : "/"; } + String u = org.appdevforall.k2go.util.WebPath.pathOf(url); // K2GO-395: one URL->path source return u.equals("/kiwix") || u.startsWith("/kiwix/"); } diff --git a/controller/app/src/main/java/org/appdevforall/k2go/util/WebPath.java b/controller/app/src/main/java/org/appdevforall/k2go/util/WebPath.java new file mode 100644 index 000000000..d283934a1 --- /dev/null +++ b/controller/app/src/main/java/org/appdevforall/k2go/util/WebPath.java @@ -0,0 +1,26 @@ +package org.appdevforall.k2go.util; + +/** + * The single source for turning a URL into its path by pure string parsing -- no + * {@code android.net.Uri}, so it is dependency-free and unit-testable. + * + *

Strips the {@code #fragment}, the {@code ?query} and the {@code scheme://host}, + * returning the path. Returns {@code "/"} when a scheme is present but there is no + * path, and {@code ""} for a null URL. The in-WebView controllers (FQR maps, Kiwix + * manage, Kolibri guard -- K2GO-395) all use this to decide "is this the box's + * /maps/ , /kiwix/ or /kolibri/ page", instead of each carrying its own copy. + */ +public final class WebPath { + + private WebPath() {} + + public static String pathOf(String url) { + if (url == null) return ""; + String u = url; + int hash = u.indexOf('#'); if (hash >= 0) u = u.substring(0, hash); + int q = u.indexOf('?'); if (q >= 0) u = u.substring(0, q); + int scheme = u.indexOf("://"); + if (scheme >= 0) { int slash = u.indexOf('/', scheme + 3); u = slash >= 0 ? u.substring(slash) : "/"; } + return u; + } +} From d7a150c6b189aad6ffc7e077f31e89ee58a4e367 Mon Sep 17 00:00:00 2001 From: Luis Guzman Date: Wed, 16 Sep 2026 17:51:02 -0600 Subject: [PATCH 11/18] K2GO-395 feat(networkpolicy): gate the native Kolibri import via WebView interception Kolibri's own web app (box /kolibri/ in the PortalActivity WebView) imports content straight from Studio over metered data, bypassing Get More / ContentAdmission -- device recon reproduced a 265 MB import starting with no prompt. KolibriGuardController (armed on /kolibri/, mirroring FqrController) injects JS that parks a POST /api/tasks/tasks/ remote-import behind NetworkPolicyGate via the K2GoKolibri bridge, proceeding or aborting on consent. Second-pass review fixes: commitLive bails if detached; gateImport resolves if the prompt cannot show (no hung request); the hook warns on any unclassified task POST (fail-open regression signal). ADR-395 sec.4.3 records the seam and caveats. --- .../org/appdevforall/k2go/PortalActivity.java | 8 + .../presentation/KolibriConfirmFragment.java | 4 + .../k2go/redesign/KolibriGuardController.java | 143 ++++++++++++++++++ .../docs/ADR-395-network-cost-consent.md | 31 +++- 4 files changed, 182 insertions(+), 4 deletions(-) create mode 100644 controller/app/src/main/java/org/appdevforall/k2go/redesign/KolibriGuardController.java diff --git a/controller/app/src/main/java/org/appdevforall/k2go/PortalActivity.java b/controller/app/src/main/java/org/appdevforall/k2go/PortalActivity.java index 65b05994d..fb0fbcc5e 100644 --- a/controller/app/src/main/java/org/appdevforall/k2go/PortalActivity.java +++ b/controller/app/src/main/java/org/appdevforall/k2go/PortalActivity.java @@ -72,6 +72,7 @@ public class PortalActivity extends AppCompatActivity { private GestureWebView webView; private org.appdevforall.k2go.redesign.FqrController fqr; // ADFA-4879: FQR maps (only on /maps/) private org.appdevforall.k2go.redesign.KiwixManageController kiwixMgr; // ADFA-5004: ZIM delete (only on /kiwix/) + private org.appdevforall.k2go.redesign.KolibriGuardController kolibriGuard; // K2GO-395: metered gate for native Kolibri import (only on /kolibri/) private static final long AUTO_HIDE_MS = 4000L; // ADFA-4887: nav-bar auto-hide after inactivity private boolean fullscreenOn = false; // ADFA-4887: Home button toggles fullscreen private Handler hideHandler; // ADFA-4887: nav-bar auto-hide (cleared in onDestroy) @@ -224,6 +225,7 @@ public boolean shouldOverrideUrlLoading(WebView view, android.webkit.WebResource // Internal server link stays in the WebView (and travels through the proxy). if (NavigationPolicy.isInternalHost(host)) { if (fqr != null) fqr.prepareForUrl(url); // ADFA-4879: add the FQR bridge only on /maps/ + if (kolibriGuard != null) kolibriGuard.prepareForUrl(url); // K2GO-395: Kolibri gate only on /kolibri/ return false; } @@ -259,6 +261,7 @@ public void onPageFinished(WebView view, String url) { if (fqr != null) fqr.onPageFinished(url); // ADFA-5004: arm/disarm in-app ZIM manager depending on whether this is /kiwix/. if (kiwixMgr != null) kiwixMgr.onPageFinished(url); + if (kolibriGuard != null) kolibriGuard.onPageFinished(url); // K2GO-395: arm the Kolibri import gate } @Override @@ -371,6 +374,10 @@ public boolean onConsoleMessage(android.webkit.ConsoleMessage consoleMessage) { // (gated in KiwixManageController#onPageFinished). kiwixMgr = new org.appdevforall.k2go.redesign.KiwixManageController(this, webView); + // K2GO-395 (ADR-395): metered-cost gate for the NATIVE Kolibri import, active only on /kolibri/. + kolibriGuard = new org.appdevforall.k2go.redesign.KolibriGuardController(this, webView); + kolibriGuard.prepareForUrl(finalTargetUrl); + // ADFA-5043: Books (Calibre-Web) / Courses (Kolibri) auto-login as box admin — fetch a session // cookie, inject it into the WebView CookieManager, THEN load, so the card opens already // authenticated. Degrades gracefully: if the service isn't installed/ready, just load without it. @@ -533,6 +540,7 @@ protected void onDestroy() { // The durable server job (if any) keeps running and shows up on the next /maps/ reload. if (fqr != null) fqr.detach(); if (kiwixMgr != null) kiwixMgr.detach(); // ADFA-5004 + if (kolibriGuard != null) kolibriGuard.detach(); // K2GO-395 if (hideHandler != null && hideRunnable != null) hideHandler.removeCallbacks(hideRunnable); // ADFA-4887 super.onDestroy(); } diff --git a/controller/app/src/main/java/org/appdevforall/k2go/kolibri/presentation/KolibriConfirmFragment.java b/controller/app/src/main/java/org/appdevforall/k2go/kolibri/presentation/KolibriConfirmFragment.java index f9bb373ab..9d40f4212 100644 --- a/controller/app/src/main/java/org/appdevforall/k2go/kolibri/presentation/KolibriConfirmFragment.java +++ b/controller/app/src/main/java/org/appdevforall/k2go/kolibri/presentation/KolibriConfirmFragment.java @@ -393,6 +393,10 @@ private void startLive(List chosen) { } private void commitLive(List toDownload) { + // K2GO-395: commitLive is deferred behind the metered gate dialog, so it can run after the + // fragment detaches (rotation / navigation). Bail if we are no longer attached, mirroring + // finishStart, before touching requireContext()/requireActivity() below. + if (!isAdded() || getActivity() == null) return; // The order is read off the view model here, on the main thread, and written // on the IO pool: SharedPreferences plus a foreground service start is small // but it is still disk at the moment of a tap. diff --git a/controller/app/src/main/java/org/appdevforall/k2go/redesign/KolibriGuardController.java b/controller/app/src/main/java/org/appdevforall/k2go/redesign/KolibriGuardController.java new file mode 100644 index 000000000..9b38e010b --- /dev/null +++ b/controller/app/src/main/java/org/appdevforall/k2go/redesign/KolibriGuardController.java @@ -0,0 +1,143 @@ +/* + * ============================================================================ + * Name : KolibriGuardController.java + * Author : AppDevForAll + * Copyright : Copyright (c) 2026 AppDevForAll + * Description : K2GO-395 (ADR-395). Metered-cost gate for the NATIVE Kolibri app. + * + * Kolibri runs in the rootfs and is shown in the PortalActivity WebView at the box + * /kolibri/ page. It has its OWN content-import (download) manager, which our Get More + * flow and ContentAdmission do NOT cover: tapping "Import" in Kolibri's own UI starts a + * server-side download from Kolibri Studio over the device's metered link, with no + * consent prompt. Device recon (K2GO-395): the import is enqueued by + * POST /api/tasks/tasks/ (referer /kolibri/en/device/) + * and cancelled by POST /api/tasks/tasks//cancel/. The task list polls + * GET /api/tasks/tasks/?queue=content. + * + * Lifecycle mirrors FqrController / KiwixManageController: PortalActivity forwards + * prepareForUrl(url) (add the JS bridge only on /kolibri/), onPageFinished(url) (inject + * the hook on the kolibri page), and onDestroy -> detach(). + * + * How it gates: the injected hook wraps XMLHttpRequest (axios, which Kolibri uses) and + * fetch. A POST to /api/tasks/tasks/ that carries a REMOTE import task (heuristic on the + * body: contains "remote"/"channelupdate", not a local "disk" import) is PAUSED; the hook + * calls the native bridge K2GoKolibri.gateImport(reqId, body), which runs + * NetworkPolicyGate.guardHeavyStart on the UI thread and resolves back via + * evaluateJavascript(window.__k2goKolibriResolve) -- proceed on consent (or unmetered / + * already-consented, which resolves instantly), abort on decline. A task POST that is NOT + * classified as a remote import is logged (console.warn -> logcat) so a Kolibri task-API + * change is visible rather than a silent miss. + * + * The task-API shape is Kolibri internal and may change across versions -- the hook is + * intentionally narrow and fails OPEN (any error lets the request through) so a Kolibri + * change never bricks import; the console.warn above is the regression signal. gateImport + * also resolves if the prompt cannot be shown, so a parked request is never left hanging. + * ============================================================================ + */ +package org.appdevforall.k2go.redesign; + +import android.app.Activity; +import android.webkit.JavascriptInterface; +import android.webkit.WebView; + +import org.appdevforall.k2go.networkpolicy.presentation.NetworkPolicyGate; +import org.appdevforall.k2go.util.WebPath; + +public final class KolibriGuardController { + + private final Activity activity; + private final WebView webView; + + public KolibriGuardController(Activity activity, WebView webView) { + this.activity = activity; + this.webView = webView; + } + + /** Add the JS bridge only for the /kolibri/ page and remove it elsewhere (defence in depth: the + * box is trusted, but keep the interface off every other page). Takes effect on the next load. */ + public void prepareForUrl(String url) { + if (isKolibriPage(url)) webView.addJavascriptInterface(this, "K2GoKolibri"); + else webView.removeJavascriptInterface("K2GoKolibri"); + } + + /** Inject the fetch/XHR hook once the kolibri page has loaded. Idempotent in-page. */ + public void onPageFinished(String url) { + if (isKolibriPage(url)) webView.evaluateJavascript(HOOK_JS, null); + } + + /** Host is going away: drop the bridge. The hook lives in the page's JS context and dies with it. */ + public void detach() { + webView.removeJavascriptInterface("K2GoKolibri"); + } + + /** + * Called from the page hook (binder thread) when a Kolibri remote-import POST is parked. Shows the + * consent prompt on the UI thread and resolves the parked request back in JS: proceed on consent + * (or unmetered / already-consented), abort on decline. If the prompt cannot be shown (activity + * finishing), resolves proceed so the request is never left parked -- consistent with fail-open. + */ + @JavascriptInterface + public void gateImport(String reqId, String body) { + if (reqId == null) return; + final String id = reqId; + activity.runOnUiThread(() -> { + try { + NetworkPolicyGate.guardHeavyStart(activity, () -> resolve(id, true), () -> resolve(id, false)); + } catch (RuntimeException e) { + resolve(id, true); + } + }); + } + + private void resolve(String reqId, boolean granted) { + // Only the id we minted in JS (alphanumeric + '_'); never interpolate arbitrary text. + if (reqId == null || !reqId.matches("[A-Za-z0-9_]+")) return; + webView.evaluateJavascript( + "window.__k2goKolibriResolve&&window.__k2goKolibriResolve('" + reqId + "'," + granted + ");", + null); + } + + /** True when the URL path is a box Kolibri page. */ + static boolean isKolibriPage(String url) { + String p = WebPath.pathOf(url); + return p.equals("/kolibri") || p.startsWith("/kolibri/"); + } + + // The page hook. Wraps XHR (axios) + fetch; parks a remote-import task POST behind the native gate, + // warns on any other task POST (regression signal), and fails OPEN on any error. + private static final String HOOK_JS = + "(function(){" + + "if(window.__k2goKolibriHooked)return;window.__k2goKolibriHooked=true;" + + "window.__k2goKolibriPending={};" + + "window.__k2goKolibriResolve=function(id,g){var p=window.__k2goKolibriPending[id];if(!p)return;" + + "delete window.__k2goKolibriPending[id];try{p(g);}catch(e){}};" + + "function isTaskPost(m,u){try{" + + "if(!m||(''+m).toUpperCase()!=='POST')return false;if(!u)return false;" + + "var s=(''+u).split('?')[0].split('#')[0];if(s.charAt(s.length-1)==='/')s=s.substring(0,s.length-1);" + + "return s.endsWith('/api/tasks/tasks');" + + "}catch(e){return false;}}" + + "function isRemoteImport(b){var t=(typeof b==='string')?b:'';return /remote|channelupdate/i.test(t)&&!/disk/i.test(t);}" + + "function park(body,proceed,cancel){" + + "var id='k'+Date.now()+'_'+Math.random().toString(36).slice(2);" + + "window.__k2goKolibriPending[id]=function(g){if(g)proceed();else cancel();};" + + "try{K2GoKolibri.gateImport(id,(typeof body==='string')?body:'');}catch(e){proceed();}}" + + "function classify(m,u,b,proceed,cancel){" + + "if(!isTaskPost(m,u))return false;" + + "if(isRemoteImport(b)){park(b,proceed,cancel);return true;}" + + "try{console.warn('K2Go-Kolibri: ungated task POST',(typeof b==='string')?b.slice(0,160):'');}catch(e){}" + + "return false;}" + + "try{var XO=XMLHttpRequest.prototype.open,XS=XMLHttpRequest.prototype.send;" + + "XMLHttpRequest.prototype.open=function(m,u){this.__k2m=m;this.__k2u=u;return XO.apply(this,arguments);};" + + "XMLHttpRequest.prototype.send=function(b){var self=this,a=arguments;" + + "if(classify(self.__k2m,self.__k2u,b,function(){XS.apply(self,a);}," + + "function(){try{self.abort();}catch(e){}try{self.dispatchEvent(new Event('error'));}catch(e){}}))return;" + + "return XS.apply(self,arguments);};}catch(e){}" + + "try{var OF=window.fetch;if(OF){window.fetch=function(i,n){" + + "var m=(n&&n.method)||(i&&i.method)||'GET';var u=(typeof i==='string')?i:((i&&i.url)||'');var b=n&&n.body;" + + "var handled=false,pr,rj;var p=new Promise(function(res,rej){pr=res;rj=rej;});" + + "handled=classify(m,u,(typeof b==='string')?b:'',function(){OF(i,n).then(pr,rj);}," + + "function(){rj(new DOMException('Canceled by network policy','AbortError'));});" + + "return handled?p:OF(i,n);};}}catch(e){}" + + "console.log('K2Go-Kolibri gate armed');" + + "})();"; +} diff --git a/controller/docs/ADR-395-network-cost-consent.md b/controller/docs/ADR-395-network-cost-consent.md index 2576f0fbb..1032b9d61 100644 --- a/controller/docs/ADR-395-network-cost-consent.md +++ b/controller/docs/ADR-395-network-cost-consent.md @@ -156,6 +156,28 @@ and not consented, either ask via the gate or set `request.setAllowedOverMetered(false)` so the system holds it for Wi-Fi. If unmetered or already consented, enqueue as today. +### 4.3 Native Kolibri import (WebView interception) + +A distinct egress the app does NOT own: Kolibri's OWN web app (served by the box at +`/kolibri/`, shown in the PortalActivity WebView) has its own content-import +manager. Tapping Import there starts a server-side download from Kolibri Studio +over the metered link, bypassing Get More / ContentAdmission entirely. Device recon +(reproduced on the OnePlus over a metered hotspot: a 265 MB import began downloading +with no prompt) identified the trigger: `POST /api/tasks/tasks/` (cancel is +`POST /api/tasks/tasks//cancel/`; the queue polls +`GET /api/tasks/tasks/?queue=content`). + +`KolibriGuardController` gates it, mirroring `FqrController`/`KiwixManageController`: +armed on the `/kolibri/` page, it injects JS that wraps XHR (axios) and fetch, +parks a remote-import task POST, calls the native bridge (`K2GoKolibri.gateImport`) +which runs `NetworkPolicyGate.guardHeavyStart`, and proceeds or aborts on the +result. Caveats (from the code-review second pass): it depends on Kolibri's internal +task API, so it fails OPEN (never bricks import) and logs a console warning on any +task POST it cannot classify (the regression signal); the remote-vs-local decision +is a body heuristic (`remote`/`channelupdate`, not `disk`) pending a task-type-field +match. This is the one seam that reaches into a third-party app's egress, so it is +inherently best-effort. + ## 5. Reference implementation status (this change) Landed as a compiling, tested starting point for the implementer: @@ -176,10 +198,11 @@ Landed as a compiling, tested starting point for the implementer: - Strings translated to all 33 locales (machine-generated, pending human review) in `values*/strings_networkpolicy.xml`; `strings_untranslated.xml` is clear. -Remaining to finish the contract: the Kolibri commit-point prompt (its drain is -already held; the ask needs care because the flow is async), the Maps seam -(re-locate post K2GO-394), the DownloadManager seam, and the two-`hasInternet` -fold. (l10n done pending review.) +Done since: the Books and Kolibri commit-point prompts, the FQR maps-region seam +(sec.4.1) and the native-Kolibri import gate (sec.4.3), all device-verified on a +metered hotspot. Remaining to finish the contract: the base Maps runrole install, +the rootfs-image install (aria2), the DownloadManager seam (OTA / portal), and the +two-`hasInternet` fold. (l10n done pending review.) ## 6. Device evidence appendix (dark surfaces flattened) From 2cb73623caaf948cd2325a4dc5f16c6a2033d57c Mon Sep 17 00:00:00 2001 From: Luis Guzman Date: Wed, 16 Sep 2026 18:00:41 -0600 Subject: [PATCH 12/18] K2GO-395 fix(networkpolicy): decline Kolibri import with a clean abort (no false disconnect) The declined-import path dispatched a synthetic 'error' event on the XHR after abort(), which Kolibri's axios layer read as a network drop -> it entered a false 'Disconnected from Kolibri' state and cancelled its own task polls (device-observed). abort() alone already notifies axios as a CanceledError (a user cancel), so drop the extra dispatch. Device-verified: Not now now cancels cleanly -- 0 disconnect errors, 0 task POST sent, Kolibri stays connected and keeps the selection. --- .../org/appdevforall/k2go/redesign/KolibriGuardController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controller/app/src/main/java/org/appdevforall/k2go/redesign/KolibriGuardController.java b/controller/app/src/main/java/org/appdevforall/k2go/redesign/KolibriGuardController.java index 9b38e010b..11ed6f1c8 100644 --- a/controller/app/src/main/java/org/appdevforall/k2go/redesign/KolibriGuardController.java +++ b/controller/app/src/main/java/org/appdevforall/k2go/redesign/KolibriGuardController.java @@ -130,7 +130,7 @@ static boolean isKolibriPage(String url) { + "XMLHttpRequest.prototype.open=function(m,u){this.__k2m=m;this.__k2u=u;return XO.apply(this,arguments);};" + "XMLHttpRequest.prototype.send=function(b){var self=this,a=arguments;" + "if(classify(self.__k2m,self.__k2u,b,function(){XS.apply(self,a);}," - + "function(){try{self.abort();}catch(e){}try{self.dispatchEvent(new Event('error'));}catch(e){}}))return;" + + "function(){try{self.abort();}catch(e){}}))return;" + "return XS.apply(self,arguments);};}catch(e){}" + "try{var OF=window.fetch;if(OF){window.fetch=function(i,n){" + "var m=(n&&n.method)||(i&&i.method)||'GET';var u=(typeof i==='string')?i:((i&&i.url)||'');var b=n&&n.body;" From 1e605e03372dcd98c4f76b4a52b048eba8fef1a7 Mon Sep 17 00:00:00 2001 From: Luis Guzman Date: Wed, 16 Sep 2026 18:54:17 -0600 Subject: [PATCH 13/18] K2GO-395 refactor(l10n): fold networkpolicy strings into strings.xml Move the 8 network-cost-consent strings from the per-locale strings_networkpolicy.xml files into strings.xml (all 34 locales) and delete the feature files. One string file per locale; strings_untranslated.xml stays the only external tracker, for in-flight WIP awaiting translation. A loose strings_.xml is harder to find than a single tracker: to locate a string you must first know which feature owns it, and the last feature added is the one you do not know to look in. CLAUDE.md is updated to require one strings.xml and forbid per-feature string files. --- CLAUDE.md | 23 +++++++++++-------- .../app/src/main/res/values-ar/strings.xml | 9 ++++++++ .../res/values-ar/strings_networkpolicy.xml | 12 ---------- .../app/src/main/res/values-az/strings.xml | 9 ++++++++ .../res/values-az/strings_networkpolicy.xml | 12 ---------- .../app/src/main/res/values-bg/strings.xml | 9 ++++++++ .../res/values-bg/strings_networkpolicy.xml | 12 ---------- .../app/src/main/res/values-bn/strings.xml | 9 ++++++++ .../res/values-bn/strings_networkpolicy.xml | 12 ---------- .../app/src/main/res/values-cs/strings.xml | 9 ++++++++ .../res/values-cs/strings_networkpolicy.xml | 12 ---------- .../app/src/main/res/values-de/strings.xml | 9 ++++++++ .../res/values-de/strings_networkpolicy.xml | 12 ---------- .../app/src/main/res/values-el/strings.xml | 9 ++++++++ .../res/values-el/strings_networkpolicy.xml | 12 ---------- .../app/src/main/res/values-es/strings.xml | 9 ++++++++ .../res/values-es/strings_networkpolicy.xml | 12 ---------- .../app/src/main/res/values-fa/strings.xml | 9 ++++++++ .../res/values-fa/strings_networkpolicy.xml | 12 ---------- .../app/src/main/res/values-fr/strings.xml | 9 ++++++++ .../res/values-fr/strings_networkpolicy.xml | 12 ---------- .../app/src/main/res/values-gu/strings.xml | 9 ++++++++ .../res/values-gu/strings_networkpolicy.xml | 12 ---------- .../app/src/main/res/values-hi/strings.xml | 9 ++++++++ .../res/values-hi/strings_networkpolicy.xml | 12 ---------- .../app/src/main/res/values-hu/strings.xml | 9 ++++++++ .../res/values-hu/strings_networkpolicy.xml | 12 ---------- .../app/src/main/res/values-in/strings.xml | 9 ++++++++ .../res/values-in/strings_networkpolicy.xml | 12 ---------- .../app/src/main/res/values-it/strings.xml | 9 ++++++++ .../res/values-it/strings_networkpolicy.xml | 12 ---------- .../app/src/main/res/values-ja/strings.xml | 9 ++++++++ .../res/values-ja/strings_networkpolicy.xml | 12 ---------- .../app/src/main/res/values-ko/strings.xml | 9 ++++++++ .../res/values-ko/strings_networkpolicy.xml | 12 ---------- .../app/src/main/res/values-lt/strings.xml | 9 ++++++++ .../res/values-lt/strings_networkpolicy.xml | 12 ---------- .../app/src/main/res/values-nl/strings.xml | 9 ++++++++ .../res/values-nl/strings_networkpolicy.xml | 12 ---------- .../app/src/main/res/values-no/strings.xml | 9 ++++++++ .../res/values-no/strings_networkpolicy.xml | 12 ---------- .../app/src/main/res/values-pl/strings.xml | 9 ++++++++ .../res/values-pl/strings_networkpolicy.xml | 12 ---------- .../app/src/main/res/values-pt/strings.xml | 9 ++++++++ .../res/values-pt/strings_networkpolicy.xml | 12 ---------- .../app/src/main/res/values-ro/strings.xml | 9 ++++++++ .../res/values-ro/strings_networkpolicy.xml | 12 ---------- .../src/main/res/values-ru-rRU/strings.xml | 9 ++++++++ .../values-ru-rRU/strings_networkpolicy.xml | 12 ---------- .../app/src/main/res/values-sk/strings.xml | 9 ++++++++ .../res/values-sk/strings_networkpolicy.xml | 12 ---------- .../app/src/main/res/values-sr/strings.xml | 9 ++++++++ .../res/values-sr/strings_networkpolicy.xml | 12 ---------- .../app/src/main/res/values-sw/strings.xml | 9 ++++++++ .../res/values-sw/strings_networkpolicy.xml | 12 ---------- .../app/src/main/res/values-ta/strings.xml | 9 ++++++++ .../res/values-ta/strings_networkpolicy.xml | 12 ---------- .../app/src/main/res/values-tr/strings.xml | 9 ++++++++ .../res/values-tr/strings_networkpolicy.xml | 12 ---------- .../app/src/main/res/values-uk/strings.xml | 9 ++++++++ .../res/values-uk/strings_networkpolicy.xml | 12 ---------- .../app/src/main/res/values-vi/strings.xml | 9 ++++++++ .../res/values-vi/strings_networkpolicy.xml | 12 ---------- .../app/src/main/res/values-yo/strings.xml | 9 ++++++++ .../res/values-yo/strings_networkpolicy.xml | 12 ---------- .../src/main/res/values-zh-rCN/strings.xml | 9 ++++++++ .../values-zh-rCN/strings_networkpolicy.xml | 12 ---------- .../app/src/main/res/values/strings.xml | 9 ++++++++ .../main/res/values/strings_networkpolicy.xml | 12 ---------- .../main/res/values/strings_untranslated.xml | 11 +++++---- 70 files changed, 325 insertions(+), 423 deletions(-) delete mode 100644 controller/app/src/main/res/values-ar/strings_networkpolicy.xml delete mode 100644 controller/app/src/main/res/values-az/strings_networkpolicy.xml delete mode 100644 controller/app/src/main/res/values-bg/strings_networkpolicy.xml delete mode 100644 controller/app/src/main/res/values-bn/strings_networkpolicy.xml delete mode 100644 controller/app/src/main/res/values-cs/strings_networkpolicy.xml delete mode 100644 controller/app/src/main/res/values-de/strings_networkpolicy.xml delete mode 100644 controller/app/src/main/res/values-el/strings_networkpolicy.xml delete mode 100644 controller/app/src/main/res/values-es/strings_networkpolicy.xml delete mode 100644 controller/app/src/main/res/values-fa/strings_networkpolicy.xml delete mode 100644 controller/app/src/main/res/values-fr/strings_networkpolicy.xml delete mode 100644 controller/app/src/main/res/values-gu/strings_networkpolicy.xml delete mode 100644 controller/app/src/main/res/values-hi/strings_networkpolicy.xml delete mode 100644 controller/app/src/main/res/values-hu/strings_networkpolicy.xml delete mode 100644 controller/app/src/main/res/values-in/strings_networkpolicy.xml delete mode 100644 controller/app/src/main/res/values-it/strings_networkpolicy.xml delete mode 100644 controller/app/src/main/res/values-ja/strings_networkpolicy.xml delete mode 100644 controller/app/src/main/res/values-ko/strings_networkpolicy.xml delete mode 100644 controller/app/src/main/res/values-lt/strings_networkpolicy.xml delete mode 100644 controller/app/src/main/res/values-nl/strings_networkpolicy.xml delete mode 100644 controller/app/src/main/res/values-no/strings_networkpolicy.xml delete mode 100644 controller/app/src/main/res/values-pl/strings_networkpolicy.xml delete mode 100644 controller/app/src/main/res/values-pt/strings_networkpolicy.xml delete mode 100644 controller/app/src/main/res/values-ro/strings_networkpolicy.xml delete mode 100644 controller/app/src/main/res/values-ru-rRU/strings_networkpolicy.xml delete mode 100644 controller/app/src/main/res/values-sk/strings_networkpolicy.xml delete mode 100644 controller/app/src/main/res/values-sr/strings_networkpolicy.xml delete mode 100644 controller/app/src/main/res/values-sw/strings_networkpolicy.xml delete mode 100644 controller/app/src/main/res/values-ta/strings_networkpolicy.xml delete mode 100644 controller/app/src/main/res/values-tr/strings_networkpolicy.xml delete mode 100644 controller/app/src/main/res/values-uk/strings_networkpolicy.xml delete mode 100644 controller/app/src/main/res/values-vi/strings_networkpolicy.xml delete mode 100644 controller/app/src/main/res/values-yo/strings_networkpolicy.xml delete mode 100644 controller/app/src/main/res/values-zh-rCN/strings_networkpolicy.xml delete mode 100644 controller/app/src/main/res/values/strings_networkpolicy.xml diff --git a/CLAUDE.md b/CLAUDE.md index 9681286a3..70793fe59 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -113,16 +113,19 @@ two layered migrations from colliding, follow these rules: - **Shared contracts land first.** If two features need a common domain type or port, define and merge that small interface on its own first, then both features build against it. Don't duplicate it on two branches. -- **Per-feature resource files** to avoid `strings.xml` collisions: a feature may - add its own `res/values/strings_.xml` (Android merges all `` - files) instead of everyone editing the one shared `strings.xml`. Append, never - reorder existing keys. **This is a temporary device for parallel work, not the - end state.** A `strings_.xml` is folded back into `strings.xml` (with all - 33 locale values) once the feature lands and the collision risk is gone; the - final tree should carry no loose `strings_.xml`. The one exception is - `strings_untranslated.xml`, the deliberate WIP tracker for strings awaiting - translation (see the l10n conventions). This holds until the policy changes to - keep per-feature string files permanently. +- **One place for strings; no per-feature string files.** All UI strings end in + `strings.xml` (with all locale values). Do NOT create `res/values/strings_.xml` + files. A scattered per-feature file is more dangerous than a single tracker: to + find a string you must first know which feature owns it, and the last feature + added is exactly the one you do not know to look in -- so strings get lost. The + ONLY external string file is `strings_untranslated.xml`: the single, always-known + WIP tracker for strings that are user-facing but not yet translated (see the l10n + conventions). Park a WIP string there while you work, then migrate it into + `strings.xml` (all locale values) before the PR merges. A PR closes fully + integrated, so `strings_untranslated.xml` should be near-empty when the next PR + opens -- never a growing pile. Edits to `strings.xml` stay additive and append-only + (never reorder existing keys) to keep parallel-branch collisions trivial to + resolve; discoverability wins over collision-avoidance. - **Wire dependencies by hand, per feature.** Each feature has its own `…ViewModelFactory` / small factory. There is no shared DI graph for everyone to edit (introducing Hilt/Dagger is a separate ADR), so composition roots don't diff --git a/controller/app/src/main/res/values-ar/strings.xml b/controller/app/src/main/res/values-ar/strings.xml index f171a2a8e..953d030e7 100644 --- a/controller/app/src/main/res/values-ar/strings.xml +++ b/controller/app/src/main/res/values-ar/strings.xml @@ -1330,4 +1330,13 @@ احتوى K2Go نشاطًا غير معتاد تعامل K2Go مع عملية كانت تستخدم مساحة تخزين كبيرة وأبقى نظامك يعمل. انقر لإرسال تقرير. جارٍ تنزيل بيانات الخريطة + + يبدو أنك تستخدم بيانات الجوال + يستخدم هذا التنزيل بيانات الجوال. قد يترتب عليه تكلفة أو يستنفد باقة بياناتك. هل تريد المتابعة؟ + المتابعة عبر البيانات + ليس الآن + لا توجد شبكة. اتصل بشبكة Wi-Fi للتنزيل. + يبدو أنك انتقلت إلى بيانات الجوال + تستخدم التنزيلات الجديدة الآن بيانات الجوال. قد يترتب على ذلك تكلفة أو يستنفد باقة بياناتك. + تنبيهات تكلفة البيانات diff --git a/controller/app/src/main/res/values-ar/strings_networkpolicy.xml b/controller/app/src/main/res/values-ar/strings_networkpolicy.xml deleted file mode 100644 index 937e29f0a..000000000 --- a/controller/app/src/main/res/values-ar/strings_networkpolicy.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - يبدو أنك تستخدم بيانات الجوال - يستخدم هذا التنزيل بيانات الجوال. قد يترتب عليه تكلفة أو يستنفد باقة بياناتك. هل تريد المتابعة؟ - المتابعة عبر البيانات - ليس الآن - لا توجد شبكة. اتصل بشبكة Wi-Fi للتنزيل. - يبدو أنك انتقلت إلى بيانات الجوال - تستخدم التنزيلات الجديدة الآن بيانات الجوال. قد يترتب على ذلك تكلفة أو يستنفد باقة بياناتك. - تنبيهات تكلفة البيانات - diff --git a/controller/app/src/main/res/values-az/strings.xml b/controller/app/src/main/res/values-az/strings.xml index 58c4086ba..392dc14ee 100644 --- a/controller/app/src/main/res/values-az/strings.xml +++ b/controller/app/src/main/res/values-az/strings.xml @@ -1350,4 +1350,13 @@ K2Go qeyri-adi fəaliyyəti cilovladı K2Go çox yaddaş istifadə edən prosesi idarə etdi və sisteminizi işlək saxladı. Hesabat göndərmək üçün toxunun. Xəritə məlumatları yüklənir + + Görünür, mobil internetdən istifadə edirsiniz + Bu yükləmə mobil internetdən istifadə edir. Bu, xərc yarada və ya data paketinizi bitirə bilər. Davam etmək istəyirsiniz? + Mobil internetlə davam et + İndi yox + Şəbəkə yoxdur. Yükləmək üçün Wi-Fi-a qoşulun. + Deyəsən, mobil internetə keçdiniz + Yeni yükləmələr indi mobil internetdən istifadə edir. Bu, xərc yarada və ya data paketinizi bitirə bilər. + Data xərci bildirişləri diff --git a/controller/app/src/main/res/values-az/strings_networkpolicy.xml b/controller/app/src/main/res/values-az/strings_networkpolicy.xml deleted file mode 100644 index f2006938b..000000000 --- a/controller/app/src/main/res/values-az/strings_networkpolicy.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - Görünür, mobil internetdən istifadə edirsiniz - Bu yükləmə mobil internetdən istifadə edir. Bu, xərc yarada və ya data paketinizi bitirə bilər. Davam etmək istəyirsiniz? - Mobil internetlə davam et - İndi yox - Şəbəkə yoxdur. Yükləmək üçün Wi-Fi-a qoşulun. - Deyəsən, mobil internetə keçdiniz - Yeni yükləmələr indi mobil internetdən istifadə edir. Bu, xərc yarada və ya data paketinizi bitirə bilər. - Data xərci bildirişləri - diff --git a/controller/app/src/main/res/values-bg/strings.xml b/controller/app/src/main/res/values-bg/strings.xml index ab2a81d53..1b48dcec9 100644 --- a/controller/app/src/main/res/values-bg/strings.xml +++ b/controller/app/src/main/res/values-bg/strings.xml @@ -1337,4 +1337,13 @@ K2Go ограничи необичайна активност K2Go се справи с процес, който използваше твърде много хранилище, и запази системата ви работеща. Докоснете, за да изпратите отчет. Изтегляне на данни за картата + + Изглежда използвате мобилни данни + Това изтегляне използва мобилни данни. Може да има такса или да изчерпи плана ви за данни. Искате ли да продължите? + Продължи с мобилни данни + Не сега + Няма мрежа. Свържете се с Wi-Fi, за да изтеглите. + Изглежда превключихте към мобилни данни + Новите изтегляния вече използват мобилни данни. Това може да има такса или да изчерпи плана ви за данни. + Известия за разход на данни diff --git a/controller/app/src/main/res/values-bg/strings_networkpolicy.xml b/controller/app/src/main/res/values-bg/strings_networkpolicy.xml deleted file mode 100644 index 1905d195d..000000000 --- a/controller/app/src/main/res/values-bg/strings_networkpolicy.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - Изглежда използвате мобилни данни - Това изтегляне използва мобилни данни. Може да има такса или да изчерпи плана ви за данни. Искате ли да продължите? - Продължи с мобилни данни - Не сега - Няма мрежа. Свържете се с Wi-Fi, за да изтеглите. - Изглежда превключихте към мобилни данни - Новите изтегляния вече използват мобилни данни. Това може да има такса или да изчерпи плана ви за данни. - Известия за разход на данни - diff --git a/controller/app/src/main/res/values-bn/strings.xml b/controller/app/src/main/res/values-bn/strings.xml index 79a8ffa40..05f199c82 100644 --- a/controller/app/src/main/res/values-bn/strings.xml +++ b/controller/app/src/main/res/values-bn/strings.xml @@ -1343,4 +1343,13 @@ K2Go অস্বাভাবিক কার্যকলাপ নিয়ন্ত্রণ করেছে K2Go অত্যধিক স্টোরেজ ব্যবহার করা একটি প্রক্রিয়া সামলেছে এবং আপনার সিস্টেম চালু রেখেছে। রিপোর্ট পাঠাতে ট্যাপ করুন। মানচিত্রের ডেটা ডাউনলোড হচ্ছে + + মনে হচ্ছে আপনি মোবাইল ডেটা ব্যবহার করছেন + এই ডাউনলোডটি মোবাইল ডেটা ব্যবহার করে। এতে খরচ হতে পারে বা আপনার ডেটা প্ল্যান শেষ হতে পারে। আপনি কি চালিয়ে যেতে চান? + ডেটা দিয়ে চালিয়ে যান + এখন নয় + কোনো নেটওয়ার্ক নেই। ডাউনলোড করতে Wi-Fi এ সংযোগ করুন। + মনে হচ্ছে আপনি মোবাইল ডেটাতে স্যুইচ করেছেন + নতুন ডাউনলোডগুলি এখন মোবাইল ডেটা ব্যবহার করে। এতে খরচ হতে পারে বা আপনার ডেটা প্ল্যান শেষ হতে পারে। + ডেটা খরচের সতর্কতা diff --git a/controller/app/src/main/res/values-bn/strings_networkpolicy.xml b/controller/app/src/main/res/values-bn/strings_networkpolicy.xml deleted file mode 100644 index ccf96117d..000000000 --- a/controller/app/src/main/res/values-bn/strings_networkpolicy.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - মনে হচ্ছে আপনি মোবাইল ডেটা ব্যবহার করছেন - এই ডাউনলোডটি মোবাইল ডেটা ব্যবহার করে। এতে খরচ হতে পারে বা আপনার ডেটা প্ল্যান শেষ হতে পারে। আপনি কি চালিয়ে যেতে চান? - ডেটা দিয়ে চালিয়ে যান - এখন নয় - কোনো নেটওয়ার্ক নেই। ডাউনলোড করতে Wi-Fi এ সংযোগ করুন। - মনে হচ্ছে আপনি মোবাইল ডেটাতে স্যুইচ করেছেন - নতুন ডাউনলোডগুলি এখন মোবাইল ডেটা ব্যবহার করে। এতে খরচ হতে পারে বা আপনার ডেটা প্ল্যান শেষ হতে পারে। - ডেটা খরচের সতর্কতা - diff --git a/controller/app/src/main/res/values-cs/strings.xml b/controller/app/src/main/res/values-cs/strings.xml index e5dbc903e..730e67b0c 100644 --- a/controller/app/src/main/res/values-cs/strings.xml +++ b/controller/app/src/main/res/values-cs/strings.xml @@ -1337,4 +1337,13 @@ K2Go zvládl neobvyklou aktivitu K2Go zpracoval proces, který využíval příliš mnoho úložiště, a udržel systém v chodu. Klepnutím odešlete hlášení. Stahování mapových dat + + Zdá se, že používáte mobilní data + Toto stahování používá mobilní data. Může být zpoplatněno nebo vyčerpat váš datový limit. Chcete pokračovat? + Pokračovat na mobilních datech + Teď ne + Žádná síť. Připojte se k Wi-Fi pro stažení. + Zdá se, že jste přepnuli na mobilní data + Nová stahování nyní používají mobilní data. Může být zpoplatněno nebo vyčerpat váš datový limit. + Upozornění na náklady za data diff --git a/controller/app/src/main/res/values-cs/strings_networkpolicy.xml b/controller/app/src/main/res/values-cs/strings_networkpolicy.xml deleted file mode 100644 index 4321d35de..000000000 --- a/controller/app/src/main/res/values-cs/strings_networkpolicy.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - Zdá se, že používáte mobilní data - Toto stahování používá mobilní data. Může být zpoplatněno nebo vyčerpat váš datový limit. Chcete pokračovat? - Pokračovat na mobilních datech - Teď ne - Žádná síť. Připojte se k Wi-Fi pro stažení. - Zdá se, že jste přepnuli na mobilní data - Nová stahování nyní používají mobilní data. Může být zpoplatněno nebo vyčerpat váš datový limit. - Upozornění na náklady za data - diff --git a/controller/app/src/main/res/values-de/strings.xml b/controller/app/src/main/res/values-de/strings.xml index d0a5f84cd..9864d0b55 100644 --- a/controller/app/src/main/res/values-de/strings.xml +++ b/controller/app/src/main/res/values-de/strings.xml @@ -1330,4 +1330,13 @@ K2Go hat ungewöhnliche Aktivität eingedämmt K2Go hat einen Prozess bewältigt, der zu viel Speicher belegte, und Ihr System am Laufen gehalten. Tippen, um einen Bericht zu senden. Kartendaten werden heruntergeladen + + Sie scheinen mobile Daten zu nutzen + Dieser Download nutzt mobile Daten. Das kann Kosten verursachen oder Ihr Datenvolumen aufbrauchen. Möchten Sie fortfahren? + Mit mobilen Daten fortfahren + Nicht jetzt + Kein Netzwerk. Verbinden Sie sich mit WLAN, um herunterzuladen. + Sie scheinen zu mobilen Daten gewechselt zu haben + Neue Downloads nutzen jetzt mobile Daten. Das kann Kosten verursachen oder Ihr Datenvolumen aufbrauchen. + Warnungen zu Datenkosten diff --git a/controller/app/src/main/res/values-de/strings_networkpolicy.xml b/controller/app/src/main/res/values-de/strings_networkpolicy.xml deleted file mode 100644 index be4c16dfe..000000000 --- a/controller/app/src/main/res/values-de/strings_networkpolicy.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - Sie scheinen mobile Daten zu nutzen - Dieser Download nutzt mobile Daten. Das kann Kosten verursachen oder Ihr Datenvolumen aufbrauchen. Möchten Sie fortfahren? - Mit mobilen Daten fortfahren - Nicht jetzt - Kein Netzwerk. Verbinden Sie sich mit WLAN, um herunterzuladen. - Sie scheinen zu mobilen Daten gewechselt zu haben - Neue Downloads nutzen jetzt mobile Daten. Das kann Kosten verursachen oder Ihr Datenvolumen aufbrauchen. - Warnungen zu Datenkosten - diff --git a/controller/app/src/main/res/values-el/strings.xml b/controller/app/src/main/res/values-el/strings.xml index d65f4f652..7da76004e 100644 --- a/controller/app/src/main/res/values-el/strings.xml +++ b/controller/app/src/main/res/values-el/strings.xml @@ -1337,4 +1337,13 @@ Το K2Go περιόρισε ασυνήθιστη δραστηριότητα Το K2Go χειρίστηκε μια διεργασία που χρησιμοποιούσε υπερβολικό χώρο και κράτησε το σύστημά σας σε λειτουργία. Πατήστε για αποστολή αναφοράς. Λήψη δεδομένων χάρτη + + Φαίνεται ότι χρησιμοποιείτε δεδομένα κινητής + Αυτή η λήψη χρησιμοποιεί δεδομένα κινητής. Μπορεί να έχει κόστος ή να εξαντλήσει το πρόγραμμα δεδομένων σας. Θέλετε να συνεχίσετε; + Συνέχεια με δεδομένα + Όχι τώρα + Χωρίς δίκτυο. Συνδεθείτε σε Wi-Fi για λήψη. + Φαίνεται ότι μεταβήκατε σε δεδομένα κινητής + Οι νέες λήψεις χρησιμοποιούν τώρα δεδομένα κινητής. Αυτό μπορεί να έχει κόστος ή να εξαντλήσει το πρόγραμμα δεδομένων σας. + Ειδοποιήσεις κόστους δεδομένων diff --git a/controller/app/src/main/res/values-el/strings_networkpolicy.xml b/controller/app/src/main/res/values-el/strings_networkpolicy.xml deleted file mode 100644 index 5aae3107d..000000000 --- a/controller/app/src/main/res/values-el/strings_networkpolicy.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - Φαίνεται ότι χρησιμοποιείτε δεδομένα κινητής - Αυτή η λήψη χρησιμοποιεί δεδομένα κινητής. Μπορεί να έχει κόστος ή να εξαντλήσει το πρόγραμμα δεδομένων σας. Θέλετε να συνεχίσετε; - Συνέχεια με δεδομένα - Όχι τώρα - Χωρίς δίκτυο. Συνδεθείτε σε Wi-Fi για λήψη. - Φαίνεται ότι μεταβήκατε σε δεδομένα κινητής - Οι νέες λήψεις χρησιμοποιούν τώρα δεδομένα κινητής. Αυτό μπορεί να έχει κόστος ή να εξαντλήσει το πρόγραμμα δεδομένων σας. - Ειδοποιήσεις κόστους δεδομένων - diff --git a/controller/app/src/main/res/values-es/strings.xml b/controller/app/src/main/res/values-es/strings.xml index d2515fc75..c47d40fba 100644 --- a/controller/app/src/main/res/values-es/strings.xml +++ b/controller/app/src/main/res/values-es/strings.xml @@ -1406,4 +1406,13 @@ K2Go contuvo actividad inusual K2Go manejó un proceso que usaba demasiado almacenamiento y mantuvo tu sistema funcionando. Toca para enviar un reporte. Descargando datos del mapa + + Parece que estás usando datos móviles + Esta descarga usa datos móviles. Puede tener costo o agotar tu plan de datos. ¿Deseas continuar? + Continuar con datos + Ahora no + Sin red. Conéctate a Wi-Fi para descargar. + Parece que cambiaste a datos móviles + Las nuevas descargas ahora usan datos móviles. Esto puede tener costo o agotar tu plan de datos. + Alertas de costo de datos diff --git a/controller/app/src/main/res/values-es/strings_networkpolicy.xml b/controller/app/src/main/res/values-es/strings_networkpolicy.xml deleted file mode 100644 index ceddeae83..000000000 --- a/controller/app/src/main/res/values-es/strings_networkpolicy.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - Parece que estás usando datos móviles - Esta descarga usa datos móviles. Puede tener costo o agotar tu plan de datos. ¿Deseas continuar? - Continuar con datos - Ahora no - Sin red. Conéctate a Wi-Fi para descargar. - Parece que cambiaste a datos móviles - Las nuevas descargas ahora usan datos móviles. Esto puede tener costo o agotar tu plan de datos. - Alertas de costo de datos - diff --git a/controller/app/src/main/res/values-fa/strings.xml b/controller/app/src/main/res/values-fa/strings.xml index c955c953a..67a84e288 100644 --- a/controller/app/src/main/res/values-fa/strings.xml +++ b/controller/app/src/main/res/values-fa/strings.xml @@ -1330,4 +1330,13 @@ K2Go فعالیت غیرعادی را مهار کرد K2Go فرایندی را که فضای زیادی مصرف می‌کرد مدیریت کرد و سیستم شما را فعال نگه داشت. برای ارسال گزارش ضربه بزنید. در حال دانلود داده‌های نقشه + + به نظر می‌رسد از داده تلفن همراه استفاده می‌کنید + این دانلود از داده تلفن همراه استفاده می‌کند. ممکن است هزینه داشته باشد یا بسته داده شما را تمام کند. آیا می‌خواهید ادامه دهید؟ + ادامه با داده تلفن همراه + الان نه + شبکه‌ای وجود ندارد. برای دانلود به Wi-Fi متصل شوید. + به نظر می‌رسد به داده تلفن همراه تغییر داده‌اید + دانلودهای جدید اکنون از داده تلفن همراه استفاده می‌کنند. این ممکن است هزینه داشته باشد یا بسته داده شما را تمام کند. + هشدارهای هزینه داده diff --git a/controller/app/src/main/res/values-fa/strings_networkpolicy.xml b/controller/app/src/main/res/values-fa/strings_networkpolicy.xml deleted file mode 100644 index aa304ea79..000000000 --- a/controller/app/src/main/res/values-fa/strings_networkpolicy.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - به نظر می‌رسد از داده تلفن همراه استفاده می‌کنید - این دانلود از داده تلفن همراه استفاده می‌کند. ممکن است هزینه داشته باشد یا بسته داده شما را تمام کند. آیا می‌خواهید ادامه دهید؟ - ادامه با داده تلفن همراه - الان نه - شبکه‌ای وجود ندارد. برای دانلود به Wi-Fi متصل شوید. - به نظر می‌رسد به داده تلفن همراه تغییر داده‌اید - دانلودهای جدید اکنون از داده تلفن همراه استفاده می‌کنند. این ممکن است هزینه داشته باشد یا بسته داده شما را تمام کند. - هشدارهای هزینه داده - diff --git a/controller/app/src/main/res/values-fr/strings.xml b/controller/app/src/main/res/values-fr/strings.xml index 764bed170..4a174c258 100644 --- a/controller/app/src/main/res/values-fr/strings.xml +++ b/controller/app/src/main/res/values-fr/strings.xml @@ -1417,4 +1417,13 @@ K2Go a contenu une activité inhabituelle K2Go a géré un processus qui utilisait trop de stockage et a gardé votre système en marche. Appuyez pour envoyer un rapport. Téléchargement des données cartographiques + + Vous semblez utiliser les données mobiles + Ce téléchargement utilise les données mobiles. Il peut coûter cher ou épuiser votre forfait de données. Voulez-vous continuer ? + Continuer avec les données + Pas maintenant + Aucun réseau. Connectez-vous au Wi-Fi pour télécharger. + Vous semblez être passé aux données mobiles + Les nouveaux téléchargements utilisent désormais les données mobiles. Cela peut coûter cher ou épuiser votre forfait de données. + Alertes de coût des données diff --git a/controller/app/src/main/res/values-fr/strings_networkpolicy.xml b/controller/app/src/main/res/values-fr/strings_networkpolicy.xml deleted file mode 100644 index 9135fe4bd..000000000 --- a/controller/app/src/main/res/values-fr/strings_networkpolicy.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - Vous semblez utiliser les données mobiles - Ce téléchargement utilise les données mobiles. Il peut coûter cher ou épuiser votre forfait de données. Voulez-vous continuer ? - Continuer avec les données - Pas maintenant - Aucun réseau. Connectez-vous au Wi-Fi pour télécharger. - Vous semblez être passé aux données mobiles - Les nouveaux téléchargements utilisent désormais les données mobiles. Cela peut coûter cher ou épuiser votre forfait de données. - Alertes de coût des données - diff --git a/controller/app/src/main/res/values-gu/strings.xml b/controller/app/src/main/res/values-gu/strings.xml index 1303442e0..18f946245 100644 --- a/controller/app/src/main/res/values-gu/strings.xml +++ b/controller/app/src/main/res/values-gu/strings.xml @@ -1343,4 +1343,13 @@ K2Go એ અસામાન્ય પ્રવૃત્તિ કાબૂમાં લીધી K2Go એ વધુ પડતું સ્ટોરેજ વાપરતી પ્રક્રિયા સંભાળી અને તમારી સિસ્ટમ ચાલુ રાખી. રિપોર્ટ મોકલવા ટૅપ કરો. નકશા ડેટા ડાઉનલોડ થઈ રહ્યો છે + + એવું લાગે છે કે તમે મોબાઇલ ડેટા વાપરી રહ્યા છો + આ ડાઉનલોડ મોબાઇલ ડેટા વાપરે છે. તેનાથી ખર્ચ થઈ શકે અથવા તમારો ડેટા પ્લાન ખતમ થઈ શકે. શું તમે ચાલુ રાખવા માંગો છો? + ડેટા પર ચાલુ રાખો + હમણાં નહીં + કોઈ નેટવર્ક નથી. ડાઉનલોડ કરવા માટે Wi-Fi સાથે કનેક્ટ કરો. + એવું લાગે છે કે તમે મોબાઇલ ડેટા પર સ્વિચ કર્યું + નવા ડાઉનલોડ હવે મોબાઇલ ડેટા વાપરે છે. તેનાથી ખર્ચ થઈ શકે અથવા તમારો ડેટા પ્લાન ખતમ થઈ શકે. + ડેટા ખર્ચ ચેતવણીઓ diff --git a/controller/app/src/main/res/values-gu/strings_networkpolicy.xml b/controller/app/src/main/res/values-gu/strings_networkpolicy.xml deleted file mode 100644 index 5abd087e0..000000000 --- a/controller/app/src/main/res/values-gu/strings_networkpolicy.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - એવું લાગે છે કે તમે મોબાઇલ ડેટા વાપરી રહ્યા છો - આ ડાઉનલોડ મોબાઇલ ડેટા વાપરે છે. તેનાથી ખર્ચ થઈ શકે અથવા તમારો ડેટા પ્લાન ખતમ થઈ શકે. શું તમે ચાલુ રાખવા માંગો છો? - ડેટા પર ચાલુ રાખો - હમણાં નહીં - કોઈ નેટવર્ક નથી. ડાઉનલોડ કરવા માટે Wi-Fi સાથે કનેક્ટ કરો. - એવું લાગે છે કે તમે મોબાઇલ ડેટા પર સ્વિચ કર્યું - નવા ડાઉનલોડ હવે મોબાઇલ ડેટા વાપરે છે. તેનાથી ખર્ચ થઈ શકે અથવા તમારો ડેટા પ્લાન ખતમ થઈ શકે. - ડેટા ખર્ચ ચેતવણીઓ - diff --git a/controller/app/src/main/res/values-hi/strings.xml b/controller/app/src/main/res/values-hi/strings.xml index 7beb91b0a..352d3acc8 100644 --- a/controller/app/src/main/res/values-hi/strings.xml +++ b/controller/app/src/main/res/values-hi/strings.xml @@ -1407,4 +1407,13 @@ K2Go ने असामान्य गतिविधि को रोका K2Go ने बहुत अधिक स्टोरेज उपयोग कर रही प्रक्रिया को संभाला और आपका सिस्टम चालू रखा। रिपोर्ट भेजने के लिए टैप करें। मानचित्र डेटा डाउनलोड हो रहा है + + ऐसा लगता है कि आप मोबाइल डेटा का उपयोग कर रहे हैं + यह डाउनलोड मोबाइल डेटा का उपयोग करता है. इससे शुल्क लग सकता है या आपका डेटा प्लान खत्म हो सकता है. क्या आप जारी रखना चाहते हैं? + डेटा पर जारी रखें + अभी नहीं + कोई नेटवर्क नहीं. डाउनलोड करने के लिए Wi-Fi से कनेक्ट करें. + ऐसा लगता है कि आप मोबाइल डेटा पर स्विच हो गए + नए डाउनलोड अब मोबाइल डेटा का उपयोग करते हैं. इससे शुल्क लग सकता है या आपका डेटा प्लान खत्म हो सकता है. + डेटा लागत अलर्ट diff --git a/controller/app/src/main/res/values-hi/strings_networkpolicy.xml b/controller/app/src/main/res/values-hi/strings_networkpolicy.xml deleted file mode 100644 index 6d14e350c..000000000 --- a/controller/app/src/main/res/values-hi/strings_networkpolicy.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - ऐसा लगता है कि आप मोबाइल डेटा का उपयोग कर रहे हैं - यह डाउनलोड मोबाइल डेटा का उपयोग करता है. इससे शुल्क लग सकता है या आपका डेटा प्लान खत्म हो सकता है. क्या आप जारी रखना चाहते हैं? - डेटा पर जारी रखें - अभी नहीं - कोई नेटवर्क नहीं. डाउनलोड करने के लिए Wi-Fi से कनेक्ट करें. - ऐसा लगता है कि आप मोबाइल डेटा पर स्विच हो गए - नए डाउनलोड अब मोबाइल डेटा का उपयोग करते हैं. इससे शुल्क लग सकता है या आपका डेटा प्लान खत्म हो सकता है. - डेटा लागत अलर्ट - diff --git a/controller/app/src/main/res/values-hu/strings.xml b/controller/app/src/main/res/values-hu/strings.xml index 4b49263ae..2d9a6248e 100644 --- a/controller/app/src/main/res/values-hu/strings.xml +++ b/controller/app/src/main/res/values-hu/strings.xml @@ -1330,4 +1330,13 @@ A K2Go visszafogott egy szokatlan tevékenységet A K2Go kezelt egy túl sok tárhelyet használó folyamatot, és működésben tartotta a rendszert. Koppintson a jelentés küldéséhez. Térképadatok letöltése + + Úgy tűnik, mobiladatot használ + Ez a letöltés mobiladatot használ. Ez költséggel járhat, vagy elfogyaszthatja az adatkeretét. Folytatja? + Folytatás mobiladaton + Most nem + Nincs hálózat. Csatlakozzon Wi-Fi-hez a letöltéshez. + Úgy tűnik, átváltott mobiladatra + Az új letöltések most mobiladatot használnak. Ez költséggel járhat, vagy elfogyaszthatja az adatkeretét. + Adatköltség-figyelmeztetések diff --git a/controller/app/src/main/res/values-hu/strings_networkpolicy.xml b/controller/app/src/main/res/values-hu/strings_networkpolicy.xml deleted file mode 100644 index e20ccf660..000000000 --- a/controller/app/src/main/res/values-hu/strings_networkpolicy.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - Úgy tűnik, mobiladatot használ - Ez a letöltés mobiladatot használ. Ez költséggel járhat, vagy elfogyaszthatja az adatkeretét. Folytatja? - Folytatás mobiladaton - Most nem - Nincs hálózat. Csatlakozzon Wi-Fi-hez a letöltéshez. - Úgy tűnik, átváltott mobiladatra - Az új letöltések most mobiladatot használnak. Ez költséggel járhat, vagy elfogyaszthatja az adatkeretét. - Adatköltség-figyelmeztetések - diff --git a/controller/app/src/main/res/values-in/strings.xml b/controller/app/src/main/res/values-in/strings.xml index c0e35ee23..c18e29326 100644 --- a/controller/app/src/main/res/values-in/strings.xml +++ b/controller/app/src/main/res/values-in/strings.xml @@ -1337,4 +1337,13 @@ K2Go menahan aktivitas tidak biasa K2Go menangani proses yang menggunakan terlalu banyak penyimpanan dan menjaga sistem Anda tetap berjalan. Ketuk untuk mengirim laporan. Mengunduh data peta + + Anda tampaknya menggunakan data seluler + Unduhan ini menggunakan data seluler. Ini dapat menimbulkan biaya atau menghabiskan paket data Anda. Apakah Anda ingin melanjutkan? + Lanjutkan dengan data + Nanti saja + Tidak ada jaringan. Sambungkan ke Wi-Fi untuk mengunduh. + Anda tampaknya beralih ke data seluler + Unduhan baru kini menggunakan data seluler. Ini dapat menimbulkan biaya atau menghabiskan paket data Anda. + Peringatan biaya data diff --git a/controller/app/src/main/res/values-in/strings_networkpolicy.xml b/controller/app/src/main/res/values-in/strings_networkpolicy.xml deleted file mode 100644 index 537ea5f98..000000000 --- a/controller/app/src/main/res/values-in/strings_networkpolicy.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - Anda tampaknya menggunakan data seluler - Unduhan ini menggunakan data seluler. Ini dapat menimbulkan biaya atau menghabiskan paket data Anda. Apakah Anda ingin melanjutkan? - Lanjutkan dengan data - Nanti saja - Tidak ada jaringan. Sambungkan ke Wi-Fi untuk mengunduh. - Anda tampaknya beralih ke data seluler - Unduhan baru kini menggunakan data seluler. Ini dapat menimbulkan biaya atau menghabiskan paket data Anda. - Peringatan biaya data - diff --git a/controller/app/src/main/res/values-it/strings.xml b/controller/app/src/main/res/values-it/strings.xml index a4c64a66c..e1f48b5da 100644 --- a/controller/app/src/main/res/values-it/strings.xml +++ b/controller/app/src/main/res/values-it/strings.xml @@ -1330,4 +1330,13 @@ K2Go ha contenuto un\'attività insolita K2Go ha gestito un processo che usava troppo spazio e ha mantenuto il sistema attivo. Tocca per inviare un rapporto. Download dei dati della mappa + + Sembra che tu stia usando i dati mobili + Questo download usa i dati mobili. Può comportare costi o esaurire il tuo piano dati. Vuoi continuare? + Continua con i dati + Non ora + Nessuna rete. Connettiti al Wi-Fi per scaricare. + Sembra che tu sia passato ai dati mobili + I nuovi download ora usano i dati mobili. Questo può comportare costi o esaurire il tuo piano dati. + Avvisi sui costi dei dati diff --git a/controller/app/src/main/res/values-it/strings_networkpolicy.xml b/controller/app/src/main/res/values-it/strings_networkpolicy.xml deleted file mode 100644 index 8727dd9e6..000000000 --- a/controller/app/src/main/res/values-it/strings_networkpolicy.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - Sembra che tu stia usando i dati mobili - Questo download usa i dati mobili. Può comportare costi o esaurire il tuo piano dati. Vuoi continuare? - Continua con i dati - Non ora - Nessuna rete. Connettiti al Wi-Fi per scaricare. - Sembra che tu sia passato ai dati mobili - I nuovi download ora usano i dati mobili. Questo può comportare costi o esaurire il tuo piano dati. - Avvisi sui costi dei dati - diff --git a/controller/app/src/main/res/values-ja/strings.xml b/controller/app/src/main/res/values-ja/strings.xml index 2f1b2f359..1568d6224 100644 --- a/controller/app/src/main/res/values-ja/strings.xml +++ b/controller/app/src/main/res/values-ja/strings.xml @@ -1331,4 +1331,13 @@ K2Go が異常な動作を抑制しました K2Go はストレージを過剰に使用していたプロセスを処理し、システムを稼働させ続けました。タップしてレポートを送信します。 地図データをダウンロード中 + + モバイルデータを使用しているようです + このダウンロードはモバイルデータを使用します。料金が発生したり、データプランを使い切ったりする可能性があります。続行しますか? + モバイルデータで続行 + 今はしない + ネットワークがありません。ダウンロードするには Wi-Fi に接続してください。 + モバイルデータに切り替わったようです + 新しいダウンロードはモバイルデータを使用します。料金が発生したり、データプランを使い切ったりする可能性があります。 + データ料金の通知 diff --git a/controller/app/src/main/res/values-ja/strings_networkpolicy.xml b/controller/app/src/main/res/values-ja/strings_networkpolicy.xml deleted file mode 100644 index 82a214cab..000000000 --- a/controller/app/src/main/res/values-ja/strings_networkpolicy.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - モバイルデータを使用しているようです - このダウンロードはモバイルデータを使用します。料金が発生したり、データプランを使い切ったりする可能性があります。続行しますか? - モバイルデータで続行 - 今はしない - ネットワークがありません。ダウンロードするには Wi-Fi に接続してください。 - モバイルデータに切り替わったようです - 新しいダウンロードはモバイルデータを使用します。料金が発生したり、データプランを使い切ったりする可能性があります。 - データ料金の通知 - diff --git a/controller/app/src/main/res/values-ko/strings.xml b/controller/app/src/main/res/values-ko/strings.xml index 9fb772143..64df2f93b 100644 --- a/controller/app/src/main/res/values-ko/strings.xml +++ b/controller/app/src/main/res/values-ko/strings.xml @@ -1331,4 +1331,13 @@ K2Go가 비정상적인 활동을 억제했습니다 K2Go가 저장 공간을 과도하게 사용하던 프로세스를 처리하고 시스템을 계속 실행했습니다. 탭하여 보고서를 보내세요. 지도 데이터 다운로드 중 + + 모바일 데이터를 사용 중인 것 같습니다 + 이 다운로드는 모바일 데이터를 사용합니다. 요금이 발생하거나 데이터 요금제를 모두 사용할 수 있습니다. 계속하시겠습니까? + 데이터로 계속 + 나중에 + 네트워크가 없습니다. 다운로드하려면 Wi-Fi에 연결하세요. + 모바일 데이터로 전환된 것 같습니다 + 새 다운로드가 이제 모바일 데이터를 사용합니다. 요금이 발생하거나 데이터 요금제를 모두 사용할 수 있습니다. + 데이터 요금 알림 diff --git a/controller/app/src/main/res/values-ko/strings_networkpolicy.xml b/controller/app/src/main/res/values-ko/strings_networkpolicy.xml deleted file mode 100644 index 28e8afd5c..000000000 --- a/controller/app/src/main/res/values-ko/strings_networkpolicy.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - 모바일 데이터를 사용 중인 것 같습니다 - 이 다운로드는 모바일 데이터를 사용합니다. 요금이 발생하거나 데이터 요금제를 모두 사용할 수 있습니다. 계속하시겠습니까? - 데이터로 계속 - 나중에 - 네트워크가 없습니다. 다운로드하려면 Wi-Fi에 연결하세요. - 모바일 데이터로 전환된 것 같습니다 - 새 다운로드가 이제 모바일 데이터를 사용합니다. 요금이 발생하거나 데이터 요금제를 모두 사용할 수 있습니다. - 데이터 요금 알림 - diff --git a/controller/app/src/main/res/values-lt/strings.xml b/controller/app/src/main/res/values-lt/strings.xml index a1f7628f0..84719bca2 100644 --- a/controller/app/src/main/res/values-lt/strings.xml +++ b/controller/app/src/main/res/values-lt/strings.xml @@ -1347,4 +1347,13 @@ K2Go suvaldė neįprastą veiklą K2Go sutvarkė procesą, naudojusį per daug saugyklos, ir išlaikė jūsų sistemą veikiančią. Palieskite, kad išsiųstumėte ataskaitą. Atsisiunčiami žemėlapio duomenys + + Panašu, kad naudojate mobiliuosius duomenis + Šis atsisiuntimas naudoja mobiliuosius duomenis. Tai gali kainuoti arba išnaudoti duomenų planą. Ar norite tęsti? + Tęsti su mobiliaisiais duomenimis + Ne dabar + Nėra tinklo. Prisijunkite prie Wi-Fi, kad atsisiųstumėte. + Panašu, kad perjungėte į mobiliuosius duomenis + Nauji atsisiuntimai dabar naudoja mobiliuosius duomenis. Tai gali kainuoti arba išnaudoti duomenų planą. + Duomenų sąnaudų įspėjimai diff --git a/controller/app/src/main/res/values-lt/strings_networkpolicy.xml b/controller/app/src/main/res/values-lt/strings_networkpolicy.xml deleted file mode 100644 index d1b0c61cb..000000000 --- a/controller/app/src/main/res/values-lt/strings_networkpolicy.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - Panašu, kad naudojate mobiliuosius duomenis - Šis atsisiuntimas naudoja mobiliuosius duomenis. Tai gali kainuoti arba išnaudoti duomenų planą. Ar norite tęsti? - Tęsti su mobiliaisiais duomenimis - Ne dabar - Nėra tinklo. Prisijunkite prie Wi-Fi, kad atsisiųstumėte. - Panašu, kad perjungėte į mobiliuosius duomenis - Nauji atsisiuntimai dabar naudoja mobiliuosius duomenis. Tai gali kainuoti arba išnaudoti duomenų planą. - Duomenų sąnaudų įspėjimai - diff --git a/controller/app/src/main/res/values-nl/strings.xml b/controller/app/src/main/res/values-nl/strings.xml index 14fd93a1f..2d38aba91 100644 --- a/controller/app/src/main/res/values-nl/strings.xml +++ b/controller/app/src/main/res/values-nl/strings.xml @@ -1330,4 +1330,13 @@ K2Go heeft ongebruikelijke activiteit ingeperkt K2Go heeft een proces afgehandeld dat te veel opslag gebruikte en uw systeem draaiende gehouden. Tik om een rapport te sturen. Kaartgegevens downloaden + + Je lijkt mobiele data te gebruiken + Deze download gebruikt mobiele data. Dit kan kosten opleveren of je databundel opmaken. Wil je doorgaan? + Doorgaan met mobiele data + Niet nu + Geen netwerk. Maak verbinding met wifi om te downloaden. + Je lijkt overgeschakeld te zijn naar mobiele data + Nieuwe downloads gebruiken nu mobiele data. Dit kan kosten opleveren of je databundel opmaken. + Waarschuwingen datakosten diff --git a/controller/app/src/main/res/values-nl/strings_networkpolicy.xml b/controller/app/src/main/res/values-nl/strings_networkpolicy.xml deleted file mode 100644 index c8a2485f1..000000000 --- a/controller/app/src/main/res/values-nl/strings_networkpolicy.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - Je lijkt mobiele data te gebruiken - Deze download gebruikt mobiele data. Dit kan kosten opleveren of je databundel opmaken. Wil je doorgaan? - Doorgaan met mobiele data - Niet nu - Geen netwerk. Maak verbinding met wifi om te downloaden. - Je lijkt overgeschakeld te zijn naar mobiele data - Nieuwe downloads gebruiken nu mobiele data. Dit kan kosten opleveren of je databundel opmaken. - Waarschuwingen datakosten - diff --git a/controller/app/src/main/res/values-no/strings.xml b/controller/app/src/main/res/values-no/strings.xml index c4121d69e..6d0085fdb 100644 --- a/controller/app/src/main/res/values-no/strings.xml +++ b/controller/app/src/main/res/values-no/strings.xml @@ -1340,4 +1340,13 @@ K2Go begrenset uvanlig aktivitet K2Go håndterte en prosess som brukte for mye lagringsplass, og holdt systemet i gang. Trykk for å sende en rapport. Laster ned kartdata + + Det ser ut til at du bruker mobildata + Denne nedlastingen bruker mobildata. Det kan koste penger eller bruke opp datapakken din. Vil du fortsette? + Fortsett med mobildata + Ikke nå + Ingen nettverk. Koble til Wi-Fi for å laste ned. + Det ser ut til at du byttet til mobildata + Nye nedlastinger bruker nå mobildata. Det kan koste penger eller bruke opp datapakken din. + Varsler om datakostnad diff --git a/controller/app/src/main/res/values-no/strings_networkpolicy.xml b/controller/app/src/main/res/values-no/strings_networkpolicy.xml deleted file mode 100644 index d31abc714..000000000 --- a/controller/app/src/main/res/values-no/strings_networkpolicy.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - Det ser ut til at du bruker mobildata - Denne nedlastingen bruker mobildata. Det kan koste penger eller bruke opp datapakken din. Vil du fortsette? - Fortsett med mobildata - Ikke nå - Ingen nettverk. Koble til Wi-Fi for å laste ned. - Det ser ut til at du byttet til mobildata - Nye nedlastinger bruker nå mobildata. Det kan koste penger eller bruke opp datapakken din. - Varsler om datakostnad - diff --git a/controller/app/src/main/res/values-pl/strings.xml b/controller/app/src/main/res/values-pl/strings.xml index 276baaf1e..eae4c07ca 100644 --- a/controller/app/src/main/res/values-pl/strings.xml +++ b/controller/app/src/main/res/values-pl/strings.xml @@ -1340,4 +1340,13 @@ K2Go opanował nietypową aktywność K2Go obsłużył proces zużywający zbyt dużo pamięci i utrzymał system w działaniu. Dotknij, aby wysłać raport. Pobieranie danych mapy + + Wygląda na to, że używasz danych komórkowych + To pobieranie używa danych komórkowych. Może wiązać się z kosztami lub wyczerpać pakiet danych. Czy chcesz kontynuować? + Kontynuuj na danych + Nie teraz + Brak sieci. Połącz się z Wi-Fi, aby pobrać. + Wygląda na to, że przełączono na dane komórkowe + Nowe pobierania używają teraz danych komórkowych. Może to wiązać się z kosztami lub wyczerpać pakiet danych. + Alerty o kosztach danych diff --git a/controller/app/src/main/res/values-pl/strings_networkpolicy.xml b/controller/app/src/main/res/values-pl/strings_networkpolicy.xml deleted file mode 100644 index 3830a43cb..000000000 --- a/controller/app/src/main/res/values-pl/strings_networkpolicy.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - Wygląda na to, że używasz danych komórkowych - To pobieranie używa danych komórkowych. Może wiązać się z kosztami lub wyczerpać pakiet danych. Czy chcesz kontynuować? - Kontynuuj na danych - Nie teraz - Brak sieci. Połącz się z Wi-Fi, aby pobrać. - Wygląda na to, że przełączono na dane komórkowe - Nowe pobierania używają teraz danych komórkowych. Może to wiązać się z kosztami lub wyczerpać pakiet danych. - Alerty o kosztach danych - diff --git a/controller/app/src/main/res/values-pt/strings.xml b/controller/app/src/main/res/values-pt/strings.xml index 75414b0f7..3f00a4f25 100644 --- a/controller/app/src/main/res/values-pt/strings.xml +++ b/controller/app/src/main/res/values-pt/strings.xml @@ -1409,4 +1409,13 @@ O K2Go conteve atividade incomum O K2Go tratou um processo que usava armazenamento demais e manteve seu sistema funcionando. Toque para enviar um relatório. A transferir dados do mapa + + Parece que você está usando dados móveis + Este download usa dados móveis. Pode gerar custos ou esgotar seu plano de dados. Deseja continuar? + Continuar com dados + Agora não + Sem rede. Conecte-se ao Wi-Fi para baixar. + Parece que você mudou para dados móveis + Os novos downloads agora usam dados móveis. Isso pode gerar custos ou esgotar seu plano de dados. + Alertas de custo de dados diff --git a/controller/app/src/main/res/values-pt/strings_networkpolicy.xml b/controller/app/src/main/res/values-pt/strings_networkpolicy.xml deleted file mode 100644 index 7797c7f68..000000000 --- a/controller/app/src/main/res/values-pt/strings_networkpolicy.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - Parece que você está usando dados móveis - Este download usa dados móveis. Pode gerar custos ou esgotar seu plano de dados. Deseja continuar? - Continuar com dados - Agora não - Sem rede. Conecte-se ao Wi-Fi para baixar. - Parece que você mudou para dados móveis - Os novos downloads agora usam dados móveis. Isso pode gerar custos ou esgotar seu plano de dados. - Alertas de custo de dados - diff --git a/controller/app/src/main/res/values-ro/strings.xml b/controller/app/src/main/res/values-ro/strings.xml index 24b23e779..7a5cdc367 100644 --- a/controller/app/src/main/res/values-ro/strings.xml +++ b/controller/app/src/main/res/values-ro/strings.xml @@ -1330,4 +1330,13 @@ K2Go a limitat o activitate neobișnuită K2Go a gestionat un proces care folosea prea mult spațiu și a menținut sistemul în funcțiune. Atinge pentru a trimite un raport. Se descarcă datele hărții + + Se pare că folosești date mobile + Această descărcare folosește date mobile. Poate genera costuri sau îți poate epuiza planul de date. Vrei să continui? + Continuă pe date mobile + Nu acum + Fără rețea. Conectează-te la Wi-Fi pentru a descărca. + Se pare că ai trecut la date mobile + Descărcările noi folosesc acum date mobile. Acest lucru poate genera costuri sau îți poate epuiza planul de date. + Alerte privind costul datelor diff --git a/controller/app/src/main/res/values-ro/strings_networkpolicy.xml b/controller/app/src/main/res/values-ro/strings_networkpolicy.xml deleted file mode 100644 index 0cfbf0fd4..000000000 --- a/controller/app/src/main/res/values-ro/strings_networkpolicy.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - Se pare că folosești date mobile - Această descărcare folosește date mobile. Poate genera costuri sau îți poate epuiza planul de date. Vrei să continui? - Continuă pe date mobile - Nu acum - Fără rețea. Conectează-te la Wi-Fi pentru a descărca. - Se pare că ai trecut la date mobile - Descărcările noi folosesc acum date mobile. Acest lucru poate genera costuri sau îți poate epuiza planul de date. - Alerte privind costul datelor - diff --git a/controller/app/src/main/res/values-ru-rRU/strings.xml b/controller/app/src/main/res/values-ru-rRU/strings.xml index 2edac914c..dd7844e15 100644 --- a/controller/app/src/main/res/values-ru-rRU/strings.xml +++ b/controller/app/src/main/res/values-ru-rRU/strings.xml @@ -1406,4 +1406,13 @@ K2Go сдержал необычную активность K2Go обработал процесс, использовавший слишком много памяти, и сохранил работу системы. Нажмите, чтобы отправить отчёт. Загрузка данных карты + + Похоже, вы используете мобильные данные + Эта загрузка использует мобильные данные. Это может повлечь расходы или исчерпать пакет данных. Продолжить? + Продолжить на мобильных данных + Не сейчас + Нет сети. Подключитесь к Wi-Fi для загрузки. + Похоже, вы переключились на мобильные данные + Новые загрузки теперь используют мобильные данные. Это может повлечь расходы или исчерпать пакет данных. + Оповещения о расходе данных diff --git a/controller/app/src/main/res/values-ru-rRU/strings_networkpolicy.xml b/controller/app/src/main/res/values-ru-rRU/strings_networkpolicy.xml deleted file mode 100644 index e007ac965..000000000 --- a/controller/app/src/main/res/values-ru-rRU/strings_networkpolicy.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - Похоже, вы используете мобильные данные - Эта загрузка использует мобильные данные. Это может повлечь расходы или исчерпать пакет данных. Продолжить? - Продолжить на мобильных данных - Не сейчас - Нет сети. Подключитесь к Wi-Fi для загрузки. - Похоже, вы переключились на мобильные данные - Новые загрузки теперь используют мобильные данные. Это может повлечь расходы или исчерпать пакет данных. - Оповещения о расходе данных - diff --git a/controller/app/src/main/res/values-sk/strings.xml b/controller/app/src/main/res/values-sk/strings.xml index d7ea4fc76..0ae2135e4 100644 --- a/controller/app/src/main/res/values-sk/strings.xml +++ b/controller/app/src/main/res/values-sk/strings.xml @@ -1337,4 +1337,13 @@ K2Go zvládol neobvyklú aktivitu K2Go spracoval proces, ktorý využíval priveľa úložiska, a udržal systém v chode. Klepnutím odošlite hlásenie. Sťahovanie údajov mapy + + Zdá sa, že používate mobilné dáta + Toto sťahovanie používa mobilné dáta. Môže byť spoplatnené alebo vyčerpať váš dátový limit. Chcete pokračovať? + Pokračovať na mobilných dátach + Teraz nie + Žiadna sieť. Pripojte sa k Wi-Fi na stiahnutie. + Zdá sa, že ste sa prepli na mobilné dáta + Nové sťahovania teraz používajú mobilné dáta. Môže byť spoplatnené alebo vyčerpať váš dátový limit. + Upozornenia na náklady za dáta diff --git a/controller/app/src/main/res/values-sk/strings_networkpolicy.xml b/controller/app/src/main/res/values-sk/strings_networkpolicy.xml deleted file mode 100644 index fa5c85e69..000000000 --- a/controller/app/src/main/res/values-sk/strings_networkpolicy.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - Zdá sa, že používate mobilné dáta - Toto sťahovanie používa mobilné dáta. Môže byť spoplatnené alebo vyčerpať váš dátový limit. Chcete pokračovať? - Pokračovať na mobilných dátach - Teraz nie - Žiadna sieť. Pripojte sa k Wi-Fi na stiahnutie. - Zdá sa, že ste sa prepli na mobilné dáta - Nové sťahovania teraz používajú mobilné dáta. Môže byť spoplatnené alebo vyčerpať váš dátový limit. - Upozornenia na náklady za dáta - diff --git a/controller/app/src/main/res/values-sr/strings.xml b/controller/app/src/main/res/values-sr/strings.xml index 3a5f09539..38d91d886 100644 --- a/controller/app/src/main/res/values-sr/strings.xml +++ b/controller/app/src/main/res/values-sr/strings.xml @@ -1343,4 +1343,13 @@ K2Go је обуздао необичну активност K2Go је обрадио процес који је користио превише простора и одржао систем у раду. Додирните да пошаљете извештај. Преузимање података мапе + + Изгледа да користите мобилне податке + Ово преузимање користи мобилне податке. Може да има трошак или да потроши ваш план за податке. Желите ли да наставите? + Настави на мобилним подацима + Не сада + Нема мреже. Повежите се на Wi-Fi да бисте преузели. + Изгледа да сте прешли на мобилне податке + Нова преузимања сада користе мобилне податке. Ово може да има трошак или да потроши ваш план за податке. + Обавештења о трошку података diff --git a/controller/app/src/main/res/values-sr/strings_networkpolicy.xml b/controller/app/src/main/res/values-sr/strings_networkpolicy.xml deleted file mode 100644 index b7eb523b2..000000000 --- a/controller/app/src/main/res/values-sr/strings_networkpolicy.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - Изгледа да користите мобилне податке - Ово преузимање користи мобилне податке. Може да има трошак или да потроши ваш план за податке. Желите ли да наставите? - Настави на мобилним подацима - Не сада - Нема мреже. Повежите се на Wi-Fi да бисте преузели. - Изгледа да сте прешли на мобилне податке - Нова преузимања сада користе мобилне податке. Ово може да има трошак или да потроши ваш план за податке. - Обавештења о трошку података - diff --git a/controller/app/src/main/res/values-sw/strings.xml b/controller/app/src/main/res/values-sw/strings.xml index c7041d488..52aa2e5f8 100644 --- a/controller/app/src/main/res/values-sw/strings.xml +++ b/controller/app/src/main/res/values-sw/strings.xml @@ -1350,4 +1350,13 @@ K2Go ilidhibiti shughuli isiyo ya kawaida K2Go ilishughulikia mchakato uliokuwa ukitumia hifadhi nyingi mno na kuweka mfumo wako ukiendelea kufanya kazi. Gusa kutuma ripoti. Inapakua data ya ramani + + Inaonekana unatumia data ya simu + Upakuaji huu unatumia data ya simu. Inaweza kugharimu au kumaliza kifurushi chako cha data. Ungependa kuendelea? + Endelea kwa data + Si sasa + Hakuna mtandao. Unganisha kwa Wi-Fi ili kupakua. + Inaonekana umebadili hadi data ya simu + Vipakuliwa vipya sasa vinatumia data ya simu. Hii inaweza kugharimu au kumaliza kifurushi chako cha data. + Arifa za gharama ya data diff --git a/controller/app/src/main/res/values-sw/strings_networkpolicy.xml b/controller/app/src/main/res/values-sw/strings_networkpolicy.xml deleted file mode 100644 index 2c0a2de1d..000000000 --- a/controller/app/src/main/res/values-sw/strings_networkpolicy.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - Inaonekana unatumia data ya simu - Upakuaji huu unatumia data ya simu. Inaweza kugharimu au kumaliza kifurushi chako cha data. Ungependa kuendelea? - Endelea kwa data - Si sasa - Hakuna mtandao. Unganisha kwa Wi-Fi ili kupakua. - Inaonekana umebadili hadi data ya simu - Vipakuliwa vipya sasa vinatumia data ya simu. Hii inaweza kugharimu au kumaliza kifurushi chako cha data. - Arifa za gharama ya data - diff --git a/controller/app/src/main/res/values-ta/strings.xml b/controller/app/src/main/res/values-ta/strings.xml index 49d11ad4d..2fc518bb7 100644 --- a/controller/app/src/main/res/values-ta/strings.xml +++ b/controller/app/src/main/res/values-ta/strings.xml @@ -1350,4 +1350,13 @@ K2Go அசாதாரண செயல்பாட்டைக் கட்டுப்படுத்தியது அதிக சேமிப்பைப் பயன்படுத்திய ஒரு செயல்முறையை K2Go கையாண்டு உங்கள் கணினியை இயங்க வைத்தது. அறிக்கை அனுப்ப தட்டவும். வரைபடத் தரவைப் பதிவிறக்குகிறது + + நீங்கள் மொபைல் டேட்டாவைப் பயன்படுத்துவது போல் தெரிகிறது + இந்தப் பதிவிறக்கம் மொபைல் டேட்டாவைப் பயன்படுத்துகிறது. இதனால் கட்டணம் ஏற்படலாம் அல்லது உங்கள் தரவுத் திட்டம் தீர்ந்துபோகலாம். தொடர விரும்புகிறீர்களா? + டேட்டாவில் தொடரவும் + இப்போது வேண்டாம் + நெட்வொர்க் இல்லை. பதிவிறக்க Wi-Fi உடன் இணைக்கவும். + நீங்கள் மொபைல் டேட்டாவிற்கு மாறியது போல் தெரிகிறது + புதிய பதிவிறக்கங்கள் இப்போது மொபைல் டேட்டாவைப் பயன்படுத்துகின்றன. இதனால் கட்டணம் ஏற்படலாம் அல்லது உங்கள் தரவுத் திட்டம் தீர்ந்துபோகலாம். + டேட்டா செலவு எச்சரிக்கைகள் diff --git a/controller/app/src/main/res/values-ta/strings_networkpolicy.xml b/controller/app/src/main/res/values-ta/strings_networkpolicy.xml deleted file mode 100644 index d54e4f168..000000000 --- a/controller/app/src/main/res/values-ta/strings_networkpolicy.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - நீங்கள் மொபைல் டேட்டாவைப் பயன்படுத்துவது போல் தெரிகிறது - இந்தப் பதிவிறக்கம் மொபைல் டேட்டாவைப் பயன்படுத்துகிறது. இதனால் கட்டணம் ஏற்படலாம் அல்லது உங்கள் தரவுத் திட்டம் தீர்ந்துபோகலாம். தொடர விரும்புகிறீர்களா? - டேட்டாவில் தொடரவும் - இப்போது வேண்டாம் - நெட்வொர்க் இல்லை. பதிவிறக்க Wi-Fi உடன் இணைக்கவும். - நீங்கள் மொபைல் டேட்டாவிற்கு மாறியது போல் தெரிகிறது - புதிய பதிவிறக்கங்கள் இப்போது மொபைல் டேட்டாவைப் பயன்படுத்துகின்றன. இதனால் கட்டணம் ஏற்படலாம் அல்லது உங்கள் தரவுத் திட்டம் தீர்ந்துபோகலாம். - டேட்டா செலவு எச்சரிக்கைகள் - diff --git a/controller/app/src/main/res/values-tr/strings.xml b/controller/app/src/main/res/values-tr/strings.xml index 1fae40528..6cc3e9c6f 100644 --- a/controller/app/src/main/res/values-tr/strings.xml +++ b/controller/app/src/main/res/values-tr/strings.xml @@ -1330,4 +1330,13 @@ K2Go olağan dışı bir etkinliği kontrol altına aldı K2Go çok fazla depolama kullanan bir işlemi yönetti ve sisteminizi çalışır durumda tuttu. Rapor göndermek için dokunun. Harita verileri indiriliyor + + Mobil veri kullanıyor gibisiniz + Bu indirme mobil veri kullanıyor. Ücretlendirilebilir veya veri paketinizi tüketebilir. Devam etmek istiyor musunuz? + Mobil veriyle devam et + Şimdi değil + Ağ yok. İndirmek için Wi-Fi\'ye bağlanın. + Mobil veriye geçmiş gibisiniz + Yeni indirmeler artık mobil veri kullanıyor. Bu, ücretlendirilebilir veya veri paketinizi tüketebilir. + Veri maliyeti uyarıları diff --git a/controller/app/src/main/res/values-tr/strings_networkpolicy.xml b/controller/app/src/main/res/values-tr/strings_networkpolicy.xml deleted file mode 100644 index fe363bc98..000000000 --- a/controller/app/src/main/res/values-tr/strings_networkpolicy.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - Mobil veri kullanıyor gibisiniz - Bu indirme mobil veri kullanıyor. Ücretlendirilebilir veya veri paketinizi tüketebilir. Devam etmek istiyor musunuz? - Mobil veriyle devam et - Şimdi değil - Ağ yok. İndirmek için Wi-Fi\'ye bağlanın. - Mobil veriye geçmiş gibisiniz - Yeni indirmeler artık mobil veri kullanıyor. Bu, ücretlendirilebilir veya veri paketinizi tüketebilir. - Veri maliyeti uyarıları - diff --git a/controller/app/src/main/res/values-uk/strings.xml b/controller/app/src/main/res/values-uk/strings.xml index 9830fba16..7a1e4fff1 100644 --- a/controller/app/src/main/res/values-uk/strings.xml +++ b/controller/app/src/main/res/values-uk/strings.xml @@ -1330,4 +1330,13 @@ K2Go стримав незвичну активність K2Go опрацював процес, що використовував забагато сховища, і зберіг роботу системи. Натисніть, щоб надіслати звіт. Завантаження даних карти + + Схоже, ви використовуєте мобільні дані + Це завантаження використовує мобільні дані. Це може призвести до витрат або вичерпати пакет даних. Продовжити? + Продовжити на мобільних даних + Не зараз + Немає мережі. Підключіться до Wi-Fi, щоб завантажити. + Схоже, ви перейшли на мобільні дані + Нові завантаження тепер використовують мобільні дані. Це може призвести до витрат або вичерпати пакет даних. + Сповіщення про витрати даних diff --git a/controller/app/src/main/res/values-uk/strings_networkpolicy.xml b/controller/app/src/main/res/values-uk/strings_networkpolicy.xml deleted file mode 100644 index 9071a3a02..000000000 --- a/controller/app/src/main/res/values-uk/strings_networkpolicy.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - Схоже, ви використовуєте мобільні дані - Це завантаження використовує мобільні дані. Це може призвести до витрат або вичерпати пакет даних. Продовжити? - Продовжити на мобільних даних - Не зараз - Немає мережі. Підключіться до Wi-Fi, щоб завантажити. - Схоже, ви перейшли на мобільні дані - Нові завантаження тепер використовують мобільні дані. Це може призвести до витрат або вичерпати пакет даних. - Сповіщення про витрати даних - diff --git a/controller/app/src/main/res/values-vi/strings.xml b/controller/app/src/main/res/values-vi/strings.xml index 94cb19c32..46c4027b1 100644 --- a/controller/app/src/main/res/values-vi/strings.xml +++ b/controller/app/src/main/res/values-vi/strings.xml @@ -1330,4 +1330,13 @@ K2Go đã kiểm soát hoạt động bất thường K2Go đã xử lý một tiến trình sử dụng quá nhiều bộ nhớ và giữ cho hệ thống hoạt động. Nhấn để gửi báo cáo. Đang tải dữ liệu bản đồ + + Có vẻ như bạn đang dùng dữ liệu di động + Bản tải xuống này dùng dữ liệu di động. Có thể phát sinh chi phí hoặc dùng hết gói dữ liệu của bạn. Bạn có muốn tiếp tục không? + Tiếp tục bằng dữ liệu + Để sau + Không có mạng. Kết nối Wi-Fi để tải xuống. + Có vẻ như bạn đã chuyển sang dữ liệu di động + Các bản tải xuống mới hiện dùng dữ liệu di động. Điều này có thể phát sinh chi phí hoặc dùng hết gói dữ liệu của bạn. + Cảnh báo chi phí dữ liệu diff --git a/controller/app/src/main/res/values-vi/strings_networkpolicy.xml b/controller/app/src/main/res/values-vi/strings_networkpolicy.xml deleted file mode 100644 index c73e04cdc..000000000 --- a/controller/app/src/main/res/values-vi/strings_networkpolicy.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - Có vẻ như bạn đang dùng dữ liệu di động - Bản tải xuống này dùng dữ liệu di động. Có thể phát sinh chi phí hoặc dùng hết gói dữ liệu của bạn. Bạn có muốn tiếp tục không? - Tiếp tục bằng dữ liệu - Để sau - Không có mạng. Kết nối Wi-Fi để tải xuống. - Có vẻ như bạn đã chuyển sang dữ liệu di động - Các bản tải xuống mới hiện dùng dữ liệu di động. Điều này có thể phát sinh chi phí hoặc dùng hết gói dữ liệu của bạn. - Cảnh báo chi phí dữ liệu - diff --git a/controller/app/src/main/res/values-yo/strings.xml b/controller/app/src/main/res/values-yo/strings.xml index 010bb7ecf..79be5ff31 100644 --- a/controller/app/src/main/res/values-yo/strings.xml +++ b/controller/app/src/main/res/values-yo/strings.xml @@ -1350,4 +1350,13 @@ K2Go ṣàkóso iṣẹ́ àìṣàmúlò K2Go ṣàkóso ìlànà kan tí ó ń lo ibi ìsàvè púpọ̀ jù ó sì jẹ́ kí ètò rẹ máa ṣiṣẹ́. Tẹ̀ láti fi ìròyìn ránṣẹ́. Ń gba data maapu wọlé + + O dabi pe o n lo data alagbeka + Igbasilẹ yii n lo data alagbeka. O le san owo tabi lo package data rẹ tan. Ṣe o fẹ tẹsiwaju? + Tẹsiwaju pẹlu data + Kii ṣe bayii + Ko si nẹtiwọọki. Sopọ si Wi-Fi lati gbaa wọle. + O dabi pe o ti yipada si data alagbeka + Awọn igbasilẹ tuntun n lo data alagbeka bayii. Eyi le san owo tabi lo package data rẹ tan. + Awọn ikilọ iye owo data diff --git a/controller/app/src/main/res/values-yo/strings_networkpolicy.xml b/controller/app/src/main/res/values-yo/strings_networkpolicy.xml deleted file mode 100644 index 56de0b6a0..000000000 --- a/controller/app/src/main/res/values-yo/strings_networkpolicy.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - O dabi pe o n lo data alagbeka - Igbasilẹ yii n lo data alagbeka. O le san owo tabi lo package data rẹ tan. Ṣe o fẹ tẹsiwaju? - Tẹsiwaju pẹlu data - Kii ṣe bayii - Ko si nẹtiwọọki. Sopọ si Wi-Fi lati gbaa wọle. - O dabi pe o ti yipada si data alagbeka - Awọn igbasilẹ tuntun n lo data alagbeka bayii. Eyi le san owo tabi lo package data rẹ tan. - Awọn ikilọ iye owo data - diff --git a/controller/app/src/main/res/values-zh-rCN/strings.xml b/controller/app/src/main/res/values-zh-rCN/strings.xml index d7ae2d12b..d9a6d7422 100644 --- a/controller/app/src/main/res/values-zh-rCN/strings.xml +++ b/controller/app/src/main/res/values-zh-rCN/strings.xml @@ -1331,4 +1331,13 @@ K2Go 已控制异常活动 K2Go 处理了一个占用过多存储的进程,并保持系统运行。点按以发送报告。 正在下载地图数据 + + 您似乎正在使用移动数据 + 此下载会使用移动数据。可能产生费用或用尽您的套餐流量。是否继续? + 使用数据继续 + 暂不 + 无网络。请连接 Wi-Fi 以下载。 + 您似乎已切换到移动数据 + 新的下载现在会使用移动数据。这可能产生费用或用尽您的套餐流量。 + 数据费用提醒 diff --git a/controller/app/src/main/res/values-zh-rCN/strings_networkpolicy.xml b/controller/app/src/main/res/values-zh-rCN/strings_networkpolicy.xml deleted file mode 100644 index 2276686c5..000000000 --- a/controller/app/src/main/res/values-zh-rCN/strings_networkpolicy.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - 您似乎正在使用移动数据 - 此下载会使用移动数据。可能产生费用或用尽您的套餐流量。是否继续? - 使用数据继续 - 暂不 - 无网络。请连接 Wi-Fi 以下载。 - 您似乎已切换到移动数据 - 新的下载现在会使用移动数据。这可能产生费用或用尽您的套餐流量。 - 数据费用提醒 - diff --git a/controller/app/src/main/res/values/strings.xml b/controller/app/src/main/res/values/strings.xml index 29f3a2ef7..c95505b0f 100644 --- a/controller/app/src/main/res/values/strings.xml +++ b/controller/app/src/main/res/values/strings.xml @@ -1514,4 +1514,13 @@ K2Go contained unusual activity K2Go handled a process that was using too much storage and kept your system running. Tap to send a report. Downloading map data + + You seem to be on mobile data + This download uses mobile data. It can add cost or use up your data plan. Do you want to continue? + Continue on data + Not now + No network. Connect to Wi-Fi to download. + You seem to have switched to mobile data + New downloads now use mobile data. This can add cost or use up your data plan. + Data cost alerts diff --git a/controller/app/src/main/res/values/strings_networkpolicy.xml b/controller/app/src/main/res/values/strings_networkpolicy.xml deleted file mode 100644 index fc91139b2..000000000 --- a/controller/app/src/main/res/values/strings_networkpolicy.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - You seem to be on mobile data - This download uses mobile data. It can add cost or use up your data plan. Do you want to continue? - Continue on data - Not now - No network. Connect to Wi-Fi to download. - You seem to have switched to mobile data - New downloads now use mobile data. This can add cost or use up your data plan. - Data cost alerts - diff --git a/controller/app/src/main/res/values/strings_untranslated.xml b/controller/app/src/main/res/values/strings_untranslated.xml index c514051e5..e924a6196 100644 --- a/controller/app/src/main/res/values/strings_untranslated.xml +++ b/controller/app/src/main/res/values/strings_untranslated.xml @@ -5,12 +5,13 @@ This is the tracking list for strings that are user-facing but NOT yet translated to the 33 locales. They carry translatable="false" HERE (so lint's MissingTranslation is satisfied and the debt is visible in one place) instead of being buried inline in strings.xml. When a string is - translated, migrate it to strings.xml (or its feature file) with all 33 locale values and drop the - entry here. + translated, migrate it to strings.xml with all locale values and drop the entry here. This is the + ONLY external string file. There are no per-feature string files; everything else lives in + strings.xml. --> - + From 5bc3c79ed587ff9592dcac8a2126c088ab2faf84 Mon Sep 17 00:00:00 2001 From: Luis Guzman Date: Wed, 16 Sep 2026 18:54:32 -0600 Subject: [PATCH 14/18] K2GO-395 feat(networkpolicy): gate the dashboard live update on metered cost The LIVE dashboard update/install (dash-node >= 1.2.0) is a REST-heavy transfer: the box git-fetches and blue-green rebuilds over the device default network. It started with no cost prompt. Wrap the LIVE branch (DashboardRebuild.start -> startRest) in NetworkPolicyGate.guardHeavyStart. The proot bridge (< 1.2.0, startProot) stops the box and is out of scope. Only startRest POSTs a new rebuild (DashboardRebuildService ACTION_START); ACTION_ATTACH re-owns a running one without POSTing, so one UI gate covers it. Not routed through ContentAdmission -- that already defers TO a dashboard update, so it would be circular. --- .../k2go/redesign/DashboardRebuild.java | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/controller/app/src/main/java/org/appdevforall/k2go/redesign/DashboardRebuild.java b/controller/app/src/main/java/org/appdevforall/k2go/redesign/DashboardRebuild.java index 1b3870349..c98bd6a89 100644 --- a/controller/app/src/main/java/org/appdevforall/k2go/redesign/DashboardRebuild.java +++ b/controller/app/src/main/java/org/appdevforall/k2go/redesign/DashboardRebuild.java @@ -93,8 +93,19 @@ private static void start(@NonNull Fragment host, @NonNull View anchor, boolean if (!host.isAdded()) return; // ADFA-5339: the site refresh only applies to the LIVE REST path; the proot bridge rebuild // (< 1.2.0) has no site step, so the checkbox is simply not carried there. - if (op.isLive()) startRest(host, anchor, updateSite); - else startProot(host); + if (op.isLive()) { + // K2GO-395 (ADR-395): the LIVE path is a REST-heavy transfer -- the box git-fetches + // and blue-green rebuilds over the device's default network. Prompt before spending + // metered data. Like FQR (a user-driven Operation, not a banked ContentType), it is + // gated at the UI commit, not through ContentAdmission -- which already defers TO a + // dashboard update, so routing it there would be circular. The only POST path is + // startRest -> DashboardRebuildService ACTION_START (ATTACH re-owns without POSTing), + // so this one gate covers it. No queue to fall back on: a decline just does not start. + org.appdevforall.k2go.networkpolicy.presentation.NetworkPolicyGate.guardHeavyStart( + host.requireActivity(), () -> startRest(host, anchor, updateSite)); + } else { + startProot(host); + } }); }); } From e52f8041ab814a6821e89faf39da476fe6629063 Mon Sep 17 00:00:00 2001 From: Luis Guzman Date: Wed, 16 Sep 2026 18:54:45 -0600 Subject: [PATCH 15/18] K2GO-395 feat(networkpolicy): gate the Get More base-maps download on metered cost K2GO-394 moved the base-map bytes onto REST: openMapsIndex -> the maps runrole now pre-downloads the selected base layers through dash-node (InstallService.downloadMapsBasemapsThenRun -> RestContentClient("basemaps")) before the proot post-process. That heavy transfer started with no cost prompt. Gate the Get More (post-install) commit point (openMapsIndex) with NetworkPolicyGate.guardHeavyStart, inside the existing InstallConfirm.gate body. Not gated at MapsProvisioner.drain: that drain is a serialized proot stage whose refusal is TERMINAL (SetupProgressActivity retires it as mapsStartFailed, by design, to avoid an unexplained spinner), so a cost-hold there would read as a hard failure, not a deferral. The wizard/system-install maps path (mapsWizardConfirm) and that headless hold ride with the install/rootfs PR. --- .../k2go/redesign/SetupLibraryActivity.java | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/controller/app/src/main/java/org/appdevforall/k2go/redesign/SetupLibraryActivity.java b/controller/app/src/main/java/org/appdevforall/k2go/redesign/SetupLibraryActivity.java index a8e6b7f55..b301fbb12 100644 --- a/controller/app/src/main/java/org/appdevforall/k2go/redesign/SetupLibraryActivity.java +++ b/controller/app/src/main/java/org/appdevforall/k2go/redesign/SetupLibraryActivity.java @@ -579,12 +579,23 @@ public void openMapsConfirm(String[] names, String[] opts, long[] mb, String[] l public void openMapsIndex(String[] levels, long totalMb) { // ADFA-5228: maps is a proot (STOPPED) install — confirm before entering the index that runs it. InstallConfirm.gate(this, org.appdevforall.k2go.system.domain.Operation.appInstall("maps"), () -> { - String base = levels != null && levels.length > 0 && levels[0] != null ? levels[0] : "11"; - String sat = levels != null && levels.length > 1 && levels[1] != null ? levels[1] : "none"; - String ter = levels != null && levels.length > 2 && levels[2] != null ? levels[2] : "0-none"; - boolean search = levels != null && levels.length > 3 && levels[3] != null; - MapsWishlist.save(this, base, sat, ter, search, totalMb); - startActivity(new Intent(this, SetupProgressActivity.class)); + // K2GO-395 (ADR-395): maps pre-downloads its base layers over REST (dash-node, + // InstallService.downloadMapsBasemapsThenRun) before the runrole, so this is a costed heavy + // start -- prompt before spending metered data. Gated at the UI commit (like FQR), not at + // MapsProvisioner.drain: the maps drain is a serialized proot stage where a refusal is + // TERMINAL (SetupProgressActivity marks mapsStartFailed and retires the stage, by design, to + // avoid an unexplained spinner), so a "cost-hold = retry later" does not fit there without an + // orchestrator "waiting for network" state. The wizard/system-install maps path + // (mapsWizardConfirm) + that headless integration ride with the install/rootfs PR. Decline or + // offline just does not enter the index; nothing is banked. + org.appdevforall.k2go.networkpolicy.presentation.NetworkPolicyGate.guardHeavyStart(this, () -> { + String base = levels != null && levels.length > 0 && levels[0] != null ? levels[0] : "11"; + String sat = levels != null && levels.length > 1 && levels[1] != null ? levels[1] : "none"; + String ter = levels != null && levels.length > 2 && levels[2] != null ? levels[2] : "0-none"; + boolean search = levels != null && levels.length > 3 && levels[3] != null; + MapsWishlist.save(this, base, sat, ter, search, totalMb); + startActivity(new Intent(this, SetupProgressActivity.class)); + }); }); } From 67510da77c067ae3d10d31c3fc77b125658bb9e1 Mon Sep 17 00:00:00 2001 From: Luis Guzman Date: Wed, 16 Sep 2026 18:54:56 -0600 Subject: [PATCH 16/18] K2GO-395 fix(networkpolicy): name the real deferral reason in the cost-gate log allowsHeavyStartNow() is false for BOTH metered-without-consent and no usable network, but the deferral log hardcoded "metered network without consent", so an offline deferral was misattributed. Classify once with decideNow() and log the real reason. From the wary full-branch review. --- .../k2go/system/data/ContentAdmission.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/controller/app/src/main/java/org/appdevforall/k2go/system/data/ContentAdmission.java b/controller/app/src/main/java/org/appdevforall/k2go/system/data/ContentAdmission.java index 32a0cb052..fb0912200 100644 --- a/controller/app/src/main/java/org/appdevforall/k2go/system/data/ContentAdmission.java +++ b/controller/app/src/main/java/org/appdevforall/k2go/system/data/ContentAdmission.java @@ -16,6 +16,7 @@ import org.appdevforall.k2go.install.presentation.ModuleQueueRepository; import org.appdevforall.k2go.networkpolicy.data.NetworkCostAdmission; +import org.appdevforall.k2go.networkpolicy.domain.NetworkPolicyDecision; import org.appdevforall.k2go.redesign.DashboardRebuildService; import org.appdevforall.k2go.redesign.MapsProvisioner; import org.appdevforall.k2go.system.domain.ContentType; @@ -78,9 +79,13 @@ public static boolean canStart(Context ctx, ContentType stream) { // the user's consent. This is the single headless hold that covers every drain path -- the // commit point, the wizard-bank path, and every background re-drain -- so a banked order // waits for Wi-Fi or consent rather than silently spending mobile data. Prompting happens at - // the UI commit point (NetworkPolicyGate); here we only defer. - if (!NetworkCostAdmission.allowsHeavyStartNow(ctx)) { - Log.d(TAG, stream.key() + " drain deferred: metered network without consent"); + // the UI commit point (NetworkPolicyGate); here we only defer. Two distinct hold reasons -- + // no usable network vs metered-without-consent -- so the log names the real one. + NetworkPolicyDecision cost = NetworkCostAdmission.decideNow(ctx); + if (cost != NetworkPolicyDecision.ALLOW) { + Log.d(TAG, stream.key() + " drain deferred: " + + (cost == NetworkPolicyDecision.BLOCKED_NO_NETWORK + ? "no usable network" : "metered network without consent")); return false; } return true; From 48d85dc7e80edf91e1f1fbc0fde65d17f6829752 Mon Sep 17 00:00:00 2001 From: Luis Guzman Date: Wed, 16 Sep 2026 18:54:56 -0600 Subject: [PATCH 17/18] K2GO-395 docs(networkpolicy): record dashboard + base-maps seams; defer install/rootfs Update ADR-395: the dashboard LIVE update/install and the Get More base-maps download are now gated at their UI commits (both REST-heavy, both user-driven Operations, so UI-gated like FQR, not through ContentAdmission). Note that K2GO-394 moved the maps bytes onto REST, and why the maps hold is at the commit point, not the drain (mapsStartFailed is terminal). Record the l10n fold. Mark the remaining work -- wizard/system-install maps, rootfs (aria2), DownloadManager (OTA/portal), the two-hasInternet fold -- as deferred to the install/rootfs PR and follow-ups. --- .../docs/ADR-395-network-cost-consent.md | 48 ++++++++++++++----- 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/controller/docs/ADR-395-network-cost-consent.md b/controller/docs/ADR-395-network-cost-consent.md index 1032b9d61..1ec8a63ea 100644 --- a/controller/docs/ADR-395-network-cost-consent.md +++ b/controller/docs/ADR-395-network-cost-consent.md @@ -128,8 +128,14 @@ reference (`ZimConfirmFragment`), so each remaining seam is a one-line wrap. | ZIM | `ZimConfirmFragment.java:105-110` -> `a.startZimDownload()` | LANDED (reference) | | Books | `BooksConfirmFragment.java:82` -> `a.startBooksDownload()` | `guardHeavyStart(a, a::startBooksDownload)` | | Kolibri | `KolibriConfirmFragment.java:227` -> `startLive(chosen)` | `guardHeavyStart(requireActivity(), () -> startLive(chosen))` | -| Maps | `MapsConfirmFragment.java:90` -- the live `else` branch (~:99), NOT the `wizard` branch | wrap the live-download body | -| Rootfs/modules install | `InstallService` started at `SetupProgressActivity.java:1401`; commit point is the wizard "Install" confirmation | wrap that confirm before starting InstallService | +| Maps (Get More, post-install) | `SetupLibraryActivity.openMapsIndex` (the `MapsConfirmFragment` live `else` branch, `MapsConfirmFragment.java:103`) | LANDED -- `guardHeavyStart` inside the `InstallConfirm.gate` body. Base layers pre-download over REST before the runrole: K2GO-394 moved the maps bytes onto dash-node (`InstallService.downloadMapsBasemapsThenRun` -> `RestContentClient("basemaps")`), so it IS a costed heavy start. NOT gated at `MapsProvisioner.drain`: that drain is a serialized proot stage where a refusal is TERMINAL (`SetupProgressActivity` retires it as `mapsStartFailed`, by design, to avoid an unexplained spinner), so a cost-hold does not fit there without an orchestrator "waiting for network" state -- that headless integration rides with the install/rootfs PR. | +| Dashboard update/install (LIVE) | `DashboardRebuild.start` -> `startRest` (`DashboardRebuild.java:96`) | LANDED -- `guardHeavyStart` on the LIVE branch only. dash-node >= 1.2.0 git-fetches + blue-green rebuilds over REST across the default network. The proot bridge (< 1.2.0, `startProot`) stops the box and is out of scope. Only `startRest` POSTs a new rebuild (`DashboardRebuildService` `ACTION_START`); `ACTION_ATTACH` re-owns a running one without POSTing, so one UI gate covers it. | +| Wizard/system-install maps + rootfs/modules install | `mapsWizardConfirm`; `InstallService` from the wizard | DEFERRED to the install/rootfs PR ("el install va aparte con el rootfs"). Rootfs is aria2 (not REST); the wizard maps download rides with it. | + +Dashboard and Get-More-Maps are user-driven Operations, not banked `ContentType`s, +so they gate at the UI commit (like FQR), NOT through `ContentAdmission` -- +`ContentAdmission` already defers TO a dashboard update and to maps, so routing +them back through it would be circular. DownloadManager seams differ -- no `Service.start`; the app enqueues and the system transfers. Consult the gate first, then honor the decision (proceed on @@ -195,14 +201,23 @@ Landed as a compiling, tested starting point for the implementer: - Wiring: observer started in `IIABApplication`; the ZIM and Books commit points (`SetupLibraryActivity.startZimDownload` / `startBooksDownload`) bank first, then gate the drain, so a declined/offline order is queued, not lost. -- Strings translated to all 33 locales (machine-generated, pending human review) - in `values*/strings_networkpolicy.xml`; `strings_untranslated.xml` is clear. +- Strings translated to all 33 locales (machine-generated, pending human review), + folded into `values*/strings.xml` (one string file; no per-feature file); + `strings_untranslated.xml` is clear. Done since: the Books and Kolibri commit-point prompts, the FQR maps-region seam -(sec.4.1) and the native-Kolibri import gate (sec.4.3), all device-verified on a -metered hotspot. Remaining to finish the contract: the base Maps runrole install, -the rootfs-image install (aria2), the DownloadManager seam (OTA / portal), and the -two-`hasInternet` fold. (l10n done pending review.) +(sec.4.1), the native-Kolibri import gate (sec.4.3), the dashboard LIVE update/install +gate and the Get-More base-Maps gate (sec.4), all device-verified on a metered hotspot +except the last two (pending a device pass). Every REST-heavy egress the user can +trigger on a live box now routes through the gate: ZIM, Books, Kolibri (Get More + +native), FQR regions, dashboard live update, and Get-More base maps. + +Remaining, DEFERRED to the install/rootfs PR ("el install va aparte con el rootfs"): +the wizard/system-install maps path (`mapsWizardConfirm`) and its headless hold with +an orchestrator "waiting for network" state; the rootfs-image install (aria2, not +REST). Separate follow-ups: the DownloadManager seam (OTA / portal, +`setAllowedOverMetered`) and the two-`hasInternet` fold into `AndroidNetworkClassifier`. +(l10n done pending human review.) ## 6. Device evidence appendix (dark surfaces flattened) @@ -327,7 +342,16 @@ happens BEFORE the gate (`SetupLibraryActivity.startZimDownload` banks, then gat the drain), so a declined or offline order is queued, not lost. This removes the commit-point special case rather than adding one. -The ZIM and Books commit points now bank-then-prompt. Still open as follow-ups: -the Kolibri commit-point prompt (its drain is already held; the ask needs care -because the flow banks on a background thread), the Maps seam (K2GO-394 reworked -Maps; re-locate it), DownloadManager (OTA/portal), and the two-`hasInternet` fold. +The ZIM, Books and Kolibri commit points now bank-then-prompt. The Get-More base-Maps +download (K2GO-394 moved its bytes onto REST) is gated at its UI commit +(`openMapsIndex`), NOT at `MapsProvisioner.drain`: unlike the content drains, the maps +drain is a serialized proot stage whose refusal is TERMINAL (`mapsStartFailed`), so a +cost-hold there would read as a hard failure, not a deferral -- the headless maps hold +needs an orchestrator "waiting for network" state and rides with the install/rootfs PR. +The dashboard LIVE update/install is gated at its UI commit (`startRest`). Both are +user-driven Operations, so they gate at the UI (like FQR), not through `ContentAdmission` +(which defers TO them -- routing back would be circular). + +Still open as follow-ups (see sec.5): the wizard/system-install maps path and its +headless hold (install/rootfs PR), the rootfs-image install (aria2), the DownloadManager +seam (OTA/portal), and the two-`hasInternet` fold. From 7f0d9e3fd873e2305add50f8020fddd28fd4e6a3 Mon Sep 17 00:00:00 2001 From: Luis Guzman Date: Wed, 16 Sep 2026 18:59:31 -0600 Subject: [PATCH 18/18] K2GO-395 fix(networkpolicy): guard dashboard startRest against host detach The metered gate defers startRest behind the consent dialog, so the host fragment can detach before Continue is tapped; host.requireContext() would then throw. Bail on !isAdded() first (same guard the snackbar already used and that KolibriConfirmFragment.commitLive uses). The update is not banked, so a dropped start on this rare window is re-triggerable from the card. From the second code-review pass on the new commits. --- .../org/appdevforall/k2go/redesign/DashboardRebuild.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/controller/app/src/main/java/org/appdevforall/k2go/redesign/DashboardRebuild.java b/controller/app/src/main/java/org/appdevforall/k2go/redesign/DashboardRebuild.java index c98bd6a89..e5ae0449b 100644 --- a/controller/app/src/main/java/org/appdevforall/k2go/redesign/DashboardRebuild.java +++ b/controller/app/src/main/java/org/appdevforall/k2go/redesign/DashboardRebuild.java @@ -127,8 +127,14 @@ private static void startProot(@NonNull Fragment host) { * reports done/error, with no time cap. A visible dashboard card refreshes on the service's * completion broadcast; nothing pins this screen. */ private static void startRest(@NonNull Fragment host, @NonNull View anchor, boolean updateSite) { + // K2GO-395: this now runs deferred behind the metered consent dialog, so the host fragment may + // have detached (config change / navigation) before the user taps Continue. Bail before + // requireContext() would throw -- consistent with the isAdded() guard already used for the + // snackbar below and with KolibriConfirmFragment.commitLive. The update is not banked, so a + // dropped start on this rare window is re-triggerable from the card, not a lost queue item. + if (!host.isAdded()) return; DashboardRebuildService.start(host.requireContext().getApplicationContext(), updateSite); - if (host.isAdded()) Snackbars.make(anchor, R.string.k2go_dash_update_started).show(); + Snackbars.make(anchor, R.string.k2go_dash_update_started).show(); } /** ADFA-5333: reverse gate for LIVE content downloads (ZIM/Books/Kolibri). Those run on the server