Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions play-services-core/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,7 @@
<activity
android:name="com.google.android.gms.wearable.consent.TermsOfServiceActivity"
android:process=":ui"
android:exported="true"
android:configChanges="screenSize|orientation|keyboardHidden">
<intent-filter>
<action android:name="com.google.android.gms.wearable.TOS"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,51 @@

package com.google.android.gms.wearable.consent

import android.content.Intent
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.google.android.gms.R
import com.google.android.material.button.MaterialButton


class TermsOfServiceActivity : AppCompatActivity() {

private var acceptButton: MaterialButton? = null
private var declineButton: MaterialButton? = null

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setResult(RESULT_CANCELED)
// setResult(RESULT_CANCELED)
// finish()

// TODO: make consent list
setContentView(R.layout.activity_wearable_tos);

acceptButton = findViewById(R.id.terms_of_service_accept_button);
declineButton = findViewById(R.id.terms_of_service_decline_button);

acceptButton?.setOnClickListener { acceptConsents() }
declineButton?.setOnClickListener { declineConsents() }

}

private fun acceptConsents() {
val result = Intent().apply {
putExtra("consents_accepted", true)
putExtra("tos_accepted", true)
putExtra("privacy_policy_accepted", true)
}

setResult(RESULT_OK, result)
finish()
}

private fun declineConsents() {
val result = Intent().apply {
putExtra("consents_accepted", false)
}

setResult(RESULT_CANCELED, result)
finish()
}
}
66 changes: 66 additions & 0 deletions play-services-core/src/main/res/layout/activity_wearable_tos.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:keepScreenOn="true">
<androidx.core.widget.NestedScrollView
android:id="@+id/terms_of_service_scroll_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="vertical"
android:id="@+id/header"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/header_image"
android:layout_height="48dp"
android:layout_width="48dp"/>
<TextView
android:id="@+id/terms_of_service_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Terms of service"/>
</LinearLayout>
<TextView
android:id="@+id/terms_of_service_subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/terms_of_service_list"
android:clipToPadding="false"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
<androidx.constraintlayout.widget.ConstraintLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent">
<com.google.android.material.button.MaterialButton
android:id="@+id/terms_of_service_accept_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="I agree"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<com.google.android.material.button.MaterialButton
android:id="@+id/terms_of_service_decline_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Decline"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
9 changes: 7 additions & 2 deletions play-services-wearable/core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,13 @@ apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'maven-publish'
apply plugin: 'signing'
apply plugin: 'com.squareup.wire'

dependencies {
implementation project(':play-services-base-core')

implementation project(':play-services-location')
implementation project(':play-services-wearable')

implementation "org.microg:wearable:$wearableVersion"
}

android {
Expand Down Expand Up @@ -51,6 +50,12 @@ android {
}
}

wire {
java {

}
}

apply from: '../../gradle/publish-android.gradle'

description = 'microG service implementation for play-services-wearable'
8 changes: 8 additions & 0 deletions play-services-wearable/core/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@

<manifest xmlns:android="http://schemas.android.com/apk/res/android">

<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />

<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />

<application>
</application>
</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import android.content.Context;
import android.net.Uri;
import android.util.Log;

import com.google.android.gms.common.api.CommonStatusCodes;
import com.google.android.gms.common.data.DataHolder;
Expand All @@ -27,23 +28,57 @@

import org.microg.gms.common.PackageUtils;

import java.nio.charset.StandardCharsets;
import java.util.HashSet;
import java.util.Set;

