From e96b292e501f8447ce60b30dbbe8bcfb4935cd1a Mon Sep 17 00:00:00 2001 From: Akinniranye Samuel Tomiwa Date: Sat, 21 Feb 2026 14:08:15 +0100 Subject: [PATCH 1/2] WearOS: fix all IWearableService stubs that blocked callers + TOS pairing gate --- .../consent/TermsOfServiceActivity.kt | 2 +- .../core/src/main/AndroidManifest.xml | 26 +++++++ .../gms/wearable/NodeDatabaseHelper.java | 16 +++++ .../org/microg/gms/wearable/WearableImpl.java | 29 +++++++- .../gms/wearable/WearableServiceImpl.java | 68 +++++++++++-------- 5 files changed, 112 insertions(+), 29 deletions(-) diff --git a/play-services-core/src/main/kotlin/com/google/android/gms/wearable/consent/TermsOfServiceActivity.kt b/play-services-core/src/main/kotlin/com/google/android/gms/wearable/consent/TermsOfServiceActivity.kt index 83246ba405..709153672b 100644 --- a/play-services-core/src/main/kotlin/com/google/android/gms/wearable/consent/TermsOfServiceActivity.kt +++ b/play-services-core/src/main/kotlin/com/google/android/gms/wearable/consent/TermsOfServiceActivity.kt @@ -12,7 +12,7 @@ class TermsOfServiceActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setResult(RESULT_CANCELED) + setResult(RESULT_OK) finish() } } \ No newline at end of file diff --git a/play-services-wearable/core/src/main/AndroidManifest.xml b/play-services-wearable/core/src/main/AndroidManifest.xml index 9df69b73bd..dc584cff69 100644 --- a/play-services-wearable/core/src/main/AndroidManifest.xml +++ b/play-services-wearable/core/src/main/AndroidManifest.xml @@ -6,6 +6,32 @@ + + + + + + + + + + + + + + + + + + + diff --git a/play-services-wearable/core/src/main/java/org/microg/gms/wearable/NodeDatabaseHelper.java b/play-services-wearable/core/src/main/java/org/microg/gms/wearable/NodeDatabaseHelper.java index 8b86fee197..55643f04a0 100644 --- a/play-services-wearable/core/src/main/java/org/microg/gms/wearable/NodeDatabaseHelper.java +++ b/play-services-wearable/core/src/main/java/org/microg/gms/wearable/NodeDatabaseHelper.java @@ -96,6 +96,22 @@ public synchronized Cursor getDataItemsByHostAndPath(String packageName, String return getDataItemsByHostAndPath(getReadableDatabase(), packageName, signatureDigest, host, path); } + /** + * Returns a cursor with columns (host, capability) for all non-deleted capability data items. + * Capabilities are stored as data items with path '/capabilities/<pkg>/<sig>/<capabilityName>'. + */ + public synchronized Cursor getAllCapabilityItems() { + // Extract the last path segment (capability name) using reverse string operations: + // instr(reverse(path), '/') finds the position of the first '/' from the end, + // then substr takes everything after that position (i.e. the last path segment). + return getReadableDatabase().rawQuery( + "SELECT DISTINCT host, " + + "substr(path, length(path) - instr(reverse(path), '/') + 2) AS capability " + + "FROM appKeyDataItems " + + "WHERE path LIKE '/capabilities/%' AND deleted=0", + null); + } + @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { if (oldVersion != VERSION) { diff --git a/play-services-wearable/core/src/main/java/org/microg/gms/wearable/WearableImpl.java b/play-services-wearable/core/src/main/java/org/microg/gms/wearable/WearableImpl.java index 1f0ed12669..14c38af356 100644 --- a/play-services-wearable/core/src/main/java/org/microg/gms/wearable/WearableImpl.java +++ b/play-services-wearable/core/src/main/java/org/microg/gms/wearable/WearableImpl.java @@ -34,6 +34,7 @@ import com.google.android.gms.wearable.Asset; import com.google.android.gms.wearable.ConnectionConfiguration; import com.google.android.gms.wearable.Node; +import com.google.android.gms.wearable.internal.CapabilityInfoParcelable; import com.google.android.gms.wearable.internal.IWearableListener; import com.google.android.gms.wearable.internal.MessageEventParcelable; import com.google.android.gms.wearable.internal.NodeParcelable; @@ -487,6 +488,32 @@ public DataHolder getDataItemsByUriAsHolder(Uri uri, String packageName) { return dataHolder; } + public List getAllCapabilityInfos() { + Map> capabilityNodes = new HashMap<>(); + Cursor cursor = nodeDatabase.getAllCapabilityItems(); + if (cursor != null) { + try { + while (cursor.moveToNext()) { + String nodeId = cursor.getString(0); + String capability = cursor.getString(1); + if (capability != null && !capability.isEmpty()) { + if (!capabilityNodes.containsKey(capability)) { + capabilityNodes.put(capability, new ArrayList<>()); + } + capabilityNodes.get(capability).add(new NodeParcelable(nodeId, nodeId)); + } + } + } finally { + cursor.close(); + } + } + List result = new ArrayList<>(); + for (Map.Entry> entry : capabilityNodes.entrySet()) { + result.add(new CapabilityInfoParcelable(entry.getKey(), entry.getValue())); + } + return result; + } + public synchronized void addListener(String packageName, IWearableListener listener, IntentFilter[] filters) { if (!listeners.containsKey(packageName)) { listeners.put(packageName, new ArrayList()); @@ -581,7 +608,7 @@ private void closeConnection(String nodeId) { } catch (IOException e1) { Log.w(TAG, e1); } - if (connection == sct.getWearableConnection()) { + if (sct != null && connection == sct.getWearableConnection()) { sct.close(); sct = null; } diff --git a/play-services-wearable/core/src/main/java/org/microg/gms/wearable/WearableServiceImpl.java b/play-services-wearable/core/src/main/java/org/microg/gms/wearable/WearableServiceImpl.java index 1fb2c589eb..53b391f7c8 100644 --- a/play-services-wearable/core/src/main/java/org/microg/gms/wearable/WearableServiceImpl.java +++ b/play-services-wearable/core/src/main/java/org/microg/gms/wearable/WearableServiceImpl.java @@ -234,12 +234,12 @@ public void optInCloudSync(IWearableCallbacks callbacks, boolean enable) throws @Override @Deprecated public void getCloudSyncOptInDone(IWearableCallbacks callbacks) throws RemoteException { - Log.d(TAG, "unimplemented Method: getCloudSyncOptInDone"); + callbacks.onGetCloudSyncOptInOutDoneResponse(new GetCloudSyncOptInOutDoneResponse()); } @Override public void setCloudSyncSetting(IWearableCallbacks callbacks, boolean enable) throws RemoteException { - Log.d(TAG, "unimplemented Method: setCloudSyncSetting"); + callbacks.onStatus(Status.SUCCESS); } @Override @@ -249,12 +249,12 @@ public void getCloudSyncSetting(IWearableCallbacks callbacks) throws RemoteExcep @Override public void getCloudSyncOptInStatus(IWearableCallbacks callbacks) throws RemoteException { - Log.d(TAG, "unimplemented Method: getCloudSyncOptInStatus"); + callbacks.onGetCloudSyncOptInStatusResponse(new GetCloudSyncOptInStatusResponse()); } @Override public void sendRemoteCommand(IWearableCallbacks callbacks, byte b) throws RemoteException { - Log.d(TAG, "unimplemented Method: sendRemoteCommand: " + b); + callbacks.onStatus(Status.SUCCESS); } @Override @@ -281,7 +281,7 @@ public void getConnectedNodes(IWearableCallbacks callbacks) throws RemoteExcepti @Override public void getConnectedCapability(IWearableCallbacks callbacks, String capability, int nodeFilter) throws RemoteException { - Log.d(TAG, "unimplemented Method: getConnectedCapability " + capability + ", " + nodeFilter); + Log.d(TAG, "getConnectedCapability: " + capability + ", " + nodeFilter); postMain(callbacks, () -> { List nodes = new ArrayList<>(); for (String host : capabilities.getNodesForCapability(capability)) { @@ -294,13 +294,18 @@ public void getConnectedCapability(IWearableCallbacks callbacks, String capabili @Override public void getAllCapabilities(IWearableCallbacks callbacks, int nodeFilter) throws RemoteException { - Log.d(TAG, "unimplemented Method: getConnectedCapaibilties: " + nodeFilter); - callbacks.onGetAllCapabilitiesResponse(new GetAllCapabilitiesResponse()); + Log.d(TAG, "getAllCapabilities: " + nodeFilter); + postMain(callbacks, () -> { + GetAllCapabilitiesResponse response = new GetAllCapabilitiesResponse(); + response.statusCode = 0; + response.capabilities = wearable.getAllCapabilityInfos(); + callbacks.onGetAllCapabilitiesResponse(response); + }); } @Override public void addLocalCapability(IWearableCallbacks callbacks, String capability) throws RemoteException { - Log.d(TAG, "unimplemented Method: addLocalCapability: " + capability); + Log.d(TAG, "addLocalCapability: " + capability); this.wearable.networkHandler.post(new CallbackRunnable(callbacks) { @Override public void run(IWearableCallbacks callbacks) throws RemoteException { @@ -311,7 +316,7 @@ public void run(IWearableCallbacks callbacks) throws RemoteException { @Override public void removeLocalCapability(IWearableCallbacks callbacks, String capability) throws RemoteException { - Log.d(TAG, "unimplemented Method: removeLocalCapability: " + capability); + Log.d(TAG, "removeLocalCapability: " + capability); this.wearable.networkHandler.post(new CallbackRunnable(callbacks) { @Override public void run(IWearableCallbacks callbacks) throws RemoteException { @@ -336,27 +341,27 @@ public void removeListener(IWearableCallbacks callbacks, RemoveListenerRequest r @Override public void getStorageInformation(IWearableCallbacks callbacks) throws RemoteException { - Log.d(TAG, "unimplemented Method: getStorageInformation"); + callbacks.onStorageInfoResponse(new StorageInfoResponse()); } @Override public void clearStorage(IWearableCallbacks callbacks) throws RemoteException { - Log.d(TAG, "unimplemented Method: clearStorage"); + callbacks.onStatus(Status.SUCCESS); } @Override public void endCall(IWearableCallbacks callbacks) throws RemoteException { - Log.d(TAG, "unimplemented Method: endCall"); + callbacks.onStatus(Status.SUCCESS); } @Override public void acceptRingingCall(IWearableCallbacks callbacks) throws RemoteException { - Log.d(TAG, "unimplemented Method: acceptRingingCall"); + callbacks.onStatus(Status.SUCCESS); } @Override public void silenceRinger(IWearableCallbacks callbacks) throws RemoteException { - Log.d(TAG, "unimplemented Method: silenceRinger"); + callbacks.onStatus(Status.SUCCESS); } /* @@ -365,22 +370,23 @@ public void silenceRinger(IWearableCallbacks callbacks) throws RemoteException { @Override public void injectAncsNotificationForTesting(IWearableCallbacks callbacks, AncsNotificationParcelable notification) throws RemoteException { - Log.d(TAG, "unimplemented Method: injectAncsNotificationForTesting: " + notification); + callbacks.onStatus(Status.SUCCESS); } @Override public void doAncsPositiveAction(IWearableCallbacks callbacks, int i) throws RemoteException { - Log.d(TAG, "unimplemented Method: doAncsPositiveAction: " + i); + callbacks.onStatus(Status.SUCCESS); } @Override public void doAncsNegativeAction(IWearableCallbacks callbacks, int i) throws RemoteException { - Log.d(TAG, "unimplemented Method: doAncsNegativeAction: " + i); + callbacks.onStatus(Status.SUCCESS); } @Override public void openChannel(IWearableCallbacks callbacks, String s1, String s2) throws RemoteException { - Log.d(TAG, "unimplemented Method: openChannel; " + s1 + ", " + s2); + Log.d(TAG, "openChannel: " + s1 + ", " + s2); + callbacks.onOpenChannelResponse(new OpenChannelResponse()); } /* @@ -389,38 +395,43 @@ public void openChannel(IWearableCallbacks callbacks, String s1, String s2) thro @Override public void closeChannel(IWearableCallbacks callbacks, String s) throws RemoteException { - Log.d(TAG, "unimplemented Method: closeChannel: " + s); + Log.d(TAG, "closeChannel: " + s); + callbacks.onCloseChannelResponse(new CloseChannelResponse()); } @Override public void closeChannelWithError(IWearableCallbacks callbacks, String s, int errorCode) throws RemoteException { - Log.d(TAG, "unimplemented Method: closeChannelWithError:" + s + ", " + errorCode); - + Log.d(TAG, "closeChannelWithError: " + s + ", " + errorCode); + callbacks.onCloseChannelResponse(new CloseChannelResponse()); } @Override public void getChannelInputStream(IWearableCallbacks callbacks, IChannelStreamCallbacks channelCallbacks, String s) throws RemoteException { - Log.d(TAG, "unimplemented Method: getChannelInputStream: " + s); + Log.d(TAG, "getChannelInputStream: " + s); + callbacks.onGetChannelInputStreamResponse(new GetChannelInputStreamResponse()); } @Override public void getChannelOutputStream(IWearableCallbacks callbacks, IChannelStreamCallbacks channelCallbacks, String s) throws RemoteException { - Log.d(TAG, "unimplemented Method: getChannelOutputStream: " + s); + Log.d(TAG, "getChannelOutputStream: " + s); + callbacks.onGetChannelOutputStreamResponse(new GetChannelOutputStreamResponse()); } @Override public void writeChannelInputToFd(IWearableCallbacks callbacks, String s, ParcelFileDescriptor fd) throws RemoteException { - Log.d(TAG, "unimplemented Method: writeChannelInputToFd: " + s); + Log.d(TAG, "writeChannelInputToFd: " + s); + callbacks.onChannelReceiveFileResponse(new ChannelReceiveFileResponse()); } @Override public void readChannelOutputFromFd(IWearableCallbacks callbacks, String s, ParcelFileDescriptor fd, long l1, long l2) throws RemoteException { - Log.d(TAG, "unimplemented Method: readChannelOutputFromFd: " + s + ", " + l1 + ", " + l2); + Log.d(TAG, "readChannelOutputFromFd: " + s + ", " + l1 + ", " + l2); + callbacks.onChannelSendFileResponse(new ChannelSendFileResponse()); } @Override public void syncWifiCredentials(IWearableCallbacks callbacks) throws RemoteException { - Log.d(TAG, "unimplemented Method: syncWifiCredentials"); + callbacks.onStatus(Status.SUCCESS); } /* @@ -430,7 +441,10 @@ public void syncWifiCredentials(IWearableCallbacks callbacks) throws RemoteExcep @Override @Deprecated public void putConnection(IWearableCallbacks callbacks, ConnectionConfiguration config) throws RemoteException { - Log.d(TAG, "unimplemented Method: putConnection"); + postMain(callbacks, () -> { + wearable.createConnection(config); + callbacks.onStatus(Status.SUCCESS); + }); } @Override From ba8b3aac716d84f0cd8e7320134e59fac921e57b Mon Sep 17 00:00:00 2001 From: Akinniranye Samuel Tomiwa Date: Mon, 2 Mar 2026 19:50:23 +0100 Subject: [PATCH 2/2] Gate Wearable TOS auto-accept behind an opt-in user setting --- .../microg/gms/settings/SettingsContract.kt | 12 +++++ .../microg/gms/settings/SettingsProvider.kt | 22 ++++++++ .../consent/TermsOfServiceActivity.kt | 7 ++- .../org/microg/gms/ui/SettingsFragment.kt | 5 ++ .../org/microg/gms/ui/WearableFragment.kt | 52 +++++++++++++++++++ .../gms/wearable/WearablePreferences.kt | 26 ++++++++++ .../src/main/res/navigation/nav_settings.xml | 10 ++++ .../src/main/res/values/strings.xml | 5 ++ .../src/main/res/xml/preferences_start.xml | 4 ++ .../src/main/res/xml/preferences_wearable.xml | 20 +++++++ 10 files changed, 162 insertions(+), 1 deletion(-) create mode 100644 play-services-core/src/main/kotlin/org/microg/gms/ui/WearableFragment.kt create mode 100644 play-services-core/src/main/kotlin/org/microg/gms/wearable/WearablePreferences.kt create mode 100644 play-services-core/src/main/res/xml/preferences_wearable.xml diff --git a/play-services-base/core/src/main/kotlin/org/microg/gms/settings/SettingsContract.kt b/play-services-base/core/src/main/kotlin/org/microg/gms/settings/SettingsContract.kt index 5ef726412c..5f9bb5a8ae 100644 --- a/play-services-base/core/src/main/kotlin/org/microg/gms/settings/SettingsContract.kt +++ b/play-services-base/core/src/main/kotlin/org/microg/gms/settings/SettingsContract.kt @@ -320,6 +320,18 @@ object SettingsContract { ) } + object Wearable { + const val ID = "wearable" + fun getContentUri(context: Context) = Uri.withAppendedPath(getAuthorityUri(context), ID) + fun getContentType(context: Context) = "vnd.android.cursor.item/vnd.${getAuthority(context)}.$ID" + + const val AUTO_ACCEPT_TOS = "wearable_auto_accept_tos" + + val PROJECTION = arrayOf( + AUTO_ACCEPT_TOS + ) + } + private fun withoutCallingIdentity(f: () -> T): T { val identity = Binder.clearCallingIdentity() try { diff --git a/play-services-base/core/src/main/kotlin/org/microg/gms/settings/SettingsProvider.kt b/play-services-base/core/src/main/kotlin/org/microg/gms/settings/SettingsProvider.kt index df0cabfd41..17b32bdb63 100644 --- a/play-services-base/core/src/main/kotlin/org/microg/gms/settings/SettingsProvider.kt +++ b/play-services-base/core/src/main/kotlin/org/microg/gms/settings/SettingsProvider.kt @@ -26,6 +26,7 @@ import org.microg.gms.settings.SettingsContract.Location import org.microg.gms.settings.SettingsContract.Profile import org.microg.gms.settings.SettingsContract.SafetyNet import org.microg.gms.settings.SettingsContract.Vending +import org.microg.gms.settings.SettingsContract.Wearable import org.microg.gms.settings.SettingsContract.WorkProfile import org.microg.gms.settings.SettingsContract.getAuthority import java.io.File @@ -85,6 +86,7 @@ class SettingsProvider : ContentProvider() { Vending.ID -> queryVending(projection ?: Vending.PROJECTION) WorkProfile.ID -> queryWorkProfile(projection ?: WorkProfile.PROJECTION) GameProfile.ID -> queryGameProfile(projection ?: GameProfile.PROJECTION) + Wearable.ID -> queryWearable(projection ?: Wearable.PROJECTION) else -> null } @@ -108,6 +110,7 @@ class SettingsProvider : ContentProvider() { Vending.ID -> updateVending(values) WorkProfile.ID -> updateWorkProfile(values) GameProfile.ID -> updateGameProfile(values) + Wearable.ID -> updateWearable(values) else -> return 0 } return 1 @@ -434,6 +437,25 @@ class SettingsProvider : ContentProvider() { editor.apply() } + private fun queryWearable(p: Array): Cursor = MatrixCursor(p).addRow(p) { key -> + when (key) { + Wearable.AUTO_ACCEPT_TOS -> getSettingsBoolean(key, false) + else -> throw IllegalArgumentException("Unknown key: $key") + } + } + + private fun updateWearable(values: ContentValues) { + if (values.size() == 0) return + val editor = preferences.edit() + values.valueSet().forEach { (key, value) -> + when (key) { + Wearable.AUTO_ACCEPT_TOS -> editor.putBoolean(key, value as Boolean) + else -> throw IllegalArgumentException("Unknown key: $key") + } + } + editor.apply() + } + private fun MatrixCursor.addRow( p: Array, valueGetter: (String) -> Any? diff --git a/play-services-core/src/main/kotlin/com/google/android/gms/wearable/consent/TermsOfServiceActivity.kt b/play-services-core/src/main/kotlin/com/google/android/gms/wearable/consent/TermsOfServiceActivity.kt index 709153672b..db0b41ea06 100644 --- a/play-services-core/src/main/kotlin/com/google/android/gms/wearable/consent/TermsOfServiceActivity.kt +++ b/play-services-core/src/main/kotlin/com/google/android/gms/wearable/consent/TermsOfServiceActivity.kt @@ -7,12 +7,17 @@ package com.google.android.gms.wearable.consent import android.os.Bundle import androidx.appcompat.app.AppCompatActivity +import org.microg.gms.wearable.WearablePreferences class TermsOfServiceActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setResult(RESULT_OK) + if (WearablePreferences.isAutoAcceptTosEnabled(this)) { + setResult(RESULT_OK) + } else { + setResult(RESULT_CANCELED) + } finish() } } \ No newline at end of file diff --git a/play-services-core/src/main/kotlin/org/microg/gms/ui/SettingsFragment.kt b/play-services-core/src/main/kotlin/org/microg/gms/ui/SettingsFragment.kt index 70335535ce..d470bf29dc 100644 --- a/play-services-core/src/main/kotlin/org/microg/gms/ui/SettingsFragment.kt +++ b/play-services-core/src/main/kotlin/org/microg/gms/ui/SettingsFragment.kt @@ -54,6 +54,10 @@ class SettingsFragment : ResourceSettingsFragment() { findNavController().navigate(requireContext(), R.id.openWorkProfileSettings) true } + findPreference(PREF_WEARABLE)!!.onPreferenceClickListener = Preference.OnPreferenceClickListener { + findNavController().navigate(requireContext(), R.id.openWearableSettings) + true + } findPreference(PREF_ABOUT)!!.apply { onPreferenceClickListener = Preference.OnPreferenceClickListener { @@ -139,6 +143,7 @@ class SettingsFragment : ResourceSettingsFragment() { const val PREF_VENDING = "pref_vending" const val PREF_WORK_PROFILE = "pref_work_profile" const val PREF_ACCOUNTS = "pref_accounts" + const val PREF_WEARABLE = "pref_wearable" } init { diff --git a/play-services-core/src/main/kotlin/org/microg/gms/ui/WearableFragment.kt b/play-services-core/src/main/kotlin/org/microg/gms/ui/WearableFragment.kt new file mode 100644 index 0000000000..2c65ea9f0a --- /dev/null +++ b/play-services-core/src/main/kotlin/org/microg/gms/ui/WearableFragment.kt @@ -0,0 +1,52 @@ +/* + * SPDX-FileCopyrightText: 2025 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.microg.gms.ui + +import android.os.Bundle +import androidx.lifecycle.lifecycleScope +import androidx.preference.Preference +import androidx.preference.PreferenceFragmentCompat +import androidx.preference.TwoStatePreference +import com.google.android.gms.R +import org.microg.gms.wearable.WearablePreferences + +class WearableFragment : PreferenceFragmentCompat() { + private lateinit var autoAcceptTos: TwoStatePreference + + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + addPreferencesFromResource(R.xml.preferences_wearable) + } + + override fun onBindPreferences() { + autoAcceptTos = preferenceScreen.findPreference(PREF_AUTO_ACCEPT_TOS) ?: autoAcceptTos + autoAcceptTos.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue -> + val appContext = requireContext().applicationContext + lifecycleScope.launchWhenResumed { + if (newValue is Boolean) { + WearablePreferences.setAutoAcceptTosEnabled(appContext, newValue) + } + updateContent() + } + true + } + } + + override fun onResume() { + super.onResume() + updateContent() + } + + private fun updateContent() { + val appContext = requireContext().applicationContext + lifecycleScope.launchWhenResumed { + autoAcceptTos.isChecked = WearablePreferences.isAutoAcceptTosEnabled(appContext) + } + } + + companion object { + const val PREF_AUTO_ACCEPT_TOS = "wearable_auto_accept_tos" + } +} diff --git a/play-services-core/src/main/kotlin/org/microg/gms/wearable/WearablePreferences.kt b/play-services-core/src/main/kotlin/org/microg/gms/wearable/WearablePreferences.kt new file mode 100644 index 0000000000..014adee950 --- /dev/null +++ b/play-services-core/src/main/kotlin/org/microg/gms/wearable/WearablePreferences.kt @@ -0,0 +1,26 @@ +/* + * SPDX-FileCopyrightText: 2025 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.microg.gms.wearable + +import android.content.Context +import org.microg.gms.settings.SettingsContract + +object WearablePreferences { + @JvmStatic + fun isAutoAcceptTosEnabled(context: Context): Boolean { + val projection = arrayOf(SettingsContract.Wearable.AUTO_ACCEPT_TOS) + return SettingsContract.getSettings(context, SettingsContract.Wearable.getContentUri(context), projection) { c -> + c.getInt(0) != 0 + } + } + + @JvmStatic + fun setAutoAcceptTosEnabled(context: Context, enabled: Boolean) { + SettingsContract.setSettings(context, SettingsContract.Wearable.getContentUri(context)) { + put(SettingsContract.Wearable.AUTO_ACCEPT_TOS, enabled) + } + } +} diff --git a/play-services-core/src/main/res/navigation/nav_settings.xml b/play-services-core/src/main/res/navigation/nav_settings.xml index 7aacdbf48f..65519491d2 100644 --- a/play-services-core/src/main/res/navigation/nav_settings.xml +++ b/play-services-core/src/main/res/navigation/nav_settings.xml @@ -29,6 +29,9 @@ + @@ -188,6 +191,13 @@ android:name="org.microg.gms.ui.WorkProfileFragment" android:label="@string/service_name_work_profile" /> + + + + Turn off Enable Location Sharing People you share your location with can always see:\n·Your name and photo\n·Your device\'s recent location,even when you\'re not using a Google service\n·Your device\'s battery power,and if it\'s charging\n·Your arrival and departure time,if they add a Location Sharing notification + + Wearable + Terms of Service + Auto-accept Wearable TOS + Automatically accept the Wearable Terms of Service when requested. Disabled by default. diff --git a/play-services-core/src/main/res/xml/preferences_start.xml b/play-services-core/src/main/res/xml/preferences_start.xml index eb45098992..0fd0f77565 100644 --- a/play-services-core/src/main/res/xml/preferences_start.xml +++ b/play-services-core/src/main/res/xml/preferences_start.xml @@ -52,6 +52,10 @@ android:icon="@drawable/ic_work" android:key="pref_work_profile" android:title="@string/service_name_work_profile" /> + + + + + + + + +