public class CapabilityManager {
private static final String TAG = "CapabilityManager";

private static final Uri ROOT = Uri.parse("wear:/capabilities/");
private final Context context;
private final WearableImpl wearable;
private final String packageName;

private Set<String> capabilities = new HashSet<String>();
private final Object lock = new Object();

private final Set<String> capabilities = new HashSet<String>();

public CapabilityManager(Context context, WearableImpl wearable, String packageName) {
this.context = context;
this.wearable = wearable;
this.packageName = packageName;
}

public enum CapabilityType {
STATIC("s", "+", "+#"),
DYNAMIC("d", "-", "-#");

public final String typeCode;
public final String addSymbol;
public final String addSymbolWithHash;

CapabilityType(String typeCode, String addSymbol, String addSymbolWithHash) {
this.typeCode = typeCode;
this.addSymbol = addSymbol;
this.addSymbolWithHash = addSymbolWithHash;
}

public static CapabilityType fromBytes(byte[] data) {
if (data == null || data.length == 0) return DYNAMIC;

String code = new String(data, 0, 1, StandardCharsets.UTF_8);

if (STATIC.typeCode.equals(code)) return STATIC;

return DYNAMIC;
}

public byte[] toBytes() {
return typeCode.getBytes(StandardCharsets.UTF_8);
}
}

private Uri buildCapabilityUri(String capability, boolean withAuthority) {
Uri.Builder builder = ROOT.buildUpon();
if (withAuthority) builder.authority(wearable.getLocalNodeId());
Expand All @@ -56,30 +91,110 @@ private Uri buildCapabilityUri(String capability, boolean withAuthority) {
public Set<String> getNodesForCapability(String capability) {
DataHolder dataHolder = wearable.getDataItemsByUriAsHolder(buildCapabilityUri(capability, false), packageName);
Set<String> nodes = new HashSet<>();
for (int i = 0; i < dataHolder.getCount(); i++) {
nodes.add(dataHolder.getString("host", i, 0));
try{
for (int i = 0; i < dataHolder.getCount(); i++) {
nodes.add(dataHolder.getString("host", i, 0));
}
} finally {
dataHolder.close();
}
dataHolder.close();
return nodes;
}

public int add(String capability) {
if (this.capabilities.contains(capability)) {
return WearableStatusCodes.DUPLICATE_CAPABILITY;
return addWithType(capability, CapabilityType.DYNAMIC);
// if (this.capabilities.contains(capability)) {
// return WearableStatusCodes.DUPLICATE_CAPABILITY;
// }
// DataItemInternal dataItem = new DataItemInternal(buildCapabilityUri(capability, true));
// DataItemRecord record = wearable.putDataItem(packageName, PackageUtils.firstSignatureDigest(context, packageName), wearable.getLocalNodeId(), dataItem);
// this.capabilities.add(capability);
// wearable.syncRecordToAll(record);
// return CommonStatusCodes.SUCCESS;
}

public int addWithType(String capability, CapabilityType type) {
synchronized (lock) {
Uri uri = buildCapabilityUri(capability, true);
DataHolder existingData = wearable.getDataItemsByUriAsHolder(uri, packageName);

try {
if (existingData.getCount() > 0) {
byte[] data = existingData.getByteArray("data", 0, 0);
CapabilityType existingType = CapabilityType.fromBytes(data);

if (existingType == CapabilityType.STATIC || type == CapabilityType.DYNAMIC) {
return WearableStatusCodes.DUPLICATE_CAPABILITY;
}
}
} finally {
existingData.close();
}

DataItemInternal dataItem = new DataItemInternal(uri);
dataItem.data = type.toBytes();

DataItemRecord record = wearable.putDataItem(
packageName,
PackageUtils.firstSignatureDigest(context, packageName),
wearable.getLocalNodeId(),
dataItem
);

if (record != null) {
capabilities.add(capability);
wearable.syncRecordToAll(record);
Log.d(TAG, "Added capability: " + capability + " (type=" + type + ")");
return CommonStatusCodes.SUCCESS;
} else {
Log.e(TAG, "Failed to add capability: " + capability);
return CommonStatusCodes.ERROR;
}
}
DataItemInternal dataItem = new DataItemInternal(buildCapabilityUri(capability, true));
DataItemRecord record = wearable.putDataItem(packageName, PackageUtils.firstSignatureDigest(context, packageName), wearable.getLocalNodeId(), dataItem);
this.capabilities.add(capability);
wearable.syncRecordToAll(record);
return CommonStatusCodes.SUCCESS;
}

public int remove(String capability) {
if (!this.capabilities.contains(capability)) {
return WearableStatusCodes.UNKNOWN_CAPABILITY;
synchronized (lock) {
if (!capabilities.contains(capability)) {
Uri uri = buildCapabilityUri(capability, true);
DataHolder existingData = wearable.getDataItemsByUriAsHolder(uri, packageName);
try {
if (existingData.getCount() == 0) {
Log.w(TAG, "Capability not found: " + capability);
return WearableStatusCodes.UNKNOWN_CAPABILITY;
}
} finally {
existingData.close();
}
}

// if (!this.capabilities.contains(capability)) {
// return WearableStatusCodes.UNKNOWN_CAPABILITY;
// }
wearable.deleteDataItems(buildCapabilityUri(capability, true), packageName);
capabilities.remove(capability);
Log.d(TAG, "Removed capability: " + capability);
return CommonStatusCodes.SUCCESS;
}
}

public Set<String> getLocalCapabilities() {
synchronized (lock) {
return new HashSet<>(capabilities);
}
}

public boolean hasCapability(String capability) {
synchronized (lock) {
return capabilities.contains(capability);
}
}

public void clearAll() {
synchronized (lock) {
for (String capability: new HashSet<>(capabilities)) {
remove(capability);
}
}
wearable.deleteDataItems(buildCapabilityUri(capability, true), packageName);
capabilities.remove(capability);
return CommonStatusCodes.SUCCESS;
}
}
Loading