Repo Created
This commit is contained in:
parent
eb305e2886
commit
a8c22c65db
4784 changed files with 329907 additions and 2 deletions
55
play-services-wearable/build.gradle
Normal file
55
play-services-wearable/build.gradle
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2015 microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'com.squareup.wire'
|
||||
apply plugin: 'maven-publish'
|
||||
apply plugin: 'signing'
|
||||
|
||||
android {
|
||||
namespace "com.google.android.gms.wearable"
|
||||
|
||||
compileSdkVersion androidCompileSdk
|
||||
buildToolsVersion "$androidBuildVersionTools"
|
||||
|
||||
buildFeatures {
|
||||
aidl = true
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
versionName version
|
||||
minSdkVersion androidMinSdk
|
||||
targetSdkVersion androidTargetSdk
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = 1.8
|
||||
targetCompatibility = 1.8
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
disable 'InvalidPackage'
|
||||
}
|
||||
}
|
||||
|
||||
wire {
|
||||
java {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
apply from: '../gradle/publish-android.gradle'
|
||||
|
||||
description = 'microG implementation of play-services-wearable'
|
||||
|
||||
dependencies {
|
||||
implementation "com.squareup.wire:wire-runtime:$wireVersion"
|
||||
|
||||
// Dependencies from play-services-wearable:17.1.0
|
||||
api "androidx.core:core:1.0.0"
|
||||
api project(':play-services-base')
|
||||
api project(':play-services-basement')
|
||||
api project(':play-services-tasks')
|
||||
}
|
||||
56
play-services-wearable/core/build.gradle
Normal file
56
play-services-wearable/core/build.gradle
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'maven-publish'
|
||||
apply plugin: 'signing'
|
||||
|
||||
dependencies {
|
||||
implementation project(':play-services-base-core')
|
||||
|
||||
implementation project(':play-services-location')
|
||||
implementation project(':play-services-wearable')
|
||||
|
||||
implementation "org.microg:wearable:$wearableVersion"
|
||||
}
|
||||
|
||||
android {
|
||||
namespace "org.microg.gms.wearable.core"
|
||||
|
||||
compileSdkVersion androidCompileSdk
|
||||
buildToolsVersion "$androidBuildVersionTools"
|
||||
|
||||
defaultConfig {
|
||||
versionName version
|
||||
minSdkVersion androidMinSdk
|
||||
targetSdkVersion androidTargetSdk
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
dataBinding = true
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main.java.srcDirs += 'src/main/kotlin'
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
disable 'MissingTranslation'
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = 1.8
|
||||
targetCompatibility = 1.8
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = 1.8
|
||||
}
|
||||
}
|
||||
|
||||
apply from: '../../gradle/publish-android.gradle'
|
||||
|
||||
description = 'microG service implementation for play-services-wearable'
|
||||
11
play-services-wearable/core/src/main/AndroidManifest.xml
Normal file
11
play-services-wearable/core/src/main/AndroidManifest.xml
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ SPDX-FileCopyrightText: 2022 microG Project Team
|
||||
~ SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<application>
|
||||
</application>
|
||||
</manifest>
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* Copyright (C) 2019 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.microg.gms.wearable;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
|
||||
import com.google.android.gms.common.api.CommonStatusCodes;
|
||||
import com.google.android.gms.common.data.DataHolder;
|
||||
import com.google.android.gms.wearable.Node;
|
||||
import com.google.android.gms.wearable.WearableStatusCodes;
|
||||
import com.google.android.gms.wearable.internal.NodeParcelable;
|
||||
|
||||
import org.microg.gms.common.PackageUtils;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class 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>();
|
||||
|
||||
public CapabilityManager(Context context, WearableImpl wearable, String packageName) {
|
||||
this.context = context;
|
||||
this.wearable = wearable;
|
||||
this.packageName = packageName;
|
||||
}
|
||||
|
||||
private Uri buildCapabilityUri(String capability, boolean withAuthority) {
|
||||
Uri.Builder builder = ROOT.buildUpon();
|
||||
if (withAuthority) builder.authority(wearable.getLocalNodeId());
|
||||
builder.appendPath(packageName);
|
||||
builder.appendPath(PackageUtils.firstSignatureDigest(context, packageName));
|
||||
builder.appendPath(Uri.encode(capability));
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
dataHolder.close();
|
||||
return nodes;
|
||||
}
|
||||
|
||||
public int add(String capability) {
|
||||
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 remove(String capability) {
|
||||
if (!this.capabilities.contains(capability)) {
|
||||
return WearableStatusCodes.UNKNOWN_CAPABILITY;
|
||||
}
|
||||
wearable.deleteDataItems(buildCapabilityUri(capability, true), packageName);
|
||||
capabilities.remove(capability);
|
||||
return CommonStatusCodes.SUCCESS;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.microg.gms.wearable;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class ClockworkNodePreferences {
|
||||
|
||||
private static final String CLOCKWORK_NODE_PREFERENCES = "cw_node";
|
||||
private static final String CLOCKWORK_NODE_PREFERENCE_NODE_ID = "node_id";
|
||||
private static final String CLOCKWORK_NODE_PREFERENCE_NEXT_SEQ_ID_BLOCK = "nextSeqIdBlock";
|
||||
|
||||
private static final Object lock = new Object();
|
||||
private static long seqIdBlock;
|
||||
private static long seqIdInBlock = -1;
|
||||
|
||||
private Context context;
|
||||
|
||||
public ClockworkNodePreferences(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public String getLocalNodeId() {
|
||||
SharedPreferences preferences = context.getSharedPreferences(CLOCKWORK_NODE_PREFERENCES, Context.MODE_PRIVATE);
|
||||
String nodeId = preferences.getString(CLOCKWORK_NODE_PREFERENCE_NODE_ID, null);
|
||||
if (nodeId == null) {
|
||||
nodeId = UUID.randomUUID().toString();
|
||||
preferences.edit().putString(CLOCKWORK_NODE_PREFERENCE_NODE_ID, nodeId).apply();
|
||||
}
|
||||
return nodeId;
|
||||
}
|
||||
|
||||
public long getNextSeqId() {
|
||||
synchronized (lock) {
|
||||
SharedPreferences preferences = context.getSharedPreferences(CLOCKWORK_NODE_PREFERENCES, Context.MODE_PRIVATE);
|
||||
if (seqIdInBlock < 0) seqIdInBlock = 1000;
|
||||
if (seqIdInBlock >= 1000) {
|
||||
seqIdBlock = preferences.getLong(CLOCKWORK_NODE_PREFERENCE_NEXT_SEQ_ID_BLOCK, 100);
|
||||
preferences.edit().putLong(CLOCKWORK_NODE_PREFERENCE_NEXT_SEQ_ID_BLOCK, seqIdBlock + seqIdInBlock).apply();
|
||||
seqIdInBlock = 0;
|
||||
}
|
||||
return seqIdBlock + seqIdInBlock++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.microg.gms.wearable;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
|
||||
import com.google.android.gms.wearable.ConnectionConfiguration;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
public class ConfigurationDatabaseHelper extends SQLiteOpenHelper {
|
||||
|
||||
public static final String NULL_STRING = "NULL_STRING";
|
||||
public static final String TABLE_NAME = "connectionConfigurations";
|
||||
public static final String BY_NAME = "name=?";
|
||||
|
||||
public ConfigurationDatabaseHelper(Context context) {
|
||||
super(context, "connectionconfig.db", null, 2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase db) {
|
||||
db.execSQL("CREATE TABLE connectionConfigurations (_id INTEGER PRIMARY KEY AUTOINCREMENT,androidId TEXT,name TEXT NOT NULL,pairedBtAddress TEXT NOT NULL,connectionType INTEGER NOT NULL,role INTEGER NOT NULL,connectionEnabled INTEGER NOT NULL,nodeId TEXT, UNIQUE(name) ON CONFLICT REPLACE);");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
|
||||
}
|
||||
|
||||
private static ConnectionConfiguration configFromCursor(final Cursor cursor) {
|
||||
String name = cursor.getString(cursor.getColumnIndexOrThrow("name"));
|
||||
String pairedBtAddress = cursor.getString(cursor.getColumnIndexOrThrow("pairedBtAddress"));
|
||||
int connectionType = cursor.getInt(cursor.getColumnIndexOrThrow("connectionType"));
|
||||
int role = cursor.getInt(cursor.getColumnIndexOrThrow("role"));
|
||||
int enabled = cursor.getInt(cursor.getColumnIndexOrThrow("connectionEnabled"));
|
||||
String nodeId = cursor.getString(cursor.getColumnIndexOrThrow("nodeId"));
|
||||
if (NULL_STRING.equals(name)) name = null;
|
||||
if (NULL_STRING.equals(pairedBtAddress)) pairedBtAddress = null;
|
||||
return new ConnectionConfiguration(name, pairedBtAddress, connectionType, role, enabled > 0, nodeId);
|
||||
}
|
||||
|
||||
public ConnectionConfiguration getConfiguration(String name) {
|
||||
Cursor cursor = getReadableDatabase().query(TABLE_NAME, null, BY_NAME, new String[]{name}, null, null, null);
|
||||
ConnectionConfiguration config = null;
|
||||
if (cursor != null) {
|
||||
if (cursor.moveToNext())
|
||||
config = configFromCursor(cursor);
|
||||
cursor.close();
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
public void putConfiguration(ConnectionConfiguration config) {
|
||||
putConfiguration(config, null);
|
||||
}
|
||||
|
||||
public void putConfiguration(ConnectionConfiguration config, String oldNodeId) {
|
||||
ContentValues contentValues = new ContentValues();
|
||||
if (config.name != null) {
|
||||
contentValues.put("name", config.name);
|
||||
} else if (config.role == 2) {
|
||||
contentValues.put("name", "server");
|
||||
} else {
|
||||
contentValues.put("name", "NULL_STRING");
|
||||
}
|
||||
if (config.address != null) {
|
||||
contentValues.put("pairedBtAddress", config.address);
|
||||
} else {
|
||||
contentValues.put("pairedBtAddress", "NULL_STRING");
|
||||
}
|
||||
contentValues.put("connectionType", config.type);
|
||||
contentValues.put("role", config.role);
|
||||
contentValues.put("connectionEnabled", true);
|
||||
contentValues.put("nodeId", config.nodeId);
|
||||
if (oldNodeId == null) {
|
||||
getWritableDatabase().insert(TABLE_NAME, null, contentValues);
|
||||
} else {
|
||||
getWritableDatabase().update(TABLE_NAME, contentValues, "nodeId=?", new String[]{oldNodeId});
|
||||
}
|
||||
}
|
||||
|
||||
public ConnectionConfiguration[] getAllConfigurations() {
|
||||
Cursor cursor = getReadableDatabase().query(TABLE_NAME, null, null, null, null, null, null);
|
||||
if (cursor != null) {
|
||||
List<ConnectionConfiguration> configurations = new ArrayList<ConnectionConfiguration>();
|
||||
while (cursor.moveToNext()) {
|
||||
configurations.add(configFromCursor(cursor));
|
||||
}
|
||||
cursor.close();
|
||||
return configurations.toArray(new ConnectionConfiguration[configurations.size()]);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void setEnabledState(String name, boolean enabled) {
|
||||
getWritableDatabase().execSQL("UPDATE connectionConfigurations SET connectionEnabled=? WHERE name=?", new String[]{enabled ? "1" : "0", name});
|
||||
}
|
||||
|
||||
public int deleteConfiguration(String name) {
|
||||
return getWritableDatabase().delete(TABLE_NAME, BY_NAME, new String[]{name});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.microg.gms.wearable;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
import com.google.android.gms.wearable.Asset;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class DataItemInternal {
|
||||
public final String host;
|
||||
public final String path;
|
||||
public final Uri uri;
|
||||
public byte[] data;
|
||||
private Map<String, Asset> assets = new HashMap<String, Asset>();
|
||||
|
||||
public DataItemInternal(String host, String path) {
|
||||
this.host = host;
|
||||
this.path = path;
|
||||
this.uri = new Uri.Builder().scheme("wear").authority(host).path(path).build();
|
||||
}
|
||||
|
||||
public DataItemInternal(Uri uri) {
|
||||
this.uri = uri;
|
||||
this.host = uri.getAuthority();
|
||||
this.path = uri.getPath();
|
||||
}
|
||||
|
||||
public DataItemInternal addAsset(String key, Asset asset) {
|
||||
this.assets.put(key, asset);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Map<String, Asset> getAssets() {
|
||||
return Collections.unmodifiableMap(new HashMap<String, Asset>(assets));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder sb = new StringBuilder("DataItemInternal{");
|
||||
sb.append("uri=").append(uri);
|
||||
sb.append(", assets=").append(assets.size());
|
||||
sb.append('}');
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,188 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.microg.gms.wearable;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.android.gms.common.data.DataHolder;
|
||||
import com.google.android.gms.wearable.Asset;
|
||||
import com.google.android.gms.wearable.internal.DataItemAssetParcelable;
|
||||
import com.google.android.gms.wearable.internal.DataItemParcelable;
|
||||
|
||||
import org.microg.wearable.proto.AssetEntry;
|
||||
import org.microg.wearable.proto.SetDataItem;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import okio.ByteString;
|
||||
|
||||
public class DataItemRecord {
|
||||
private static String[] EVENT_DATA_HOLDER_FIELDS = new String[] { "event_type", "path", "data", "tags", "asset_key", "asset_id" };
|
||||
|
||||
public DataItemInternal dataItem;
|
||||
public String source;
|
||||
public long seqId;
|
||||
public long v1SeqId;
|
||||
public long lastModified;
|
||||
public boolean deleted;
|
||||
public boolean assetsAreReady;
|
||||
public String packageName;
|
||||
public String signatureDigest;
|
||||
|
||||
public ContentValues toContentValues() {
|
||||
ContentValues contentValues = new ContentValues();
|
||||
contentValues.put("sourceNode", source);
|
||||
contentValues.put("seqId", seqId);
|
||||
contentValues.put("v1SourceNode", source);
|
||||
contentValues.put("v1SeqId", v1SeqId);
|
||||
contentValues.put("timestampMs", lastModified);
|
||||
if (deleted) {
|
||||
contentValues.put("deleted", 1);
|
||||
contentValues.putNull("data");
|
||||
} else {
|
||||
contentValues.put("deleted", 0);
|
||||
contentValues.put("data", dataItem.data);
|
||||
}
|
||||
contentValues.put("assetsPresent", assetsAreReady ? 1 : 0);
|
||||
return contentValues;
|
||||
}
|
||||
|
||||
public DataHolder toEventDataHolder() {
|
||||
DataHolder.Builder builder = DataHolder.builder(EVENT_DATA_HOLDER_FIELDS);
|
||||
HashMap<String, Object> data = new HashMap<String, Object>();
|
||||
data.put("path", dataItem.uri.toString());
|
||||
if (deleted) {
|
||||
data.put("event_type", 2);
|
||||
builder.withRow(data);
|
||||
} else {
|
||||
data.put("event_type", 1);
|
||||
data.put("data", dataItem.data);
|
||||
data.put("tags", "");
|
||||
boolean added = false;
|
||||
for (Map.Entry<String, Asset> entry : dataItem.getAssets().entrySet()) {
|
||||
added = true;
|
||||
data.put("asset_id", entry.getValue().getDigest());
|
||||
data.put("asset_key", entry.getKey());
|
||||
builder.withRow(data);
|
||||
data = new HashMap<String, Object>();
|
||||
data.put("path", dataItem.uri.toString());
|
||||
}
|
||||
if (!added) {
|
||||
builder.withRow(data);
|
||||
}
|
||||
}
|
||||
return builder.build(0);
|
||||
}
|
||||
|
||||
public DataItemParcelable toParcelable() {
|
||||
Map<String, DataItemAssetParcelable> assets = new HashMap<>();
|
||||
for (Map.Entry<String, Asset> entry : dataItem.getAssets().entrySet()) {
|
||||
assets.put(entry.getKey(), new DataItemAssetParcelable(entry.getValue().getDigest(), entry.getKey()));
|
||||
}
|
||||
DataItemParcelable parcelable = new DataItemParcelable(dataItem.uri, assets);
|
||||
parcelable.data = dataItem.data;
|
||||
return parcelable;
|
||||
}
|
||||
|
||||
public SetDataItem toSetDataItem() {
|
||||
SetDataItem.Builder builder = new SetDataItem.Builder()
|
||||
.packageName(packageName)
|
||||
.signatureDigest(signatureDigest)
|
||||
.uri(dataItem.uri.toString())
|
||||
.seqId(seqId)
|
||||
.deleted(deleted)
|
||||
.lastModified(lastModified);
|
||||
if (source != null) builder.source(source);
|
||||
if (dataItem.data != null) builder.data(ByteString.of(dataItem.data));
|
||||
List<AssetEntry> protoAssets = new ArrayList<AssetEntry>();
|
||||
Map<String, Asset> assets = dataItem.getAssets();
|
||||
for (String key : assets.keySet()) {
|
||||
protoAssets.add(new AssetEntry.Builder()
|
||||
.key(key)
|
||||
.unknown3(4)
|
||||
.value(new org.microg.wearable.proto.Asset.Builder()
|
||||
.digest(assets.get(key).getDigest())
|
||||
.build()).build());
|
||||
}
|
||||
builder.assets(protoAssets);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public static DataItemRecord fromCursor(Cursor cursor) {
|
||||
DataItemRecord record = new DataItemRecord();
|
||||
record.packageName = cursor.getString(1);
|
||||
record.signatureDigest = cursor.getString(2);
|
||||
record.dataItem = new DataItemInternal(cursor.getString(3), cursor.getString(4));
|
||||
record.seqId = cursor.getLong(5);
|
||||
record.deleted = cursor.getLong(6) > 0;
|
||||
record.source = cursor.getString(7);
|
||||
record.dataItem.data = cursor.getBlob(8);
|
||||
record.lastModified = cursor.getLong(9);
|
||||
record.assetsAreReady = cursor.getLong(10) > 0;
|
||||
if (cursor.getString(11) != null) {
|
||||
record.dataItem.addAsset(cursor.getString(11), Asset.createFromRef(cursor.getString(12)));
|
||||
while (cursor.moveToNext()) {
|
||||
if (cursor.getLong(5) == record.seqId) {
|
||||
record.dataItem.addAsset(cursor.getString(11), Asset.createFromRef(cursor.getString(12)));
|
||||
}
|
||||
}
|
||||
cursor.moveToPrevious();
|
||||
}
|
||||
return record;
|
||||
}
|
||||
|
||||
public static DataItemRecord fromSetDataItem(SetDataItem setDataItem) {
|
||||
DataItemRecord record = new DataItemRecord();
|
||||
record.dataItem = new DataItemInternal(Uri.parse(setDataItem.uri));
|
||||
if (setDataItem.data != null) record.dataItem.data = setDataItem.data.toByteArray();
|
||||
if (setDataItem.assets != null) {
|
||||
for (AssetEntry asset : setDataItem.assets) {
|
||||
record.dataItem.addAsset(asset.key, Asset.createFromRef(asset.value.digest));
|
||||
}
|
||||
}
|
||||
record.source = setDataItem.source;
|
||||
record.seqId = setDataItem.seqId;
|
||||
record.v1SeqId = -1;
|
||||
record.lastModified = setDataItem.lastModified;
|
||||
record.deleted = setDataItem.deleted == null ? false : setDataItem.deleted;
|
||||
record.packageName = setDataItem.packageName;
|
||||
record.signatureDigest = setDataItem.signatureDigest;
|
||||
return record;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder sb = new StringBuilder("DataItemRecord{");
|
||||
sb.append("dataItem=").append(dataItem);
|
||||
sb.append(", source='").append(source).append('\'');
|
||||
sb.append(", seqId=").append(seqId);
|
||||
sb.append(", v1SeqId=").append(v1SeqId);
|
||||
sb.append(", lastModified=").append(lastModified);
|
||||
sb.append(", deleted=").append(deleted);
|
||||
sb.append(", assetsAreReady=").append(assetsAreReady);
|
||||
sb.append(", packageName='").append(packageName).append('\'');
|
||||
sb.append(", signatureDigest='").append(signatureDigest).append('\'');
|
||||
sb.append('}');
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,180 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.microg.gms.wearable;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.android.gms.wearable.Asset;
|
||||
import com.google.android.gms.wearable.ConnectionConfiguration;
|
||||
import com.google.android.gms.wearable.internal.MessageEventParcelable;
|
||||
|
||||
import org.microg.gms.profile.Build;
|
||||
import org.microg.gms.settings.SettingsContract;
|
||||
import org.microg.wearable.ServerMessageListener;
|
||||
import org.microg.wearable.proto.AckAsset;
|
||||
import org.microg.wearable.proto.Connect;
|
||||
import org.microg.wearable.proto.FetchAsset;
|
||||
import org.microg.wearable.proto.FilePiece;
|
||||
import org.microg.wearable.proto.Heartbeat;
|
||||
import org.microg.wearable.proto.Request;
|
||||
import org.microg.wearable.proto.RootMessage;
|
||||
import org.microg.wearable.proto.SetAsset;
|
||||
import org.microg.wearable.proto.SetDataItem;
|
||||
import org.microg.wearable.proto.SyncStart;
|
||||
import org.microg.wearable.proto.SyncTableEntry;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class MessageHandler extends ServerMessageListener {
|
||||
private static final String TAG = "GmsWearMsgHandler";
|
||||
private final WearableImpl wearable;
|
||||
private final String oldConfigNodeId;
|
||||
private String peerNodeId;
|
||||
|
||||
public MessageHandler(Context context, WearableImpl wearable, ConnectionConfiguration config) {
|
||||
this(wearable, config, Build.MODEL, config.nodeId, SettingsContract.getSettings(context, SettingsContract.CheckIn.INSTANCE.getContentUri(context), new String[]{SettingsContract.CheckIn.ANDROID_ID}, cursor -> cursor.getLong(0)));
|
||||
}
|
||||
|
||||
private MessageHandler(WearableImpl wearable, ConnectionConfiguration config, String name, String networkId, long androidId) {
|
||||
super(new Connect.Builder()
|
||||
.name(name)
|
||||
.id(wearable.getLocalNodeId())
|
||||
.networkId(networkId)
|
||||
.peerAndroidId(androidId)
|
||||
.unknown4(3)
|
||||
.peerVersion(1)
|
||||
.build());
|
||||
this.wearable = wearable;
|
||||
this.oldConfigNodeId = config.nodeId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnect(Connect connect) {
|
||||
super.onConnect(connect);
|
||||
peerNodeId = connect.id;
|
||||
wearable.onConnectReceived(getConnection(), oldConfigNodeId, connect);
|
||||
try {
|
||||
getConnection().writeMessage(new RootMessage.Builder().syncStart(new SyncStart.Builder()
|
||||
.receivedSeqId(-1L)
|
||||
.version(2)
|
||||
.syncTable(Arrays.asList(
|
||||
new SyncTableEntry.Builder().key("cloud").value(1L).build(),
|
||||
new SyncTableEntry.Builder().key(wearable.getLocalNodeId()).value(wearable.getCurrentSeqId(wearable.getLocalNodeId())).build(), // TODO
|
||||
new SyncTableEntry.Builder().key(peerNodeId).value(wearable.getCurrentSeqId(peerNodeId)).build() // TODO
|
||||
)).build()).build());
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisconnected() {
|
||||
Connect connect = getRemoteConnect();
|
||||
if (connect == null)
|
||||
connect = new Connect.Builder().id(oldConfigNodeId).name("Wear device").build();
|
||||
wearable.onDisconnectReceived(getConnection(), connect);
|
||||
super.onDisconnected();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetAsset(SetAsset setAsset) {
|
||||
Log.d(TAG, "onSetAsset: " + setAsset);
|
||||
Asset asset;
|
||||
if (setAsset.data != null) {
|
||||
asset = Asset.createFromBytes(setAsset.data.toByteArray());
|
||||
} else {
|
||||
asset = Asset.createFromRef(setAsset.digest);
|
||||
}
|
||||
wearable.addAssetToDatabase(asset, setAsset.appkeys.appKeys);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAckAsset(AckAsset ackAsset) {
|
||||
Log.d(TAG, "onAckAsset: " + ackAsset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFetchAsset(FetchAsset fetchAsset) {
|
||||
Log.d(TAG, "onFetchAsset: " + fetchAsset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSyncStart(SyncStart syncStart) {
|
||||
Log.d(TAG, "onSyncStart: " + syncStart);
|
||||
if (syncStart.version < 2) {
|
||||
Log.d(TAG, "Sync uses version " + syncStart.version + " which is not supported (yet)");
|
||||
}
|
||||
boolean hasLocalNode = false;
|
||||
if (syncStart.syncTable != null) {
|
||||
for (SyncTableEntry entry : syncStart.syncTable) {
|
||||
wearable.syncToPeer(peerNodeId, entry.key, entry.value);
|
||||
if (wearable.getLocalNodeId().equals(entry.key)) hasLocalNode = true;
|
||||
}
|
||||
} else {
|
||||
Log.d(TAG, "No sync table given.");
|
||||
}
|
||||
if (!hasLocalNode) wearable.syncToPeer(peerNodeId, wearable.getLocalNodeId(), 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetDataItem(SetDataItem setDataItem) {
|
||||
Log.d(TAG, "onSetDataItem: " + setDataItem);
|
||||
wearable.putDataItem(DataItemRecord.fromSetDataItem(setDataItem));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRpcRequest(Request rpcRequest) {
|
||||
Log.d(TAG, "onRpcRequest: " + rpcRequest);
|
||||
if (TextUtils.isEmpty(rpcRequest.targetNodeId) || rpcRequest.targetNodeId.equals(wearable.getLocalNodeId())) {
|
||||
MessageEventParcelable messageEvent = new MessageEventParcelable();
|
||||
messageEvent.data = rpcRequest.rawData != null ? rpcRequest.rawData.toByteArray() : null;
|
||||
messageEvent.path = rpcRequest.path;
|
||||
messageEvent.requestId = rpcRequest.requestId + 31 * (rpcRequest.generation + 527);
|
||||
messageEvent.sourceNodeId = TextUtils.isEmpty(rpcRequest.sourceNodeId) ? peerNodeId : rpcRequest.sourceNodeId;
|
||||
|
||||
wearable.sendMessageReceived(rpcRequest.packageName, messageEvent);
|
||||
} else if (rpcRequest.targetNodeId.equals(peerNodeId)) {
|
||||
// Drop it
|
||||
} else {
|
||||
// TODO: find next hop
|
||||
}
|
||||
try {
|
||||
getConnection().writeMessage(new RootMessage.Builder().heartbeat(new Heartbeat()).build());
|
||||
} catch (IOException e) {
|
||||
onDisconnected();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHeartbeat(Heartbeat heartbeat) {
|
||||
Log.d(TAG, "onHeartbeat: " + heartbeat);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFilePiece(FilePiece filePiece) {
|
||||
Log.d(TAG, "onFilePiece: " + filePiece);
|
||||
wearable.handleFilePiece(getConnection(), filePiece.fileName, filePiece.piece.toByteArray(), filePiece.finalPiece ? filePiece.digest : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChannelRequest(Request channelRequest) {
|
||||
Log.d(TAG, "onChannelRequest:" + channelRequest);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,297 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.microg.gms.wearable;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.android.gms.wearable.Asset;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class NodeDatabaseHelper extends SQLiteOpenHelper {
|
||||
private static final String TAG = "GmsWearNodeDB";
|
||||
|
||||
private static final String DB_NAME = "node.db";
|
||||
private static final String[] GDIBHAP_FIELDS = new String[]{"dataitems_id", "packageName", "signatureDigest", "host", "path", "seqId", "deleted", "sourceNode", "data", "timestampMs", "assetsPresent", "assetname", "assets_digest", "v1SourceNode", "v1SeqId"};
|
||||
private static final int VERSION = 9;
|
||||
|
||||
private ClockworkNodePreferences clockworkNodePreferences;
|
||||
|
||||
public NodeDatabaseHelper(Context context) {
|
||||
super(context, DB_NAME, null, VERSION);
|
||||
clockworkNodePreferences = new ClockworkNodePreferences(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase db) {
|
||||
db.execSQL("CREATE TABLE appkeys(_id INTEGER PRIMARY KEY AUTOINCREMENT,packageName TEXT NOT NULL,signatureDigest TEXT NOT NULL);");
|
||||
db.execSQL("CREATE TABLE dataitems(_id INTEGER PRIMARY KEY AUTOINCREMENT, appkeys_id INTEGER NOT NULL REFERENCES appkeys(_id), host TEXT NOT NULL, path TEXT NOT NULL, seqId INTEGER NOT NULL, deleted INTEGER NOT NULL, sourceNode TEXT NOT NULL, data BLOB, timestampMs INTEGER NOT NULL, assetsPresent INTEGER NOT NULL, v1SourceNode TEXT NOT NULL, v1SeqId INTEGER NOT NULL);");
|
||||
db.execSQL("CREATE TABLE assets(digest TEXT PRIMARY KEY, dataPresent INTEGER NOT NULL DEFAULT 0, timestampMs INTEGER NOT NULL);");
|
||||
db.execSQL("CREATE TABLE assetrefs(assetname TEXT NOT NULL, dataitems_id INTEGER NOT NULL REFERENCES dataitems(_id), assets_digest TEXT NOT NULL REFERENCES assets(digest));");
|
||||
db.execSQL("CREATE TABLE assetsacls(appkeys_id INTEGER NOT NULL REFERENCES appkeys(_id), assets_digest TEXT NOT NULL);");
|
||||
db.execSQL("CREATE TABLE nodeinfo(node TEXT NOT NULL PRIMARY KEY, seqId INTEGER, lastActivityMs INTEGER);");
|
||||
db.execSQL("CREATE VIEW appKeyDataItems AS SELECT appkeys._id AS appkeys_id, appkeys.packageName AS packageName, appkeys.signatureDigest AS signatureDigest, dataitems._id AS dataitems_id, dataitems.host AS host, dataitems.path AS path, dataitems.seqId AS seqId, dataitems.deleted AS deleted, dataitems.sourceNode AS sourceNode, dataitems.data AS data, dataitems.timestampMs AS timestampMs, dataitems.assetsPresent AS assetsPresent, dataitems.v1SourceNode AS v1SourceNode, dataitems.v1SeqId AS v1SeqId FROM appkeys, dataitems WHERE appkeys._id=dataitems.appkeys_id");
|
||||
db.execSQL("CREATE VIEW appKeyAcls AS SELECT appkeys._id AS appkeys_id, appkeys.packageName AS packageName, appkeys.signatureDigest AS signatureDigest, assetsacls.assets_digest AS assets_digest FROM appkeys, assetsacls WHERE _id=appkeys_id");
|
||||
db.execSQL("CREATE VIEW dataItemsAndAssets AS SELECT appKeyDataItems.packageName AS packageName, appKeyDataItems.signatureDigest AS signatureDigest, appKeyDataItems.dataitems_id AS dataitems_id, appKeyDataItems.host AS host, appKeyDataItems.path AS path, appKeyDataItems.seqId AS seqId, appKeyDataItems.deleted AS deleted, appKeyDataItems.sourceNode AS sourceNode, appKeyDataItems.data AS data, appKeyDataItems.timestampMs AS timestampMs, appKeyDataItems.assetsPresent AS assetsPresent, assetrefs.assetname AS assetname, assetrefs.assets_digest AS assets_digest, appKeyDataItems.v1SourceNode AS v1SourceNode, appKeyDataItems.v1SeqId AS v1SeqId FROM appKeyDataItems LEFT OUTER JOIN assetrefs ON appKeyDataItems.dataitems_id=assetrefs.dataitems_id");
|
||||
db.execSQL("CREATE VIEW assetsReadyStatus AS SELECT dataitems_id AS dataitems_id, COUNT(*) = SUM(dataPresent) AS nowReady, assetsPresent AS markedReady FROM assetrefs, dataitems LEFT OUTER JOIN assets ON assetrefs.assets_digest = assets.digest WHERE assetrefs.dataitems_id=dataitems._id GROUP BY dataitems_id;");
|
||||
db.execSQL("CREATE UNIQUE INDEX appkeys_NAME_AND_SIG ON appkeys(packageName,signatureDigest);");
|
||||
db.execSQL("CREATE UNIQUE INDEX assetrefs_ASSET_REFS ON assetrefs(assets_digest,dataitems_id,assetname);");
|
||||
db.execSQL("CREATE UNIQUE INDEX assets_DIGEST ON assets(digest);");
|
||||
db.execSQL("CREATE UNIQUE INDEX assetsacls_APPKEY_AND_DIGEST ON assetsacls(appkeys_id,assets_digest);");
|
||||
db.execSQL("CREATE UNIQUE INDEX dataitems_APPKEY_HOST_AND_PATH ON dataitems(appkeys_id,host,path);");
|
||||
db.execSQL("CREATE UNIQUE INDEX dataitems_SOURCENODE_AND_SEQID ON dataitems(sourceNode,seqId);");
|
||||
db.execSQL("CREATE UNIQUE INDEX dataitems_SOURCENODE_DELETED_AND_SEQID ON dataitems(sourceNode,deleted,seqId);");
|
||||
}
|
||||
|
||||
public synchronized Cursor getDataItemsForDataHolder(String packageName, String signatureDigest) {
|
||||
return getDataItemsForDataHolderByHostAndPath(packageName, signatureDigest, null, null);
|
||||
}
|
||||
|
||||
public synchronized Cursor getDataItemsForDataHolderByHostAndPath(String packageName, String signatureDigest, String host, String path) {
|
||||
String[] params;
|
||||
String selection;
|
||||
if (path == null) {
|
||||
params = new String[]{packageName, signatureDigest};
|
||||
selection = "packageName = ? AND signatureDigest = ?";
|
||||
} else if (TextUtils.isEmpty(host)) {
|
||||
if (path.endsWith("/")) path = path + "%";
|
||||
path = path.replace("*", "%");
|
||||
params = new String[]{packageName, signatureDigest, path};
|
||||
selection = "packageName = ? AND signatureDigest = ? AND path LIKE ?";
|
||||
} else {
|
||||
if (path.endsWith("/")) path = path + "%";
|
||||
path = path.replace("*", "%");
|
||||
host = host.replace("*", "%");
|
||||
params = new String[]{packageName, signatureDigest, host, path};
|
||||
selection = "packageName = ? AND signatureDigest = ? AND host = ? AND path LIKE ?";
|
||||
}
|
||||
selection += " AND deleted=0 AND assetsPresent !=0";
|
||||
return getReadableDatabase().rawQuery("SELECT host AS host,path AS path,data AS data,\'\' AS tags,assetname AS asset_key,assets_digest AS asset_id FROM dataItemsAndAssets WHERE " + selection, params);
|
||||
}
|
||||
|
||||
public synchronized Cursor getDataItemsByHostAndPath(String packageName, String signatureDigest, String host, String path) {
|
||||
Log.d(TAG, "getDataItemsByHostAndPath: " + packageName + ", " + signatureDigest + ", " + host + ", " + path);
|
||||
return getDataItemsByHostAndPath(getReadableDatabase(), packageName, signatureDigest, host, path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
if (oldVersion != VERSION) {
|
||||
// TODO: Upgrade not supported, cleaning up
|
||||
db.execSQL("DROP TABLE IF EXISTS appkeys;");
|
||||
db.execSQL("DROP TABLE IF EXISTS dataitems;");
|
||||
db.execSQL("DROP TABLE IF EXISTS assets;");
|
||||
db.execSQL("DROP TABLE IF EXISTS assetrefs;");
|
||||
db.execSQL("DROP TABLE IF EXISTS assetsacls;");
|
||||
db.execSQL("DROP TABLE IF EXISTS nodeinfo;");
|
||||
db.execSQL("DROP VIEW IF EXISTS appKeyDataItems;");
|
||||
db.execSQL("DROP VIEW IF EXISTS appKeyAcls;");
|
||||
db.execSQL("DROP VIEW IF EXISTS dataItemsAndAssets;");
|
||||
db.execSQL("DROP VIEW IF EXISTS assetsReadyStatus;");
|
||||
onCreate(db);
|
||||
}
|
||||
}
|
||||
|
||||
private static synchronized long getAppKey(SQLiteDatabase db, String packageName, String signatureDigest) {
|
||||
Cursor cursor = db.rawQuery("SELECT _id FROM appkeys WHERE packageName=? AND signatureDigest=?", new String[]{packageName, signatureDigest});
|
||||
if (cursor != null) {
|
||||
try {
|
||||
if (cursor.moveToNext()) {
|
||||
return cursor.getLong(0);
|
||||
}
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
ContentValues appKey = new ContentValues();
|
||||
appKey.put("packageName", packageName);
|
||||
appKey.put("signatureDigest", signatureDigest);
|
||||
return db.insert("appkeys", null, appKey);
|
||||
}
|
||||
|
||||
public synchronized void putRecord(DataItemRecord record) {
|
||||
SQLiteDatabase db = getWritableDatabase();
|
||||
db.beginTransaction();
|
||||
Cursor cursor = getDataItemsByHostAndPath(db, record.packageName, record.signatureDigest, record.dataItem.host, record.dataItem.path);
|
||||
try {
|
||||
String key;
|
||||
if (cursor.moveToNext()) {
|
||||
// update
|
||||
key = cursor.getString(0);
|
||||
updateRecord(db, key, record);
|
||||
} else {
|
||||
// insert
|
||||
key = insertRecord(db, record);
|
||||
}
|
||||
if (record.assetsAreReady) {
|
||||
ContentValues update = new ContentValues();
|
||||
update.put("assetsPresent", 1);
|
||||
db.update("dataitems", update, "_id=?", new String[]{key});
|
||||
}
|
||||
db.setTransactionSuccessful();
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
db.endTransaction();
|
||||
}
|
||||
|
||||
private static void updateRecord(SQLiteDatabase db, String key, DataItemRecord record) {
|
||||
ContentValues cv = record.toContentValues();
|
||||
db.update("dataitems", cv, "_id=?", new String[]{key});
|
||||
finishRecord(db, key, record);
|
||||
}
|
||||
|
||||
private static String insertRecord(SQLiteDatabase db, DataItemRecord record) {
|
||||
ContentValues contentValues = record.toContentValues();
|
||||
contentValues.put("appkeys_id", getAppKey(db, record.packageName, record.signatureDigest));
|
||||
contentValues.put("host", record.dataItem.host);
|
||||
contentValues.put("path", record.dataItem.path);
|
||||
String key = Long.toString(db.insertWithOnConflict("dataitems", "host", contentValues, SQLiteDatabase.CONFLICT_REPLACE));
|
||||
return finishRecord(db, key, record);
|
||||
}
|
||||
|
||||
private static String finishRecord(SQLiteDatabase db, String key, DataItemRecord record) {
|
||||
if (!record.deleted) {
|
||||
for (Map.Entry<String, Asset> asset : record.dataItem.getAssets().entrySet()) {
|
||||
ContentValues assetValues = new ContentValues();
|
||||
assetValues.put("assets_digest", asset.getValue().getDigest());
|
||||
assetValues.put("dataitems_id", key);
|
||||
assetValues.put("assetname", asset.getKey());
|
||||
db.insertWithOnConflict("assetrefs", "assetname", assetValues, SQLiteDatabase.CONFLICT_IGNORE);
|
||||
}
|
||||
Cursor status = db.query("assetsReadyStatus", new String[]{"nowReady"}, "dataitems_id=?", new String[]{key}, null, null, null);
|
||||
if (status.moveToNext()) {
|
||||
record.assetsAreReady = status.getLong(0) != 0;
|
||||
}
|
||||
status.close();
|
||||
} else {
|
||||
record.assetsAreReady = false;
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
private static Cursor getDataItemsByHostAndPath(SQLiteDatabase db, String packageName, String signatureDigest, String host, String path) {
|
||||
String[] params;
|
||||
String selection;
|
||||
if (path == null) {
|
||||
params = new String[]{packageName, signatureDigest};
|
||||
selection = "packageName =? AND signatureDigest =?";
|
||||
} else if (host == null) {
|
||||
params = new String[]{packageName, signatureDigest, path};
|
||||
selection = "packageName =? AND signatureDigest =? AND path =?";
|
||||
} else {
|
||||
params = new String[]{packageName, signatureDigest, host, path};
|
||||
selection = "packageName =? AND signatureDigest =? AND host =? AND path =?";
|
||||
}
|
||||
selection += " AND deleted=0";
|
||||
return db.query("dataItemsAndAssets", GDIBHAP_FIELDS, selection, params, null, null, "packageName, signatureDigest, host, path");
|
||||
}
|
||||
|
||||
public Cursor getModifiedDataItems(final String nodeId, final long seqId, final boolean excludeDeleted) {
|
||||
String selection = "sourceNode =? AND seqId >?" + (excludeDeleted ? " AND deleted =0" : "");
|
||||
return getReadableDatabase().query("dataItemsAndAssets", GDIBHAP_FIELDS, selection, new String[]{nodeId, Long.toString(seqId)}, null, null, "seqId", null);
|
||||
}
|
||||
|
||||
public synchronized List<DataItemRecord> deleteDataItems(String packageName, String signatureDigest, String host, String path) {
|
||||
List<DataItemRecord> updated = new ArrayList<DataItemRecord>();
|
||||
SQLiteDatabase db = getWritableDatabase();
|
||||
db.beginTransaction();
|
||||
Cursor cursor = getDataItemsByHostAndPath(db, packageName, signatureDigest, host, path);
|
||||
while (cursor.moveToNext()) {
|
||||
DataItemRecord record = DataItemRecord.fromCursor(cursor);
|
||||
record.deleted = true;
|
||||
record.assetsAreReady = true;
|
||||
record.dataItem.data = null;
|
||||
record.seqId = clockworkNodePreferences.getNextSeqId();
|
||||
record.v1SeqId = record.seqId;
|
||||
updateRecord(db, cursor.getString(0), record);
|
||||
updated.add(record);
|
||||
}
|
||||
db.setTransactionSuccessful();
|
||||
db.endTransaction();
|
||||
return updated;
|
||||
}
|
||||
|
||||
public long getCurrentSeqId(String sourceNode) {
|
||||
if (TextUtils.isEmpty(sourceNode)) return 1;
|
||||
return getCurrentSeqId(getReadableDatabase(), sourceNode);
|
||||
}
|
||||
|
||||
private long getCurrentSeqId(SQLiteDatabase db, String sourceNode) {
|
||||
Cursor cursor = db.query("dataItemsAndAssets", new String[]{"seqId"}, "sourceNode=?", new String[]{sourceNode}, null, null, "seqId DESC", "1");
|
||||
long res = 1;
|
||||
if (cursor != null) {
|
||||
if (cursor.moveToFirst()) {
|
||||
res = cursor.getLong(0);
|
||||
}
|
||||
cursor.close();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public synchronized void putAsset(Asset asset, boolean dataPresent) {
|
||||
ContentValues cv = new ContentValues();
|
||||
cv.put("digest", asset.getDigest());
|
||||
cv.put("dataPresent", dataPresent ? 1 : 0);
|
||||
cv.put("timestampMs", System.currentTimeMillis());
|
||||
getWritableDatabase().insertWithOnConflict("assets", null, cv, SQLiteDatabase.CONFLICT_REPLACE);
|
||||
}
|
||||
|
||||
public synchronized void allowAssetAccess(String digest, String packageName, String signatureDigest) {
|
||||
SQLiteDatabase db = getWritableDatabase();
|
||||
ContentValues cv = new ContentValues();
|
||||
cv.put("assets_digest", digest);
|
||||
cv.put("appkeys_id", getAppKey(db, packageName, signatureDigest));
|
||||
db.insertWithOnConflict("assetsacls", null, cv, SQLiteDatabase.CONFLICT_REPLACE);
|
||||
}
|
||||
|
||||
public Cursor listMissingAssets() {
|
||||
return getReadableDatabase().query("dataItemsAndAssets", GDIBHAP_FIELDS, "assetsPresent = 0 AND assets_digest NOT NULL", null, null, null, "packageName, signatureDigest, host, path");
|
||||
}
|
||||
|
||||
public boolean hasAsset(Asset asset) {
|
||||
Cursor cursor = getReadableDatabase().query("assets", new String[]{"dataPresent"}, "digest=?", new String[]{asset.getDigest()}, null, null, null);
|
||||
if (cursor == null) return false;
|
||||
try {
|
||||
return (cursor.moveToNext() && cursor.getInt(0) == 1);
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void markAssetAsPresent(String digest) {
|
||||
ContentValues cv = new ContentValues();
|
||||
cv.put("dataPresent", 1);
|
||||
SQLiteDatabase db = getWritableDatabase();
|
||||
db.update("assets", cv, "digest=?", new String[]{digest});
|
||||
Cursor status = db.query("assetsReadyStatus", null, "nowReady != markedReady", null, null, null, null);
|
||||
while (status.moveToNext()) {
|
||||
cv = new ContentValues();
|
||||
cv.put("assetsPresent", status.getInt(status.getColumnIndexOrThrow("nowReady")));
|
||||
db.update("dataitems", cv, "_id=?", new String[]{Integer.toString(status.getInt(status.getColumnIndexOrThrow("dataitems_id")))});
|
||||
}
|
||||
status.close();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.microg.gms.wearable;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class RpcHelper {
|
||||
private final Map<String, RpcConnectionState> rpcStateMap = new HashMap<String, RpcConnectionState>();
|
||||
private final SharedPreferences preferences;
|
||||
private final Context context;
|
||||
|
||||
public RpcHelper(Context context) {
|
||||
this.context = context;
|
||||
this.preferences = context.getSharedPreferences("wearable.rpc_service.settings", 0);
|
||||
}
|
||||
|
||||
private String getRpcConnectionId(String packageName, String targetNodeId, String path) {
|
||||
String mode = "lo";
|
||||
if (packageName.equals("com.google.android.wearable.app") && path.startsWith("/s3"))
|
||||
mode = "hi";
|
||||
return targetNodeId + ":" + mode;
|
||||
}
|
||||
|
||||
public RpcHelper.RpcConnectionState useConnectionState(String packageName, String targetNodeId, String path) {
|
||||
String rpcConnectionId = getRpcConnectionId(packageName, targetNodeId, path);
|
||||
synchronized (rpcStateMap) {
|
||||
if (!rpcStateMap.containsKey(rpcConnectionId)) {
|
||||
int g = preferences.getInt(rpcConnectionId, 1)+1;
|
||||
preferences.edit().putInt(rpcConnectionId, g).apply();
|
||||
rpcStateMap.put(rpcConnectionId, new RpcConnectionState(g));
|
||||
}
|
||||
RpcHelper.RpcConnectionState res = rpcStateMap.get(rpcConnectionId);
|
||||
res.lastRequestId++;
|
||||
return res.freeze();
|
||||
}
|
||||
}
|
||||
|
||||
public static class RpcConnectionState {
|
||||
public int generation;
|
||||
public int lastRequestId;
|
||||
|
||||
public RpcConnectionState(int generation) {
|
||||
this.generation = generation;
|
||||
}
|
||||
|
||||
public RpcConnectionState freeze() {
|
||||
RpcConnectionState res = new RpcConnectionState(generation);
|
||||
res.lastRequestId = lastRequestId;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,642 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2019 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.microg.gms.wearable;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.RemoteException;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.google.android.gms.common.data.DataHolder;
|
||||
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.IWearableListener;
|
||||
import com.google.android.gms.wearable.internal.MessageEventParcelable;
|
||||
import com.google.android.gms.wearable.internal.NodeParcelable;
|
||||
import com.google.android.gms.wearable.internal.PutDataRequest;
|
||||
|
||||
import org.microg.gms.common.PackageUtils;
|
||||
import org.microg.gms.common.RemoteListenerProxy;
|
||||
import org.microg.gms.common.Utils;
|
||||
import org.microg.wearable.SocketConnectionThread;
|
||||
import org.microg.wearable.WearableConnection;
|
||||
import org.microg.wearable.proto.AckAsset;
|
||||
import org.microg.wearable.proto.AppKey;
|
||||
import org.microg.wearable.proto.AppKeys;
|
||||
import org.microg.wearable.proto.Connect;
|
||||
import org.microg.wearable.proto.FetchAsset;
|
||||
import org.microg.wearable.proto.FilePiece;
|
||||
import org.microg.wearable.proto.Request;
|
||||
import org.microg.wearable.proto.RootMessage;
|
||||
import org.microg.wearable.proto.SetAsset;
|
||||
import org.microg.wearable.proto.SetDataItem;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
import okio.ByteString;
|
||||
|
||||
public class WearableImpl {
|
||||
|
||||
private static final String TAG = "GmsWear";
|
||||
|
||||
private static final int WEAR_TCP_PORT = 5601;
|
||||
|
||||
private final Context context;
|
||||
private final NodeDatabaseHelper nodeDatabase;
|
||||
private final ConfigurationDatabaseHelper configDatabase;
|
||||
private final Map<String, List<ListenerInfo>> listeners = new HashMap<String, List<ListenerInfo>>();
|
||||
private final Set<Node> connectedNodes = new HashSet<Node>();
|
||||
private final Map<String, WearableConnection> activeConnections = new HashMap<String, WearableConnection>();
|
||||
private RpcHelper rpcHelper;
|
||||
private SocketConnectionThread sct;
|
||||
private ConnectionConfiguration[] configurations;
|
||||
private boolean configurationsUpdated = false;
|
||||
private ClockworkNodePreferences clockworkNodePreferences;
|
||||
private CountDownLatch networkHandlerLock = new CountDownLatch(1);
|
||||
public Handler networkHandler;
|
||||
|
||||
public WearableImpl(Context context, NodeDatabaseHelper nodeDatabase, ConfigurationDatabaseHelper configDatabase) {
|
||||
this.context = context;
|
||||
this.nodeDatabase = nodeDatabase;
|
||||
this.configDatabase = configDatabase;
|
||||
this.clockworkNodePreferences = new ClockworkNodePreferences(context);
|
||||
this.rpcHelper = new RpcHelper(context);
|
||||
new Thread(() -> {
|
||||
Looper.prepare();
|
||||
networkHandler = new Handler(Looper.myLooper());
|
||||
networkHandlerLock.countDown();
|
||||
Looper.loop();
|
||||
}).start();
|
||||
}
|
||||
|
||||
public String getLocalNodeId() {
|
||||
return clockworkNodePreferences.getLocalNodeId();
|
||||
}
|
||||
|
||||
public DataItemRecord putDataItem(String packageName, String signatureDigest, String source, DataItemInternal dataItem) {
|
||||
DataItemRecord record = new DataItemRecord();
|
||||
record.packageName = packageName;
|
||||
record.signatureDigest = signatureDigest;
|
||||
record.deleted = false;
|
||||
record.source = source;
|
||||
record.dataItem = dataItem;
|
||||
record.v1SeqId = clockworkNodePreferences.getNextSeqId();
|
||||
if (record.source.equals(getLocalNodeId())) record.seqId = record.v1SeqId;
|
||||
nodeDatabase.putRecord(record);
|
||||
return record;
|
||||
}
|
||||
|
||||
public DataItemRecord putDataItem(DataItemRecord record) {
|
||||
nodeDatabase.putRecord(record);
|
||||
if (!record.assetsAreReady) {
|
||||
for (Asset asset : record.dataItem.getAssets().values()) {
|
||||
if (!nodeDatabase.hasAsset(asset)) {
|
||||
Log.d(TAG, "Asset is missing: " + asset);
|
||||
}
|
||||
}
|
||||
}
|
||||
Intent intent = new Intent("com.google.android.gms.wearable.DATA_CHANGED");
|
||||
intent.setPackage(record.packageName);
|
||||
intent.setData(record.dataItem.uri);
|
||||
invokeListeners(intent, listener -> listener.onDataChanged(record.toEventDataHolder()));
|
||||
return record;
|
||||
}
|
||||
|
||||
private Asset prepareAsset(String packageName, Asset asset) {
|
||||
if (asset.getFd() != null && asset.data == null) {
|
||||
try {
|
||||
asset.data = Utils.readStreamToEnd(new FileInputStream(asset.getFd().getFileDescriptor()));
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
}
|
||||
if (asset.data != null) {
|
||||
String digest = calculateDigest(asset.data);
|
||||
File assetFile = createAssetFile(digest);
|
||||
boolean success = assetFile.exists();
|
||||
if (!success) {
|
||||
File tmpFile = new File(assetFile.getParent(), assetFile.getName() + ".tmp");
|
||||
|
||||
try {
|
||||
FileOutputStream stream = new FileOutputStream(tmpFile);
|
||||
stream.write(asset.data);
|
||||
stream.close();
|
||||
success = tmpFile.renameTo(assetFile);
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
}
|
||||
if (success) {
|
||||
Log.d(TAG, "Successfully created asset file " + assetFile);
|
||||
return Asset.createFromRef(digest);
|
||||
} else {
|
||||
Log.w(TAG, "Failed creating asset file " + assetFile);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public File createAssetFile(String digest) {
|
||||
File dir = new File(new File(context.getFilesDir(), "assets"), digest.substring(digest.length() - 2));
|
||||
dir.mkdirs();
|
||||
return new File(dir, digest + ".asset");
|
||||
}
|
||||
|
||||
private File createAssetReceiveTempFile(String name) {
|
||||
File dir = new File(context.getFilesDir(), "piece");
|
||||
dir.mkdirs();
|
||||
return new File(dir, name);
|
||||
}
|
||||
|
||||
private String calculateDigest(byte[] data) {
|
||||
try {
|
||||
return Base64.encodeToString(MessageDigest.getInstance("SHA1").digest(data), Base64.NO_WRAP | Base64.NO_PADDING | Base64.URL_SAFE);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized ConnectionConfiguration[] getConfigurations() {
|
||||
if (configurations == null) {
|
||||
configurations = configDatabase.getAllConfigurations();
|
||||
}
|
||||
if (configurationsUpdated) {
|
||||
configurationsUpdated = false;
|
||||
ConnectionConfiguration[] newConfigurations = configDatabase.getAllConfigurations();
|
||||
for (ConnectionConfiguration configuration : configurations) {
|
||||
for (ConnectionConfiguration newConfiguration : newConfigurations) {
|
||||
if (newConfiguration.name.equals(configuration.name)) {
|
||||
newConfiguration.connected = configuration.connected;
|
||||
newConfiguration.peerNodeId = configuration.peerNodeId;
|
||||
newConfiguration.nodeId = configuration.nodeId;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
configurations = newConfigurations;
|
||||
}
|
||||
Log.d(TAG, "Configurations reported: " + Arrays.toString(configurations));
|
||||
return configurations;
|
||||
}
|
||||
|
||||
private void addConnectedNode(Node node) {
|
||||
connectedNodes.add(node);
|
||||
onConnectedNodes(getConnectedNodesParcelableList());
|
||||
}
|
||||
|
||||
private void removeConnectedNode(String nodeId) {
|
||||
for (Node connectedNode : new ArrayList<Node>(connectedNodes)) {
|
||||
if (connectedNode.getId().equals(nodeId))
|
||||
connectedNodes.remove(connectedNode);
|
||||
}
|
||||
onConnectedNodes(getConnectedNodesParcelableList());
|
||||
}
|
||||
|
||||
|
||||
public Context getContext() {
|
||||
return context;
|
||||
}
|
||||
|
||||
public void syncToPeer(String peerNodeId, String nodeId, long seqId) {
|
||||
Log.d(TAG, "-- Start syncing over to " + peerNodeId + ", nodeId " + nodeId + " starting with seqId " + seqId);
|
||||
Cursor cursor = nodeDatabase.getModifiedDataItems(nodeId, seqId, true);
|
||||
if (cursor != null) {
|
||||
while (cursor.moveToNext()) {
|
||||
if (!syncRecordToPeer(peerNodeId, DataItemRecord.fromCursor(cursor))) break;
|
||||
}
|
||||
cursor.close();
|
||||
}
|
||||
Log.d(TAG, "-- Done syncing over to " + peerNodeId + ", nodeId " + nodeId + " starting with seqId " + seqId);
|
||||
}
|
||||
|
||||
|
||||
void syncRecordToAll(DataItemRecord record) {
|
||||
for (String nodeId : new ArrayList<String>(activeConnections.keySet())) {
|
||||
syncRecordToPeer(nodeId, record);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean syncRecordToPeer(String nodeId, DataItemRecord record) {
|
||||
for (Asset asset : record.dataItem.getAssets().values()) {
|
||||
try {
|
||||
syncAssetToPeer(nodeId, record, asset);
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, "Could not sync asset " + asset + " for " + nodeId + " and " + record, e);
|
||||
closeConnection(nodeId);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
SetDataItem item = record.toSetDataItem();
|
||||
activeConnections.get(nodeId).writeMessage(new RootMessage.Builder().setDataItem(item).build());
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, e);
|
||||
closeConnection(nodeId);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void syncAssetToPeer(String nodeId, DataItemRecord record, Asset asset) throws IOException {
|
||||
RootMessage announceMessage = new RootMessage.Builder().setAsset(new SetAsset.Builder()
|
||||
.digest(asset.getDigest())
|
||||
.appkeys(new AppKeys(Collections.singletonList(new AppKey(record.packageName, record.signatureDigest))))
|
||||
.build()).hasAsset(true).build();
|
||||
activeConnections.get(nodeId).writeMessage(announceMessage);
|
||||
File assetFile = createAssetFile(asset.getDigest());
|
||||
String fileName = calculateDigest(announceMessage.encode());
|
||||
FileInputStream fis = new FileInputStream(assetFile);
|
||||
byte[] arr = new byte[12215];
|
||||
ByteString lastPiece = null;
|
||||
int c = 0;
|
||||
while ((c = fis.read(arr)) > 0) {
|
||||
if (lastPiece != null) {
|
||||
activeConnections.get(nodeId).writeMessage(new RootMessage.Builder().filePiece(new FilePiece(fileName, false, lastPiece, null)).build());
|
||||
}
|
||||
lastPiece = ByteString.of(arr, 0, c);
|
||||
}
|
||||
activeConnections.get(nodeId).writeMessage(new RootMessage.Builder().filePiece(new FilePiece(fileName, true, lastPiece, asset.getDigest())).build());
|
||||
}
|
||||
|
||||
public void addAssetToDatabase(Asset asset, List<AppKey> appKeys) {
|
||||
nodeDatabase.putAsset(asset, false);
|
||||
for (AppKey appKey : appKeys) {
|
||||
nodeDatabase.allowAssetAccess(asset.getDigest(), appKey.packageName, appKey.signatureDigest);
|
||||
}
|
||||
}
|
||||
|
||||
public long getCurrentSeqId(String nodeId) {
|
||||
return nodeDatabase.getCurrentSeqId(nodeId);
|
||||
}
|
||||
|
||||
public void handleFilePiece(WearableConnection connection, String fileName, byte[] bytes, String finalPieceDigest) {
|
||||
File file = createAssetReceiveTempFile(fileName);
|
||||
try {
|
||||
FileOutputStream fos = new FileOutputStream(file, true);
|
||||
fos.write(bytes);
|
||||
fos.close();
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
if (finalPieceDigest != null) {
|
||||
// This is a final piece. If digest matches we're so happy!
|
||||
try {
|
||||
String digest = calculateDigest(Utils.readStreamToEnd(new FileInputStream(file)));
|
||||
if (digest.equals(finalPieceDigest)) {
|
||||
if (file.renameTo(createAssetFile(digest))) {
|
||||
nodeDatabase.markAssetAsPresent(digest);
|
||||
connection.writeMessage(new RootMessage.Builder().ackAsset(new AckAsset(digest)).build());
|
||||
} else {
|
||||
Log.w(TAG, "Could not rename to target file name. delete=" + file.delete());
|
||||
}
|
||||
} else {
|
||||
Log.w(TAG, "Received digest does not match. delete=" + file.delete());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, "Failed working with temp file. delete=" + file.delete(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onConnectReceived(WearableConnection connection, String nodeId, Connect connect) {
|
||||
for (ConnectionConfiguration config : getConfigurations()) {
|
||||
if (config.nodeId.equals(nodeId)) {
|
||||
if (config.nodeId != nodeId) {
|
||||
config.nodeId = connect.id;
|
||||
configDatabase.putConfiguration(config, nodeId);
|
||||
}
|
||||
config.peerNodeId = connect.id;
|
||||
config.connected = true;
|
||||
}
|
||||
}
|
||||
Log.d(TAG, "Adding connection to list of open connections: " + connection + " with connect " + connect);
|
||||
activeConnections.put(connect.id, connection);
|
||||
onPeerConnected(new NodeParcelable(connect.id, connect.name));
|
||||
// Fetch missing assets
|
||||
Cursor cursor = nodeDatabase.listMissingAssets();
|
||||
if (cursor != null) {
|
||||
while (cursor.moveToNext()) {
|
||||
try {
|
||||
Log.d(TAG, "Fetch for " + cursor.getString(12));
|
||||
connection.writeMessage(new RootMessage.Builder()
|
||||
.fetchAsset(new FetchAsset.Builder()
|
||||
.assetName(cursor.getString(12))
|
||||
.packageName(cursor.getString(1))
|
||||
.signatureDigest(cursor.getString(2))
|
||||
.permission(false)
|
||||
.build()).build());
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
closeConnection(connect.id);
|
||||
}
|
||||
}
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
public void onDisconnectReceived(WearableConnection connection, Connect connect) {
|
||||
for (ConnectionConfiguration config : getConfigurations()) {
|
||||
if (config.nodeId.equals(connect.id)) {
|
||||
config.connected = false;
|
||||
}
|
||||
}
|
||||
Log.d(TAG, "Removing connection from list of open connections: " + connection);
|
||||
activeConnections.remove(connect.id);
|
||||
onPeerDisconnected(new NodeParcelable(connect.id, connect.name));
|
||||
}
|
||||
|
||||
public List<NodeParcelable> getConnectedNodesParcelableList() {
|
||||
List<NodeParcelable> nodes = new ArrayList<NodeParcelable>();
|
||||
for (Node connectedNode : connectedNodes) {
|
||||
nodes.add(new NodeParcelable(connectedNode));
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
|
||||
interface ListenerInvoker {
|
||||
void invoke(IWearableListener listener) throws RemoteException;
|
||||
}
|
||||
|
||||
private void invokeListeners(@Nullable Intent intent, ListenerInvoker invoker) {
|
||||
for (String packageName : new ArrayList<>(listeners.keySet())) {
|
||||
List<ListenerInfo> listeners = this.listeners.get(packageName);
|
||||
if (listeners == null) continue;
|
||||
for (int i = 0; i < listeners.size(); i++) {
|
||||
boolean filterMatched = false;
|
||||
if (intent != null) {
|
||||
for (IntentFilter filter : listeners.get(i).filters) {
|
||||
filterMatched |= filter.match(context.getContentResolver(), intent, false, TAG) > 0;
|
||||
}
|
||||
}
|
||||
if (filterMatched || listeners.get(i).filters.length == 0) {
|
||||
try {
|
||||
invoker.invoke(listeners.get(i).listener);
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "Registered listener at package " + packageName + " failed, removing.");
|
||||
listeners.remove(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (listeners.isEmpty()) {
|
||||
this.listeners.remove(packageName);
|
||||
}
|
||||
}
|
||||
if (intent != null) {
|
||||
try {
|
||||
invoker.invoke(RemoteListenerProxy.get(context, intent, IWearableListener.class, "com.google.android.gms.wearable.BIND_LISTENER"));
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "Failed to deliver message received to " + intent, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onPeerConnected(NodeParcelable node) {
|
||||
Log.d(TAG, "onPeerConnected: " + node);
|
||||
invokeListeners(null, listener -> listener.onPeerConnected(node));
|
||||
addConnectedNode(node);
|
||||
}
|
||||
|
||||
public void onPeerDisconnected(NodeParcelable node) {
|
||||
Log.d(TAG, "onPeerDisconnected: " + node);
|
||||
invokeListeners(null, listener -> listener.onPeerDisconnected(node));
|
||||
removeConnectedNode(node.getId());
|
||||
}
|
||||
|
||||
public void onConnectedNodes(List<NodeParcelable> nodes) {
|
||||
Log.d(TAG, "onConnectedNodes: " + nodes);
|
||||
invokeListeners(null, listener -> listener.onConnectedNodes(nodes));
|
||||
}
|
||||
|
||||
public DataItemRecord putData(PutDataRequest request, String packageName) {
|
||||
DataItemInternal dataItem = new DataItemInternal(fixHost(request.getUri().getHost(), true), request.getUri().getPath());
|
||||
for (Map.Entry<String, Asset> assetEntry : request.getAssets().entrySet()) {
|
||||
Asset asset = prepareAsset(packageName, assetEntry.getValue());
|
||||
if (asset != null) {
|
||||
nodeDatabase.putAsset(asset, true);
|
||||
dataItem.addAsset(assetEntry.getKey(), asset);
|
||||
}
|
||||
}
|
||||
dataItem.data = request.getData();
|
||||
DataItemRecord record = putDataItem(packageName, PackageUtils.firstSignatureDigest(context, packageName), getLocalNodeId(), dataItem);
|
||||
syncRecordToAll(record);
|
||||
return record;
|
||||
}
|
||||
|
||||
public DataHolder getDataItemsAsHolder(String packageName) {
|
||||
Cursor dataHolderItems = nodeDatabase.getDataItemsForDataHolder(packageName, PackageUtils.firstSignatureDigest(context, packageName));
|
||||
return new DataHolder(dataHolderItems, 0, null);
|
||||
}
|
||||
|
||||
private String fixHost(String host, boolean nothingToLocal) {
|
||||
if (TextUtils.isEmpty(host) && nothingToLocal) return getLocalNodeId();
|
||||
if (TextUtils.isEmpty(host)) return null;
|
||||
if (host.equals("local")) return getLocalNodeId();
|
||||
return host;
|
||||
}
|
||||
|
||||
public DataHolder getDataItemsByUriAsHolder(Uri uri, String packageName) {
|
||||
String firstSignature;
|
||||
try {
|
||||
firstSignature = PackageUtils.firstSignatureDigest(context, packageName);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
Cursor dataHolderItems = nodeDatabase.getDataItemsForDataHolderByHostAndPath(packageName, firstSignature, fixHost(uri.getHost(), false), uri.getPath());
|
||||
DataHolder dataHolder = new DataHolder(dataHolderItems, 0, null);
|
||||
Log.d(TAG, "Returning data holder of size " + dataHolder.getCount() + " for query " + uri);
|
||||
return dataHolder;
|
||||
}
|
||||
|
||||
public synchronized void addListener(String packageName, IWearableListener listener, IntentFilter[] filters) {
|
||||
if (!listeners.containsKey(packageName)) {
|
||||
listeners.put(packageName, new ArrayList<ListenerInfo>());
|
||||
}
|
||||
listeners.get(packageName).add(new ListenerInfo(listener, filters));
|
||||
}
|
||||
|
||||
public void removeListener(IWearableListener listener) {
|
||||
for (List<ListenerInfo> list : listeners.values()) {
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
if (list.get(i).listener.equals(listener)) {
|
||||
list.remove(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void enableConnection(String name) {
|
||||
configDatabase.setEnabledState(name, true);
|
||||
configurationsUpdated = true;
|
||||
if (name.equals("server") && sct == null) {
|
||||
Log.d(TAG, "Starting server on :" + WEAR_TCP_PORT);
|
||||
(sct = SocketConnectionThread.serverListen(WEAR_TCP_PORT, new MessageHandler(context, this, configDatabase.getConfiguration(name)))).start();
|
||||
}
|
||||
}
|
||||
|
||||
public void disableConnection(String name) {
|
||||
configDatabase.setEnabledState(name, false);
|
||||
configurationsUpdated = true;
|
||||
if (name.equals("server") && sct != null) {
|
||||
activeConnections.remove(sct.getWearableConnection());
|
||||
sct.close();
|
||||
sct.interrupt();
|
||||
sct = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void deleteConnection(String name) {
|
||||
configDatabase.deleteConfiguration(name);
|
||||
configurationsUpdated = true;
|
||||
}
|
||||
|
||||
public void createConnection(ConnectionConfiguration config) {
|
||||
if (config.nodeId == null) config.nodeId = getLocalNodeId();
|
||||
Log.d(TAG, "putConfig[nyp]: " + config);
|
||||
configDatabase.putConfiguration(config);
|
||||
configurationsUpdated = true;
|
||||
}
|
||||
|
||||
public int deleteDataItems(Uri uri, String packageName) {
|
||||
List<DataItemRecord> records = nodeDatabase.deleteDataItems(packageName, PackageUtils.firstSignatureDigest(context, packageName), fixHost(uri.getHost(), false), uri.getPath());
|
||||
for (DataItemRecord record : records) {
|
||||
syncRecordToAll(record);
|
||||
}
|
||||
return records.size();
|
||||
}
|
||||
|
||||
public void sendMessageReceived(String packageName, MessageEventParcelable messageEvent) {
|
||||
Log.d(TAG, "onMessageReceived: " + messageEvent);
|
||||
Intent intent = new Intent("com.google.android.gms.wearable.MESSAGE_RECEIVED");
|
||||
intent.setPackage(packageName);
|
||||
intent.setData(Uri.parse("wear://" + getLocalNodeId() + "/" + messageEvent.getPath()));
|
||||
invokeListeners(intent, listener -> listener.onMessageReceived(messageEvent));
|
||||
}
|
||||
|
||||
public DataItemRecord getDataItemByUri(Uri uri, String packageName) {
|
||||
Cursor cursor = nodeDatabase.getDataItemsByHostAndPath(packageName, PackageUtils.firstSignatureDigest(context, packageName), fixHost(uri.getHost(), true), uri.getPath());
|
||||
DataItemRecord record = null;
|
||||
if (cursor != null) {
|
||||
if (cursor.moveToNext()) {
|
||||
record = DataItemRecord.fromCursor(cursor);
|
||||
}
|
||||
cursor.close();
|
||||
}
|
||||
Log.d(TAG, "getDataItem: " + record);
|
||||
return record;
|
||||
}
|
||||
|
||||
private IWearableListener getListener(String packageName, String action, Uri uri) {
|
||||
Intent intent = new Intent(action);
|
||||
intent.setPackage(packageName);
|
||||
intent.setData(uri);
|
||||
|
||||
return RemoteListenerProxy.get(context, intent, IWearableListener.class, "com.google.android.gms.wearable.BIND_LISTENER");
|
||||
}
|
||||
|
||||
private void closeConnection(String nodeId) {
|
||||
WearableConnection connection = activeConnections.get(nodeId);
|
||||
try {
|
||||
connection.close();
|
||||
} catch (IOException e1) {
|
||||
Log.w(TAG, e1);
|
||||
}
|
||||
if (connection == sct.getWearableConnection()) {
|
||||
sct.close();
|
||||
sct = null;
|
||||
}
|
||||
activeConnections.remove(nodeId);
|
||||
for (ConnectionConfiguration config : getConfigurations()) {
|
||||
if (nodeId.equals(config.nodeId) || nodeId.equals(config.peerNodeId)) {
|
||||
config.connected = false;
|
||||
}
|
||||
}
|
||||
onPeerDisconnected(new NodeParcelable(nodeId, "Wear device"));
|
||||
Log.d(TAG, "Closed connection to " + nodeId + " on error");
|
||||
}
|
||||
|
||||
public int sendMessage(String packageName, String targetNodeId, String path, byte[] data) {
|
||||
if (activeConnections.containsKey(targetNodeId)) {
|
||||
WearableConnection connection = activeConnections.get(targetNodeId);
|
||||
RpcHelper.RpcConnectionState state = rpcHelper.useConnectionState(packageName, targetNodeId, path);
|
||||
try {
|
||||
connection.writeMessage(new RootMessage.Builder().rpcRequest(new Request.Builder()
|
||||
.targetNodeId(targetNodeId)
|
||||
.path(path)
|
||||
.rawData(ByteString.of(data))
|
||||
.packageName(packageName)
|
||||
.signatureDigest(PackageUtils.firstSignatureDigest(context, packageName))
|
||||
.sourceNodeId(getLocalNodeId())
|
||||
.generation(state.generation)
|
||||
.requestId(state.lastRequestId)
|
||||
.build()).build());
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, "Error while writing, closing link", e);
|
||||
closeConnection(targetNodeId);
|
||||
return -1;
|
||||
}
|
||||
return (state.generation + 527) * 31 + state.lastRequestId;
|
||||
}
|
||||
Log.d(TAG, targetNodeId + " seems not reachable");
|
||||
return -1;
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
try {
|
||||
this.networkHandlerLock.await();
|
||||
this.networkHandler.getLooper().quit();
|
||||
} catch (InterruptedException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
}
|
||||
|
||||
private class ListenerInfo {
|
||||
private IWearableListener listener;
|
||||
private IntentFilter[] filters;
|
||||
|
||||
private ListenerInfo(IWearableListener listener, IntentFilter[] filters) {
|
||||
this.listener = listener;
|
||||
this.filters = filters;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.microg.gms.wearable;
|
||||
|
||||
import android.os.RemoteException;
|
||||
|
||||
import com.google.android.gms.common.internal.GetServiceRequest;
|
||||
import com.google.android.gms.common.internal.IGmsCallbacks;
|
||||
|
||||
import org.microg.gms.BaseService;
|
||||
import org.microg.gms.common.GmsService;
|
||||
import org.microg.gms.common.PackageUtils;
|
||||
|
||||
public class WearableService extends BaseService {
|
||||
|
||||
private WearableImpl wearable;
|
||||
|
||||
public WearableService() {
|
||||
super("GmsWearSvc", GmsService.WEARABLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
ConfigurationDatabaseHelper configurationDatabaseHelper = new ConfigurationDatabaseHelper(getApplicationContext());
|
||||
NodeDatabaseHelper nodeDatabaseHelper = new NodeDatabaseHelper(getApplicationContext());
|
||||
wearable = new WearableImpl(getApplicationContext(), nodeDatabaseHelper, configurationDatabaseHelper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
wearable.stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request, GmsService service) throws RemoteException {
|
||||
PackageUtils.getAndCheckCallingPackage(this, request.packageName);
|
||||
callback.onPostInitComplete(0, new WearableServiceImpl(this, wearable, request.packageName), null);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,507 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2019 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.microg.gms.wearable;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.Parcel;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.android.gms.common.api.Status;
|
||||
import com.google.android.gms.wearable.Asset;
|
||||
import com.google.android.gms.wearable.ConnectionConfiguration;
|
||||
import com.google.android.gms.wearable.internal.*;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class WearableServiceImpl extends IWearableService.Stub {
|
||||
private static final String TAG = "GmsWearSvcImpl";
|
||||
|
||||
private final Context context;
|
||||
private final String packageName;
|
||||
private final WearableImpl wearable;
|
||||
private final Handler mainHandler;
|
||||
private final CapabilityManager capabilities;
|
||||
|
||||
public WearableServiceImpl(Context context, WearableImpl wearable, String packageName) {
|
||||
this.context = context;
|
||||
this.wearable = wearable;
|
||||
this.packageName = packageName;
|
||||
this.capabilities = new CapabilityManager(context, wearable, packageName);
|
||||
this.mainHandler = new Handler(context.getMainLooper());
|
||||
}
|
||||
|
||||
private void postMain(IWearableCallbacks callbacks, RemoteExceptionRunnable runnable) {
|
||||
mainHandler.post(new CallbackRunnable(callbacks) {
|
||||
@Override
|
||||
public void run(IWearableCallbacks callbacks) throws RemoteException {
|
||||
runnable.run();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void postNetwork(IWearableCallbacks callbacks, RemoteExceptionRunnable runnable) {
|
||||
this.wearable.networkHandler.post(new CallbackRunnable(callbacks) {
|
||||
@Override
|
||||
public void run(IWearableCallbacks callbacks) throws RemoteException {
|
||||
runnable.run();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* Config
|
||||
*/
|
||||
|
||||
@Override
|
||||
public void putConfig(IWearableCallbacks callbacks, final ConnectionConfiguration config) throws RemoteException {
|
||||
postMain(callbacks, () -> {
|
||||
wearable.createConnection(config);
|
||||
callbacks.onStatus(Status.SUCCESS);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteConfig(IWearableCallbacks callbacks, final String name) throws RemoteException {
|
||||
postMain(callbacks, () -> {
|
||||
wearable.deleteConnection(name);
|
||||
callbacks.onStatus(Status.SUCCESS);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getConfigs(IWearableCallbacks callbacks) throws RemoteException {
|
||||
Log.d(TAG, "getConfigs");
|
||||
postMain(callbacks, () -> {
|
||||
try {
|
||||
callbacks.onGetConfigsResponse(new GetConfigsResponse(0, wearable.getConfigurations()));
|
||||
} catch (Exception e) {
|
||||
callbacks.onGetConfigsResponse(new GetConfigsResponse(8, new ConnectionConfiguration[0]));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void enableConfig(IWearableCallbacks callbacks, final String name) throws RemoteException {
|
||||
Log.d(TAG, "enableConfig: " + name);
|
||||
postMain(callbacks, () -> {
|
||||
wearable.enableConnection(name);
|
||||
callbacks.onStatus(Status.SUCCESS);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disableConfig(IWearableCallbacks callbacks, final String name) throws RemoteException {
|
||||
Log.d(TAG, "disableConfig: " + name);
|
||||
postMain(callbacks, () -> {
|
||||
wearable.disableConnection(name);
|
||||
callbacks.onStatus(Status.SUCCESS);
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* DataItems
|
||||
*/
|
||||
|
||||
@Override
|
||||
public void putData(IWearableCallbacks callbacks, final PutDataRequest request) throws RemoteException {
|
||||
Log.d(TAG, "putData: " + request.toString(true));
|
||||
this.wearable.networkHandler.post(new CallbackRunnable(callbacks) {
|
||||
@Override
|
||||
public void run(IWearableCallbacks callbacks) throws RemoteException {
|
||||
DataItemRecord record = wearable.putData(request, packageName);
|
||||
callbacks.onPutDataResponse(new PutDataResponse(0, record.toParcelable()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getDataItem(IWearableCallbacks callbacks, final Uri uri) throws RemoteException {
|
||||
Log.d(TAG, "getDataItem: " + uri);
|
||||
postMain(callbacks, () -> {
|
||||
DataItemRecord record = wearable.getDataItemByUri(uri, packageName);
|
||||
if (record != null) {
|
||||
callbacks.onGetDataItemResponse(new GetDataItemResponse(0, record.toParcelable()));
|
||||
} else {
|
||||
callbacks.onGetDataItemResponse(new GetDataItemResponse(0, null));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getDataItems(final IWearableCallbacks callbacks) throws RemoteException {
|
||||
Log.d(TAG, "getDataItems: " + callbacks);
|
||||
postMain(callbacks, () -> {
|
||||
callbacks.onDataItemChanged(wearable.getDataItemsAsHolder(packageName));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getDataItemsByUri(IWearableCallbacks callbacks, Uri uri) throws RemoteException {
|
||||
getDataItemsByUriWithFilter(callbacks, uri, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getDataItemsByUriWithFilter(IWearableCallbacks callbacks, final Uri uri, int typeFilter) throws RemoteException {
|
||||
Log.d(TAG, "getDataItemsByUri: " + uri);
|
||||
postMain(callbacks, () -> {
|
||||
callbacks.onDataItemChanged(wearable.getDataItemsByUriAsHolder(uri, packageName));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteDataItems(IWearableCallbacks callbacks, Uri uri) throws RemoteException {
|
||||
deleteDataItemsWithFilter(callbacks, uri, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteDataItemsWithFilter(IWearableCallbacks callbacks, final Uri uri, int typeFilter) throws RemoteException {
|
||||
Log.d(TAG, "deleteDataItems: " + uri);
|
||||
this.wearable.networkHandler.post(new CallbackRunnable(callbacks) {
|
||||
@Override
|
||||
public void run(IWearableCallbacks callbacks) throws RemoteException {
|
||||
callbacks.onDeleteDataItemsResponse(new DeleteDataItemsResponse(0, wearable.deleteDataItems(uri, packageName)));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(IWearableCallbacks callbacks, final String targetNodeId, final String path, final byte[] data) throws RemoteException {
|
||||
Log.d(TAG, "sendMessage: " + targetNodeId + " / " + path + ": " + (data == null ? null : Base64.encodeToString(data, Base64.NO_WRAP)));
|
||||
this.wearable.networkHandler.post(new CallbackRunnable(callbacks) {
|
||||
@Override
|
||||
public void run(IWearableCallbacks callbacks) throws RemoteException {
|
||||
SendMessageResponse sendMessageResponse = new SendMessageResponse();
|
||||
try {
|
||||
sendMessageResponse.requestId = wearable.sendMessage(packageName, targetNodeId, path, data);
|
||||
if (sendMessageResponse.requestId == -1) {
|
||||
sendMessageResponse.statusCode = 4000;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
sendMessageResponse.statusCode = 8;
|
||||
}
|
||||
mainHandler.post(() -> {
|
||||
try {
|
||||
callbacks.onSendMessageResponse(sendMessageResponse);
|
||||
} catch (RemoteException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getFdForAsset(IWearableCallbacks callbacks, final Asset asset) throws RemoteException {
|
||||
Log.d(TAG, "getFdForAsset " + asset);
|
||||
postMain(callbacks, () -> {
|
||||
// TODO: Access control
|
||||
try {
|
||||
callbacks.onGetFdForAssetResponse(new GetFdForAssetResponse(0, ParcelFileDescriptor.open(wearable.createAssetFile(asset.getDigest()), ParcelFileDescriptor.MODE_READ_ONLY)));
|
||||
} catch (FileNotFoundException e) {
|
||||
callbacks.onGetFdForAssetResponse(new GetFdForAssetResponse(8, null));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void optInCloudSync(IWearableCallbacks callbacks, boolean enable) throws RemoteException {
|
||||
callbacks.onStatus(Status.SUCCESS);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public void getCloudSyncOptInDone(IWearableCallbacks callbacks) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: getCloudSyncOptInDone");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCloudSyncSetting(IWearableCallbacks callbacks, boolean enable) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: setCloudSyncSetting");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getCloudSyncSetting(IWearableCallbacks callbacks) throws RemoteException {
|
||||
callbacks.onGetCloudSyncSettingResponse(new GetCloudSyncSettingResponse(0, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getCloudSyncOptInStatus(IWearableCallbacks callbacks) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: getCloudSyncOptInStatus");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendRemoteCommand(IWearableCallbacks callbacks, byte b) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: sendRemoteCommand: " + b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getLocalNode(IWearableCallbacks callbacks) throws RemoteException {
|
||||
postMain(callbacks, () -> {
|
||||
try {
|
||||
callbacks.onGetLocalNodeResponse(new GetLocalNodeResponse(0, new NodeParcelable(wearable.getLocalNodeId(), wearable.getLocalNodeId())));
|
||||
} catch (Exception e) {
|
||||
callbacks.onGetLocalNodeResponse(new GetLocalNodeResponse(8, null));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getConnectedNodes(IWearableCallbacks callbacks) throws RemoteException {
|
||||
postMain(callbacks, () -> {
|
||||
callbacks.onGetConnectedNodesResponse(new GetConnectedNodesResponse(0, wearable.getConnectedNodesParcelableList()));
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* Capability
|
||||
*/
|
||||
|
||||
@Override
|
||||
public void getConnectedCapability(IWearableCallbacks callbacks, String capability, int nodeFilter) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: getConnectedCapability " + capability + ", " + nodeFilter);
|
||||
postMain(callbacks, () -> {
|
||||
List<NodeParcelable> nodes = new ArrayList<>();
|
||||
for (String host : capabilities.getNodesForCapability(capability)) {
|
||||
nodes.add(new NodeParcelable(host, host));
|
||||
}
|
||||
CapabilityInfoParcelable capabilityInfo = new CapabilityInfoParcelable(capability, nodes);
|
||||
callbacks.onGetCapabilityResponse(new GetCapabilityResponse(0, capabilityInfo));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getAllCapabilities(IWearableCallbacks callbacks, int nodeFilter) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: getConnectedCapaibilties: " + nodeFilter);
|
||||
callbacks.onGetAllCapabilitiesResponse(new GetAllCapabilitiesResponse());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addLocalCapability(IWearableCallbacks callbacks, String capability) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: addLocalCapability: " + capability);
|
||||
this.wearable.networkHandler.post(new CallbackRunnable(callbacks) {
|
||||
@Override
|
||||
public void run(IWearableCallbacks callbacks) throws RemoteException {
|
||||
callbacks.onAddLocalCapabilityResponse(new AddLocalCapabilityResponse(capabilities.add(capability)));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeLocalCapability(IWearableCallbacks callbacks, String capability) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: removeLocalCapability: " + capability);
|
||||
this.wearable.networkHandler.post(new CallbackRunnable(callbacks) {
|
||||
@Override
|
||||
public void run(IWearableCallbacks callbacks) throws RemoteException {
|
||||
callbacks.onRemoveLocalCapabilityResponse(new RemoveLocalCapabilityResponse(capabilities.remove(capability)));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener(IWearableCallbacks callbacks, AddListenerRequest request) throws RemoteException {
|
||||
if (request.listener != null) {
|
||||
wearable.addListener(packageName, request.listener, request.intentFilters);
|
||||
}
|
||||
callbacks.onStatus(Status.SUCCESS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeListener(IWearableCallbacks callbacks, RemoveListenerRequest request) throws RemoteException {
|
||||
wearable.removeListener(request.listener);
|
||||
callbacks.onStatus(Status.SUCCESS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getStorageInformation(IWearableCallbacks callbacks) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: getStorageInformation");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearStorage(IWearableCallbacks callbacks) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: clearStorage");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endCall(IWearableCallbacks callbacks) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: endCall");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void acceptRingingCall(IWearableCallbacks callbacks) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: acceptRingingCall");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void silenceRinger(IWearableCallbacks callbacks) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: silenceRinger");
|
||||
}
|
||||
|
||||
/*
|
||||
* Apple Notification Center Service
|
||||
*/
|
||||
|
||||
@Override
|
||||
public void injectAncsNotificationForTesting(IWearableCallbacks callbacks, AncsNotificationParcelable notification) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: injectAncsNotificationForTesting: " + notification);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doAncsPositiveAction(IWearableCallbacks callbacks, int i) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: doAncsPositiveAction: " + i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doAncsNegativeAction(IWearableCallbacks callbacks, int i) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: doAncsNegativeAction: " + i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void openChannel(IWearableCallbacks callbacks, String s1, String s2) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: openChannel; " + s1 + ", " + s2);
|
||||
}
|
||||
|
||||
/*
|
||||
* Channels
|
||||
*/
|
||||
|
||||
@Override
|
||||
public void closeChannel(IWearableCallbacks callbacks, String s) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: closeChannel: " + s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeChannelWithError(IWearableCallbacks callbacks, String s, int errorCode) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: closeChannelWithError:" + s + ", " + errorCode);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getChannelInputStream(IWearableCallbacks callbacks, IChannelStreamCallbacks channelCallbacks, String s) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: getChannelInputStream: " + s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getChannelOutputStream(IWearableCallbacks callbacks, IChannelStreamCallbacks channelCallbacks, String s) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: getChannelOutputStream: " + s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeChannelInputToFd(IWearableCallbacks callbacks, String s, ParcelFileDescriptor fd) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: writeChannelInputToFd: " + s);
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void syncWifiCredentials(IWearableCallbacks callbacks) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: syncWifiCredentials");
|
||||
}
|
||||
|
||||
/*
|
||||
* Connection deprecated
|
||||
*/
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public void putConnection(IWearableCallbacks callbacks, ConnectionConfiguration config) throws RemoteException {
|
||||
Log.d(TAG, "unimplemented Method: putConnection");
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public void getConnection(IWearableCallbacks callbacks) throws RemoteException {
|
||||
Log.d(TAG, "getConfig");
|
||||
postMain(callbacks, () -> {
|
||||
ConnectionConfiguration[] configurations = wearable.getConfigurations();
|
||||
if (configurations == null || configurations.length == 0) {
|
||||
callbacks.onGetConfigResponse(new GetConfigResponse(1, new ConnectionConfiguration(null, null, 0, 0, false)));
|
||||
} else {
|
||||
callbacks.onGetConfigResponse(new GetConfigResponse(0, configurations[0]));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public void enableConnection(IWearableCallbacks callbacks) throws RemoteException {
|
||||
postMain(callbacks, () -> {
|
||||
ConnectionConfiguration[] configurations = wearable.getConfigurations();
|
||||
if (configurations.length > 0) {
|
||||
enableConfig(callbacks, configurations[0].name);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public void disableConnection(IWearableCallbacks callbacks) throws RemoteException {
|
||||
postMain(callbacks, () -> {
|
||||
ConnectionConfiguration[] configurations = wearable.getConfigurations();
|
||||
if (configurations.length > 0) {
|
||||
disableConfig(callbacks, configurations[0].name);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
|
||||
if (super.onTransact(code, data, reply, flags)) return true;
|
||||
Log.d(TAG, "onTransact [unknown]: " + code + ", " + data + ", " + flags);
|
||||
return false;
|
||||
}
|
||||
|
||||
public abstract class CallbackRunnable implements Runnable {
|
||||
private IWearableCallbacks callbacks;
|
||||
|
||||
public CallbackRunnable(IWearableCallbacks callbacks) {
|
||||
this.callbacks = callbacks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
run(callbacks);
|
||||
} catch (RemoteException e) {
|
||||
mainHandler.post(() -> {
|
||||
try {
|
||||
callbacks.onStatus(Status.CANCELED);
|
||||
} catch (RemoteException e2) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void run(IWearableCallbacks callbacks) throws RemoteException;
|
||||
}
|
||||
|
||||
public interface RemoteExceptionRunnable {
|
||||
void run() throws RemoteException;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,150 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.microg.gms.wearable.location;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.android.gms.common.api.GoogleApiClient;
|
||||
import com.google.android.gms.location.LocationRequest;
|
||||
import com.google.android.gms.common.internal.ClientIdentity;
|
||||
import com.google.android.gms.location.internal.LocationRequestInternal;
|
||||
import com.google.android.gms.wearable.DataMap;
|
||||
import com.google.android.gms.wearable.MessageEvent;
|
||||
import com.google.android.gms.wearable.Node;
|
||||
import com.google.android.gms.wearable.Wearable;
|
||||
import com.google.android.gms.wearable.WearableListenerService;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class WearableLocationService extends WearableListenerService {
|
||||
private static final String TAG = "GmsWearLocSvc";
|
||||
|
||||
public static final String PATH_LOCATION_REQUESTS = "com/google/android/location/fused/wearable/LOCATION_REQUESTS";
|
||||
public static final String PATH_CAPABILITY_QUERY = "com/google/android/location/fused/wearable/CAPABILITY_QUERY";
|
||||
public static final String PATH_CAPABILITY = "com/google/android/location/fused/wearable/CAPABILITY";
|
||||
|
||||
private GoogleApiClient apiClient;
|
||||
private Map<String, Collection<LocationRequestInternal>> requestMap = new HashMap<String, Collection<LocationRequestInternal>>();
|
||||
|
||||
@Override
|
||||
public void onMessageReceived(MessageEvent messageEvent) {
|
||||
if (messageEvent.getPath().equals(PATH_LOCATION_REQUESTS)) {
|
||||
DataMap dataMap = DataMap.fromByteArray(messageEvent.getData());
|
||||
onLocationRequests(messageEvent.getSourceNodeId(), readLocationRequestList(dataMap, this), dataMap.getBoolean("TRIGGER_UPDATE", false));
|
||||
} else if (messageEvent.getPath().equals(PATH_CAPABILITY_QUERY)) {
|
||||
onCapabilityQuery(messageEvent.getSourceNodeId());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPeerDisconnected(Node peer) {
|
||||
onLocationRequests(peer.getId(), null, false);
|
||||
}
|
||||
|
||||
public void onLocationRequests(String nodeId, Collection<LocationRequestInternal> requests, boolean triggerUpdate) {
|
||||
if (requests == null || requests.isEmpty()) {
|
||||
requestMap.remove(nodeId);
|
||||
} else {
|
||||
requestMap.put(nodeId, requests);
|
||||
}
|
||||
Log.d(TAG, "Requests: "+requestMap.entrySet());
|
||||
// TODO actually request
|
||||
}
|
||||
|
||||
public void onCapabilityQuery(String nodeId) {
|
||||
Wearable.MessageApi.sendMessage(getApiClient(), nodeId, PATH_CAPABILITY, writeLocationCapability(new DataMap(), true).toByteArray());
|
||||
}
|
||||
|
||||
private GoogleApiClient getApiClient() {
|
||||
if (apiClient == null) {
|
||||
apiClient = new GoogleApiClient.Builder(this).addApi(Wearable.API).build();
|
||||
}
|
||||
if (!apiClient.isConnected()) {
|
||||
apiClient.connect();
|
||||
}
|
||||
return apiClient;
|
||||
}
|
||||
|
||||
public static DataMap writeLocationCapability(DataMap dataMap, boolean locationCapable) {
|
||||
dataMap.putBoolean("CAPABILITY_LOCATION", locationCapable);
|
||||
return dataMap;
|
||||
}
|
||||
|
||||
public static Collection<LocationRequestInternal> readLocationRequestList(DataMap dataMap, Context context) {
|
||||
if (!dataMap.containsKey("REQUEST_LIST")) {
|
||||
Log.w(TAG, "malformed DataMap: missing key REQUEST_LIST");
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<DataMap> requestMapList = dataMap.getDataMapArrayList("REQUEST_LIST");
|
||||
List<LocationRequestInternal> locationRequests = new ArrayList<LocationRequestInternal>();
|
||||
for (DataMap map : requestMapList) {
|
||||
locationRequests.add(readLocationRequest(map, context));
|
||||
}
|
||||
return locationRequests;
|
||||
}
|
||||
|
||||
private static LocationRequestInternal readLocationRequest(DataMap dataMap, Context context) {
|
||||
LocationRequest locationRequest = new LocationRequest();
|
||||
LocationRequestInternal request = new LocationRequestInternal(locationRequest);
|
||||
request.triggerUpdate = true;
|
||||
request.clients = Collections.emptyList();
|
||||
|
||||
if (dataMap.containsKey("PRIORITY"))
|
||||
locationRequest.setPriority(dataMap.getInt("PRIORITY", 0));
|
||||
if (dataMap.containsKey("INTERVAL_MS"))
|
||||
locationRequest.setInterval(dataMap.getLong("INTERVAL_MS", 0));
|
||||
if (dataMap.containsKey("FASTEST_INTERVAL_MS"))
|
||||
locationRequest.setFastestInterval(dataMap.getLong("FASTEST_INTERVAL_MS", 0));
|
||||
if (dataMap.containsKey("MAX_WAIT_TIME_MS"))
|
||||
locationRequest.setMaxWaitTime(dataMap.getLong("MAX_WAIT_TIME_MS", 0));
|
||||
if (dataMap.containsKey("SMALLEST_DISPLACEMENT_METERS"))
|
||||
locationRequest.setSmallestDisplacement(dataMap.getFloat("SMALLEST_DISPLACEMENT_METERS", 0));
|
||||
if (dataMap.containsKey("NUM_UPDATES"))
|
||||
locationRequest.setNumUpdates(dataMap.getInt("NUM_UPDATES", 0));
|
||||
if (dataMap.containsKey("EXPIRATION_DURATION_MS"))
|
||||
locationRequest.setExpirationDuration(dataMap.getLong("EXPIRATION_DURATION_MS", 0));
|
||||
if (dataMap.containsKey("TAG"))
|
||||
request.tag = dataMap.getString("TAG");
|
||||
if (dataMap.containsKey("CLIENTS_PACKAGE_ARRAY")) {
|
||||
String[] packages = dataMap.getStringArray("CLIENTS_PACKAGE_ARRAY");
|
||||
if (packages != null) {
|
||||
request.clients = new ArrayList<ClientIdentity>();
|
||||
for (String packageName : packages) {
|
||||
request.clients.add(generateClientIdentity(packageName, context));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
private static ClientIdentity generateClientIdentity(String packageName, Context context) {
|
||||
return null;
|
||||
/*try {
|
||||
return new ClientIdentity(context.getPackageManager().getApplicationInfo(packageName, 0).uid, packageName);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Log.w(TAG, "Unknown client identity: " + packageName, e);
|
||||
return new ClientIdentity(context.getApplicationInfo().uid, context.getPackageName());
|
||||
}*/
|
||||
}
|
||||
}
|
||||
18
play-services-wearable/src/main/AndroidManifest.xml
Normal file
18
play-services-wearable/src/main/AndroidManifest.xml
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2013-2017 microG Project Team
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<manifest />
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
package com.google.android.gms.wearable;
|
||||
|
||||
parcelable Asset;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
package com.google.android.gms.wearable;
|
||||
|
||||
parcelable ConnectionConfiguration;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
parcelable AddListenerRequest;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
parcelable AddLocalCapabilityResponse;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
parcelable AmsEntityUpdateParcelable;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
parcelable AncsNotificationParcelable;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
parcelable CapabilityInfoParcelable;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
parcelable ChannelEventParcelable;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
parcelable ChannelReceiveFileResponse;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
parcelable ChannelSendFileResponse;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
parcelable CloseChannelResponse;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
parcelable DeleteDataItemsResponse;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
parcelable GetAllCapabilitiesResponse;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
parcelable GetCapabilityResponse;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
parcelable GetChannelInputStreamResponse;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
parcelable GetChannelOutputStreamResponse;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
parcelable GetCloudSyncOptInOutDoneResponse;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
parcelable GetCloudSyncOptInStatusResponse;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
parcelable GetCloudSyncSettingResponse;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
parcelable GetConfigResponse;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
parcelable GetConfigsResponse;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
parcelable GetConnectedNodesResponse;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
parcelable GetDataItemResponse;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
parcelable GetFdForAssetResponse;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
parcelable GetLocalNodeResponse;
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
interface IChannelStreamCallbacks {
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
import com.google.android.gms.common.api.Status;
|
||||
import com.google.android.gms.common.data.DataHolder;
|
||||
import com.google.android.gms.wearable.internal.AddLocalCapabilityResponse;
|
||||
import com.google.android.gms.wearable.internal.ChannelReceiveFileResponse;
|
||||
import com.google.android.gms.wearable.internal.ChannelSendFileResponse;
|
||||
import com.google.android.gms.wearable.internal.CloseChannelResponse;
|
||||
import com.google.android.gms.wearable.internal.DeleteDataItemsResponse;
|
||||
import com.google.android.gms.wearable.internal.GetAllCapabilitiesResponse;
|
||||
import com.google.android.gms.wearable.internal.GetCapabilityResponse;
|
||||
import com.google.android.gms.wearable.internal.GetChannelInputStreamResponse;
|
||||
import com.google.android.gms.wearable.internal.GetChannelOutputStreamResponse;
|
||||
import com.google.android.gms.wearable.internal.GetCloudSyncOptInOutDoneResponse;
|
||||
import com.google.android.gms.wearable.internal.GetCloudSyncOptInStatusResponse;
|
||||
import com.google.android.gms.wearable.internal.GetCloudSyncSettingResponse;
|
||||
import com.google.android.gms.wearable.internal.GetConfigResponse;
|
||||
import com.google.android.gms.wearable.internal.GetConfigsResponse;
|
||||
import com.google.android.gms.wearable.internal.GetConnectedNodesResponse;
|
||||
import com.google.android.gms.wearable.internal.GetDataItemResponse;
|
||||
import com.google.android.gms.wearable.internal.GetFdForAssetResponse;
|
||||
import com.google.android.gms.wearable.internal.GetLocalNodeResponse;
|
||||
import com.google.android.gms.wearable.internal.OpenChannelResponse;
|
||||
import com.google.android.gms.wearable.internal.PutDataResponse;
|
||||
import com.google.android.gms.wearable.internal.RemoveLocalCapabilityResponse;
|
||||
import com.google.android.gms.wearable.internal.SendMessageResponse;
|
||||
import com.google.android.gms.wearable.internal.StorageInfoResponse;
|
||||
|
||||
interface IWearableCallbacks {
|
||||
// Config
|
||||
void onGetConfigResponse(in GetConfigResponse response) = 1;
|
||||
void onGetConfigsResponse(in GetConfigsResponse response) = 12;
|
||||
|
||||
// Cloud Sync
|
||||
void onGetCloudSyncOptInOutDoneResponse(in GetCloudSyncOptInOutDoneResponse response) = 27;
|
||||
void onGetCloudSyncSettingResponse(in GetCloudSyncSettingResponse response) = 28;
|
||||
void onGetCloudSyncOptInStatusResponse(in GetCloudSyncOptInStatusResponse response) = 29;
|
||||
|
||||
// Data
|
||||
void onPutDataResponse(in PutDataResponse response) = 2;
|
||||
void onGetDataItemResponse(in GetDataItemResponse response) = 3;
|
||||
void onDataItemChanged(in DataHolder dataHolder) = 4;
|
||||
void onDeleteDataItemsResponse(in DeleteDataItemsResponse response) = 5;
|
||||
void onSendMessageResponse(in SendMessageResponse response) = 6;
|
||||
void onGetFdForAssetResponse(in GetFdForAssetResponse response) = 7;
|
||||
void onGetLocalNodeResponse(in GetLocalNodeResponse response) = 8;
|
||||
void onGetConnectedNodesResponse(in GetConnectedNodesResponse response) = 9;
|
||||
|
||||
// Channels
|
||||
void onOpenChannelResponse(in OpenChannelResponse response) = 13;
|
||||
void onCloseChannelResponse(in CloseChannelResponse response) = 14;
|
||||
void onGetChannelInputStreamResponse(in GetChannelInputStreamResponse response) = 16;
|
||||
void onGetChannelOutputStreamResponse(in GetChannelOutputStreamResponse response) = 17;
|
||||
void onChannelReceiveFileResponse(in ChannelReceiveFileResponse response) = 18;
|
||||
void onChannelSendFileResponse(in ChannelSendFileResponse response) = 19;
|
||||
|
||||
void onStatus(in Status status) = 10;
|
||||
void onStorageInfoResponse(in StorageInfoResponse response) = 11;
|
||||
|
||||
// Capabilities
|
||||
void onGetCapabilityResponse(in GetCapabilityResponse response) = 21;
|
||||
void onGetAllCapabilitiesResponse(in GetAllCapabilitiesResponse response) = 22;
|
||||
void onAddLocalCapabilityResponse(in AddLocalCapabilityResponse response) = 25;
|
||||
void onRemoveLocalCapabilityResponse(in RemoveLocalCapabilityResponse response) = 26;
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
import com.google.android.gms.common.data.DataHolder;
|
||||
import com.google.android.gms.wearable.internal.AmsEntityUpdateParcelable;
|
||||
import com.google.android.gms.wearable.internal.AncsNotificationParcelable;
|
||||
import com.google.android.gms.wearable.internal.CapabilityInfoParcelable;
|
||||
import com.google.android.gms.wearable.internal.ChannelEventParcelable;
|
||||
import com.google.android.gms.wearable.internal.MessageEventParcelable;
|
||||
import com.google.android.gms.wearable.internal.NodeParcelable;
|
||||
|
||||
interface IWearableListener {
|
||||
void onDataChanged(in DataHolder data) = 0;
|
||||
void onMessageReceived(in MessageEventParcelable messageEvent) = 1;
|
||||
void onPeerConnected(in NodeParcelable node) = 2;
|
||||
void onPeerDisconnected(in NodeParcelable node) = 3;
|
||||
void onConnectedNodes(in List<NodeParcelable> nodes) = 4;
|
||||
void onNotificationReceived(in AncsNotificationParcelable notification) = 5;
|
||||
void onChannelEvent(in ChannelEventParcelable channelEvent) = 6;
|
||||
void onConnectedCapabilityChanged(in CapabilityInfoParcelable capabilityInfo) = 7;
|
||||
void onEntityUpdate(in AmsEntityUpdateParcelable update) = 8;
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
import com.google.android.gms.wearable.Asset;
|
||||
import com.google.android.gms.wearable.ConnectionConfiguration;
|
||||
import com.google.android.gms.wearable.internal.AddListenerRequest;
|
||||
import com.google.android.gms.wearable.internal.AncsNotificationParcelable;
|
||||
import com.google.android.gms.wearable.internal.PutDataRequest;
|
||||
import com.google.android.gms.wearable.internal.RemoveListenerRequest;
|
||||
import com.google.android.gms.wearable.internal.IChannelStreamCallbacks;
|
||||
import com.google.android.gms.wearable.internal.IWearableCallbacks;
|
||||
import com.google.android.gms.wearable.internal.IWearableService;
|
||||
|
||||
interface IWearableService {
|
||||
// Configs
|
||||
void putConfig(IWearableCallbacks callbacks, in ConnectionConfiguration config) = 19;
|
||||
void deleteConfig(IWearableCallbacks callbacks, String name) = 20;
|
||||
void getConfigs(IWearableCallbacks callbacks) = 21;
|
||||
void enableConfig(IWearableCallbacks callbacks, String name) = 22;
|
||||
void disableConfig(IWearableCallbacks callbacks, String name) = 23;
|
||||
|
||||
// DataItems
|
||||
void putData(IWearableCallbacks callbacks, in PutDataRequest request) = 5;
|
||||
void getDataItem(IWearableCallbacks callbacks, in Uri uri) = 6;
|
||||
void getDataItems(IWearableCallbacks callbacks) = 7;
|
||||
void getDataItemsByUri(IWearableCallbacks callbacks, in Uri uri) = 8;
|
||||
void getDataItemsByUriWithFilter(IWearableCallbacks callbacks, in Uri uri, int typeFilter) = 39;
|
||||
void deleteDataItems(IWearableCallbacks callbacks, in Uri uri) = 10;
|
||||
void deleteDataItemsWithFilter(IWearableCallbacks callbacks, in Uri uri, int typeFilter) = 40;
|
||||
|
||||
void sendMessage(IWearableCallbacks callbacks, String targetNodeId, String path, in byte[] data) = 11;
|
||||
void getFdForAsset(IWearableCallbacks callbacks, in Asset asset) = 12;
|
||||
|
||||
void getLocalNode(IWearableCallbacks callbacks) = 13;
|
||||
void getConnectedNodes(IWearableCallbacks callbacks) = 14;
|
||||
|
||||
// Capabilties
|
||||
void getConnectedCapability(IWearableCallbacks callbacks, String capability, int nodeFilter) = 41;
|
||||
void getAllCapabilities(IWearableCallbacks callbacks, int nodeFilter) = 42;
|
||||
void addLocalCapability(IWearableCallbacks callbacks, String capability) = 45;
|
||||
void removeLocalCapability(IWearableCallbacks callbacks, String capability) = 46;
|
||||
|
||||
void addListener(IWearableCallbacks callbacks, in AddListenerRequest request) = 15;
|
||||
void removeListener(IWearableCallbacks callbacks, in RemoveListenerRequest request) = 16;
|
||||
|
||||
void getStorageInformation(IWearableCallbacks callbacks) = 17;
|
||||
void clearStorage(IWearableCallbacks callbacks) = 18;
|
||||
|
||||
void endCall(IWearableCallbacks callbacks) = 24;
|
||||
void acceptRingingCall(IWearableCallbacks callbacks) = 25;
|
||||
void silenceRinger(IWearableCallbacks callbacks) = 29;
|
||||
|
||||
// Apple Notification Center Service
|
||||
void injectAncsNotificationForTesting(IWearableCallbacks callbacks, in AncsNotificationParcelable notification) = 26;
|
||||
void doAncsPositiveAction(IWearableCallbacks callbacks, int i) = 27;
|
||||
void doAncsNegativeAction(IWearableCallbacks callbacks, int i) = 28;
|
||||
|
||||
// Channels
|
||||
void openChannel(IWearableCallbacks callbacks, String s1, String s2) = 30;
|
||||
void closeChannel(IWearableCallbacks callbacks, String s) = 31;
|
||||
void closeChannelWithError(IWearableCallbacks callbacks, String s, int errorCode) = 32;
|
||||
void getChannelInputStream(IWearableCallbacks callbacks, IChannelStreamCallbacks channelCallbacks, String s) = 33;
|
||||
void getChannelOutputStream(IWearableCallbacks callbacks, IChannelStreamCallbacks channelCallbacks, String s) = 34;
|
||||
void writeChannelInputToFd(IWearableCallbacks callbacks, String s, in ParcelFileDescriptor fd) = 37;
|
||||
void readChannelOutputFromFd(IWearableCallbacks callbacks, String s, in ParcelFileDescriptor fd, long l1, long l2) = 38;
|
||||
|
||||
void syncWifiCredentials(IWearableCallbacks callbacks) = 36;
|
||||
|
||||
// Cloud Sync
|
||||
void optInCloudSync(IWearableCallbacks callbacks, boolean enable) = 47;
|
||||
void getCloudSyncOptInDone(IWearableCallbacks callbacks) = 48; // deprecated
|
||||
void setCloudSyncSetting(IWearableCallbacks callbacks, boolean enable) = 49;
|
||||
void getCloudSyncSetting(IWearableCallbacks callbacks) = 50;
|
||||
void getCloudSyncOptInStatus(IWearableCallbacks callbacks) = 51;
|
||||
|
||||
void sendRemoteCommand(IWearableCallbacks callbacks, byte b) = 52;
|
||||
|
||||
// deprecated Connection
|
||||
void putConnection(IWearableCallbacks callbacks, in ConnectionConfiguration config) = 1;
|
||||
void getConnection(IWearableCallbacks callbacks) = 2;
|
||||
void enableConnection(IWearableCallbacks callbacks) = 3;
|
||||
void disableConnection(IWearableCallbacks callbacks) = 4;
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
parcelable MessageEventParcelable;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
parcelable NodeParcelable;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
parcelable OpenChannelResponse;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
parcelable PutDataRequest;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
parcelable PutDataResponse;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
parcelable RemoveListenerRequest;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
parcelable RemoveLocalCapabilityResponse;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
parcelable SendMessageResponse;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
parcelable StorageInfoResponse;
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.wearable;
|
||||
|
||||
public interface AmsEntityUpdate {
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.wearable;
|
||||
|
||||
public interface AncsNotification {
|
||||
}
|
||||
|
|
@ -0,0 +1,154 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.wearable;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
|
||||
import com.google.android.gms.wearable.internal.PutDataRequest;
|
||||
|
||||
import org.microg.gms.common.PublicApi;
|
||||
import org.microg.safeparcel.AutoSafeParcelable;
|
||||
import org.microg.safeparcel.SafeParceled;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* An asset is a binary blob shared between data items that is replicated across the wearable
|
||||
* network on demand.
|
||||
* <p/>
|
||||
* It may represent an asset not yet added with the Android Wear network. DataItemAssets
|
||||
* are representations of an asset after it has been added to the network through a
|
||||
* {@link PutDataRequest}.
|
||||
*/
|
||||
@PublicApi
|
||||
public class Asset extends AutoSafeParcelable {
|
||||
|
||||
@SafeParceled(1)
|
||||
private int versionCode = 1;
|
||||
@SafeParceled(2)
|
||||
@PublicApi(exclude = true)
|
||||
public byte[] data;
|
||||
@SafeParceled(3)
|
||||
private String digest;
|
||||
@SafeParceled(4)
|
||||
private ParcelFileDescriptor fd;
|
||||
@SafeParceled(5)
|
||||
private Uri uri;
|
||||
|
||||
private Asset() {
|
||||
}
|
||||
|
||||
private Asset(byte[] data, String digest, ParcelFileDescriptor fd, Uri uri) {
|
||||
this.data = data;
|
||||
this.digest = digest;
|
||||
this.fd = fd;
|
||||
this.uri = uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an Asset using a byte array.
|
||||
*/
|
||||
public static Asset createFromBytes(byte[] assetData) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an Asset using a file descriptor. The FD should be closed after being successfully
|
||||
* sent in a putDataItem request.
|
||||
*/
|
||||
public static Asset createFromFd(ParcelFileDescriptor fd) {
|
||||
if (fd == null) {
|
||||
throw new IllegalArgumentException("Asset fd cannot be null");
|
||||
}
|
||||
return new Asset(null, null, fd, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an Asset using an existing Asset's digest.
|
||||
*/
|
||||
public static Asset createFromRef(String digest) {
|
||||
if (digest == null) {
|
||||
throw new IllegalArgumentException("Asset digest cannot be null");
|
||||
}
|
||||
return new Asset(null, digest, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an Asset using a content URI. Google Play services must have permission to read this
|
||||
* Uri.
|
||||
*/
|
||||
public static Asset createFromUri(Uri uri) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the digest associated with the asset data. A digest is a content identifier used to
|
||||
* identify the asset across devices.
|
||||
*/
|
||||
public String getDigest() {
|
||||
return digest;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the file descriptor referencing the asset.
|
||||
*/
|
||||
public ParcelFileDescriptor getFd() {
|
||||
return fd;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the uri referencing the asset data.
|
||||
*/
|
||||
public Uri getUri() {
|
||||
return uri;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
Asset asset = (Asset) o;
|
||||
|
||||
if (!Arrays.equals(data, asset.data)) return false;
|
||||
if (digest != null ? !digest.equals(asset.digest) : asset.digest != null) return false;
|
||||
if (fd != null ? !fd.equals(asset.fd) : asset.fd != null) return false;
|
||||
return !(uri != null ? !uri.equals(asset.uri) : asset.uri != null);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Arrays.hashCode(new Object[]{data, digest, fd, uri});
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append("Asset[@")
|
||||
.append(Integer.toHexString(hashCode()))
|
||||
.append(", ")
|
||||
.append(digest != null ? digest : "nodigest");
|
||||
if (this.data != null) sb.append(", size=").append(data.length);
|
||||
if (this.fd != null) sb.append(", fd=").append(fd);
|
||||
if (this.uri != null) sb.append(", uri=").append(uri);
|
||||
return sb.append("]").toString();
|
||||
}
|
||||
|
||||
public static final Creator<Asset> CREATOR = new AutoCreator<Asset>(Asset.class);
|
||||
}
|
||||
|
|
@ -0,0 +1,193 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.wearable;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
import com.google.android.gms.common.api.GoogleApiClient;
|
||||
import com.google.android.gms.common.api.GoogleApiClient.Builder;
|
||||
import com.google.android.gms.common.api.PendingResult;
|
||||
import com.google.android.gms.common.api.Result;
|
||||
import com.google.android.gms.common.api.Status;
|
||||
|
||||
import org.microg.gms.common.PublicApi;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Exposes an API to learn about capabilities provided by nodes on the Wear network.
|
||||
* <p/>
|
||||
* Capabilities are local to an application.
|
||||
*/
|
||||
@PublicApi
|
||||
public interface CapabilityApi {
|
||||
/**
|
||||
* Capability changed action for use in manifest-based listener filters.
|
||||
* <p/>
|
||||
* Capability events do not support filtering by host, but can be filtered by path.
|
||||
*
|
||||
* @see WearableListenerService
|
||||
*/
|
||||
String ACTION_CAPABILITY_CHANGED = "com.google.android.gms.wearable.CAPABILITY_CHANGED";
|
||||
|
||||
/**
|
||||
* Filter type for {@link #getCapability(GoogleApiClient, String, int)}, {@link #getAllCapabilities(GoogleApiClient, int)}:
|
||||
* If this filter is set then the full set of nodes that declare the given capability will be
|
||||
* included in the capability's CapabilityInfo.
|
||||
*/
|
||||
int FILTER_ALL = 0;
|
||||
|
||||
/**
|
||||
* Filter type for {@link #addListener(GoogleApiClient, CapabilityListener, Uri, int)}, if this
|
||||
* filter is set, the given URI will be taken as a literal path, and the operation will apply
|
||||
* to the matching capability only.
|
||||
*/
|
||||
int FILTER_LITERAL = 0;
|
||||
|
||||
/**
|
||||
* Filter type for {@link #addListener(GoogleApiClient, CapabilityListener, Uri, int)}, if this
|
||||
* filter is set, the given URI will be taken as a path prefix, and the operation will apply
|
||||
* to all matching capabilities.
|
||||
*/
|
||||
int FILTER_PREFIX = 1;
|
||||
|
||||
/**
|
||||
* Filter type for {@link #getCapability(GoogleApiClient, String, int)}, {@link #getAllCapabilities(GoogleApiClient, int):
|
||||
* If this filter is set then only reachable nodes that declare the given capability will be
|
||||
* included in the capability's CapabilityInfo.
|
||||
*/
|
||||
int FILTER_REACHABLE = 1;
|
||||
|
||||
/**
|
||||
* Registers a listener to be notified of a specific capability being added to or removed from
|
||||
* the Wear network. Calls to this method should be balanced with {@link #removeCapabilityListener(GoogleApiClient, CapabilityListener, String)}
|
||||
* to avoid leaking resources.
|
||||
* <p/>
|
||||
* Listener events will be called on the main thread, or the handler specified on {@code client}
|
||||
* when it was built (using {@link Builder#setHandler(Handler)}).
|
||||
* <p/>
|
||||
* Callers wishing to be notified of events in the background should use {@link WearableListenerService}.
|
||||
*/
|
||||
PendingResult<Status> addCapabilityListener(GoogleApiClient client, CapabilityListener listener, String capability);
|
||||
|
||||
/**
|
||||
* Registers a listener to be notified of capabilities being added to or removed from the Wear
|
||||
* network. Calls to this method should be balanced with {@link #removeListener(GoogleApiClient, CapabilityListener)}
|
||||
* to avoid leaking resources.
|
||||
* <p/>
|
||||
* {@code uri} and {@code filterType} can be used to filter the capability changes sent to the
|
||||
* listener. For example, if {@code uri} and {@code filterType} create a prefix filter, then
|
||||
* only capabilities matching that prefix will be notified. The {@code uri} follows the rules
|
||||
* of the {@code <data>} element of {@code <intent-filter>}. The path is ignored if a URI host
|
||||
* is not specified. To match capabilities by name or name prefix, the host must be {@code *}. i.e:
|
||||
* <p/>
|
||||
* <pre>wear://* /<capability_name></pre>
|
||||
* Listener events will be called on the main thread, or the handler specified on {@code client}
|
||||
* when it was built (using {@link Builder#setHandler(Handler)}).
|
||||
*
|
||||
* Callers wishing to be notified of events in the background should use WearableListenerService.
|
||||
*/
|
||||
PendingResult<Status> addListener(GoogleApiClient client, CapabilityListener listener, Uri uri, @CapabilityFilterType int filterType);
|
||||
|
||||
/**
|
||||
* Announces that a capability has become available on the local node.
|
||||
*/
|
||||
PendingResult<AddLocalCapabilityResult> addLocalCapability(GoogleApiClient client, String capability);
|
||||
|
||||
/**
|
||||
* Returns information about all capabilities, including the nodes that declare those
|
||||
* capabilities. The filter parameter controls whether all nodes are returned, {@link #FILTER_ALL},
|
||||
* or only those that are currently reachable by this node, {@link #FILTER_REACHABLE}.
|
||||
* <p/>
|
||||
* The local node will never be returned in the set of nodes.
|
||||
*/
|
||||
PendingResult<GetAllCapabilitiesResult> getAllCapabilities(GoogleApiClient client, @NodeFilterType int nodeFilter);
|
||||
|
||||
/**
|
||||
* Returns information about a capabilities, including the nodes that declare this capability.
|
||||
* The filter parameter controls whether all nodes are returned, {@link #FILTER_ALL}, or only
|
||||
* those that are currently reachable by this node, {@link #FILTER_REACHABLE}.
|
||||
* <p/>
|
||||
* The local node will never be returned in the set of nodes.
|
||||
*/
|
||||
PendingResult<GetCapabilityResult> getCapability(GoogleApiClient client, String capability, @NodeFilterType int nodeFilter);
|
||||
|
||||
/**
|
||||
* Removes a listener which was previously added through {@link #addCapabilityListener(GoogleApiClient, CapabilityListener, String)}.
|
||||
* The listener is only removed from listening for the capability provided and will continue to
|
||||
* receive messages for any other capabilities it was previously registered for that have not
|
||||
* also been removed.
|
||||
*/
|
||||
PendingResult<Status> removeCapabilityListener(GoogleApiClient client, CapabilityListener listener, String capability);
|
||||
|
||||
/**
|
||||
* Removes a listener which was previously added through {@link #addListener(GoogleApiClient, CapabilityListener, Uri, int)}.
|
||||
* The listener is only removed from listening for the capability provided and will continue to
|
||||
* receive messages for any other capabilities it was previously registered for that have not
|
||||
* also been removed.
|
||||
*/
|
||||
PendingResult<Status> removeListener(GoogleApiClient client, CapabilityListener listener);
|
||||
|
||||
/**
|
||||
* Announces that a capability is no longer available on the local node. Note: this will not
|
||||
* remove any capabilities announced in the Manifest for an app.
|
||||
*/
|
||||
PendingResult<RemoveLocalCapabilityResult> removeLocalCapability(GoogleApiClient client, String capability);
|
||||
|
||||
/**
|
||||
* Result returned from {@link #addLocalCapability(GoogleApiClient, String)}
|
||||
*/
|
||||
interface AddLocalCapabilityResult extends Result {
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@interface CapabilityFilterType {
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener for changes in the reachable nodes providing a capability.
|
||||
*/
|
||||
interface CapabilityListener {
|
||||
void onCapabilityChanged(CapabilityInfo capabilityInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Result returned from {@link #getAllCapabilities(GoogleApiClient, int)}
|
||||
*/
|
||||
interface GetAllCapabilitiesResult extends Result {
|
||||
Map<String, CapabilityInfo> getAllCapabilities();
|
||||
}
|
||||
|
||||
/**
|
||||
* Result returned from {@link #getCapability(GoogleApiClient, String, int)}
|
||||
*/
|
||||
interface GetCapabilityResult extends Result {
|
||||
CapabilityInfo getCapability();
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@interface NodeFilterType {
|
||||
}
|
||||
|
||||
/**
|
||||
* Result returned from {@link #removeLocalCapability(GoogleApiClient, String)}
|
||||
*/
|
||||
interface RemoveLocalCapabilityResult extends Result {
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.wearable;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Information about a Capability on the network and where it is available.
|
||||
*/
|
||||
public interface CapabilityInfo {
|
||||
/**
|
||||
* Returns the name of the capability.
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Returns the set of nodes for the capability. Disconnected nodes may or may not be included in the set.
|
||||
*/
|
||||
Set<Node> getNodes();
|
||||
}
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.wearable;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import com.google.android.gms.common.api.GoogleApiClient;
|
||||
import com.google.android.gms.common.api.PendingResult;
|
||||
import com.google.android.gms.common.api.Releasable;
|
||||
import com.google.android.gms.common.api.Result;
|
||||
import com.google.android.gms.common.api.Status;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* A channel created through {@link ChannelApi#openChannel(GoogleApiClient, String, String)}.
|
||||
* <p/>
|
||||
* The implementation of this interface is parcelable and immutable, and implements reasonable {@link #equals(Object)}
|
||||
* and {@link #hashCode()} methods, so can be used in collections.
|
||||
*/
|
||||
public interface Channel extends Parcelable {
|
||||
|
||||
PendingResult<Status> addListener(GoogleApiClient client, ChannelApi.ChannelListener listener);
|
||||
|
||||
PendingResult<Status> close(GoogleApiClient client, int errorCode);
|
||||
|
||||
PendingResult<Status> close(GoogleApiClient client);
|
||||
|
||||
PendingResult<GetInputStreamResult> getInputStream(GoogleApiClient client);
|
||||
|
||||
PendingResult<GetOutputStreamResult> getOutputStream(GoogleApiClient client);
|
||||
|
||||
String getPath();
|
||||
|
||||
PendingResult<Status> receiveFile(GoogleApiClient client, Uri uri, boolean append);
|
||||
|
||||
PendingResult<Status> removeListener(GoogleApiClient client, ChannelApi.ChannelListener listener);
|
||||
|
||||
PendingResult<Status> sendFile(GoogleApiClient client, Uri uri);
|
||||
|
||||
PendingResult<Status> sendFile(GoogleApiClient client, Uri uri, long startOffset, long length);
|
||||
|
||||
interface GetInputStreamResult extends Releasable, Result {
|
||||
/**
|
||||
* Returns an input stream which can read data from the remote node. The stream should be
|
||||
* closed when no longer needed. This method will only return {@code null} if this result's
|
||||
* {@linkplain #getStatus() status} was not {@linkplain Status#isSuccess() success}.
|
||||
* <p/>
|
||||
* The returned stream will throw {@link IOException} on read if any connection errors
|
||||
* occur. This exception might be a {@link ChannelIOException}.
|
||||
* <p/>
|
||||
* Since data for this stream comes over the network, reads may block for a long time.
|
||||
* <p/>
|
||||
* Multiple calls to this method will return the same instance.
|
||||
*/
|
||||
InputStream getInputStream();
|
||||
}
|
||||
|
||||
interface GetOutputStreamResult extends Releasable, Result {
|
||||
/**
|
||||
* Returns an output stream which can send data to a remote node. The stream should be
|
||||
* closed when no longer needed. This method will only return {@code null} if this result's
|
||||
* {@linkplain #getStatus() status} was not {@linkplain Status#isSuccess() success}.
|
||||
* <p/>
|
||||
* The returned stream will throw {@link IOException} on read if any connection errors
|
||||
* occur. This exception might be a {@link ChannelIOException}.
|
||||
* <p/>
|
||||
* Since data for this stream comes over the network, reads may block for a long time.
|
||||
* <p/>
|
||||
* Data written to this stream is buffered. If you wish to send the current data without
|
||||
* waiting for the buffer to fill up, {@linkplain OutputStream#flush() flush} the stream.
|
||||
* <p/>
|
||||
* Multiple calls to this method will return the same instance.
|
||||
*/
|
||||
OutputStream getOutputStream();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,185 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.wearable;
|
||||
|
||||
import com.google.android.gms.common.api.GoogleApiClient;
|
||||
import com.google.android.gms.common.api.GoogleApiClient.Builder;
|
||||
import com.google.android.gms.common.api.PendingResult;
|
||||
import com.google.android.gms.common.api.Result;
|
||||
import com.google.android.gms.common.api.Status;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* Client interface for Wearable Channel API. Allows apps on a wearable device to send and receive
|
||||
* data from other wearable nodes.
|
||||
* <p/>
|
||||
* Channels are bidirectional. Each side, both the initiator and the receiver may both read and
|
||||
* write to the channel by using {@link Channel#getOutputStream(GoogleApiClient)} and {@link Channel#getInputStream(GoogleApiClient)}.
|
||||
* Once a channel is established, the API for the initiator and receiver are identical.
|
||||
* <p/>
|
||||
* Channels are only available when the wearable nodes are connected. When the remote node
|
||||
* disconnects, all existing channels will be closed. Any listeners (added through {@link #addListener(GoogleApiClient, ChannelListener)}
|
||||
* and any installed {@link WearableListenerService}) will be notified of the channel closing.
|
||||
*/
|
||||
public interface ChannelApi {
|
||||
/**
|
||||
* Channel action for use in listener filters.
|
||||
*
|
||||
* @see WearableListenerService
|
||||
*/
|
||||
String ACTION_CHANNEL_EVENT = "com.google.android.gms.wearable.CHANNEL_EVENT";
|
||||
|
||||
/**
|
||||
* Registers a listener to be notified of channel events. Calls to this method should be
|
||||
* balanced with calls to {@link #removeListener(GoogleApiClient, ChannelListener)} to avoid
|
||||
* leaking resources.
|
||||
* <p/>
|
||||
* Listener events will be called on the main thread, or the handler specified on {@code client}
|
||||
* when it was built (using {@link Builder#setHandler(Handler)}).
|
||||
* <p/>
|
||||
* Callers wishing to be notified of events in the background should use {@link WearableListenerService}.
|
||||
*
|
||||
* @param client a connected client
|
||||
* @param listener a listener which will be notified of changes to any channel
|
||||
*/
|
||||
PendingResult<Status> addListener(GoogleApiClient client, ChannelListener listener);
|
||||
|
||||
/**
|
||||
* Opens a channel to exchange data with a remote node.
|
||||
* <p/>
|
||||
* Channel which are no longer needed should be closed using {@link Channel#close(GoogleApiClient)}.
|
||||
* <p/>
|
||||
* This call involves a network round trip, so may be long running. {@code client} must remain
|
||||
* connected during that time, or the request will be cancelled (like any other Play Services
|
||||
* API calls).
|
||||
*
|
||||
* @param client a connected client
|
||||
* @param nodeId the node ID of a wearable node, as returned from {@link NodeApi#getConnectedNodes(GoogleApiClient)}
|
||||
* @param path an app-specific identifier for the channel
|
||||
*/
|
||||
PendingResult<OpenChannelResult> openChannel(GoogleApiClient client, String nodeId, String path);
|
||||
|
||||
/**
|
||||
* Removes a listener which was previously added through {@link #addListener(GoogleApiClient, ChannelListener)}.
|
||||
*
|
||||
* @param client a connected client
|
||||
* @param listener a listener which was added using {@link #addListener(GoogleApiClient, ChannelListener)}
|
||||
*/
|
||||
PendingResult<Status> removeListener(GoogleApiClient client, ChannelListener listener);
|
||||
|
||||
/**
|
||||
* A listener which will be notified on changes to channels.
|
||||
*/
|
||||
interface ChannelListener {
|
||||
/**
|
||||
* Value passed to {@link #onChannelClosed(Channel, int, int)}, {@link #onInputClosed(Channel, int, int)}
|
||||
* and {@link #onOutputClosed(Channel, int, int)} when the closing is due to a remote node
|
||||
* being disconnected.
|
||||
*/
|
||||
int CLOSE_REASON_DISCONNECTED = 1;
|
||||
|
||||
/**
|
||||
* Value passed to {@link #onChannelClosed(Channel, int, int)}, {@link #onInputClosed(Channel, int, int)}
|
||||
* and {@link #onOutputClosed(Channel, int, int)} when the stream is closed due to the
|
||||
* local node calling {@link Channel#close(GoogleApiClient)} or {@link Channel#close(GoogleApiClient, int)}.
|
||||
*/
|
||||
int CLOSE_REASON_LOCAL_CLOSE = 3;
|
||||
|
||||
/**
|
||||
* Value passed to {@link #onInputClosed(Channel, int, int)} or {@link #onOutputClosed(Channel, int, int)}
|
||||
* (but not {@link #onChannelClosed(Channel, int, int)}), when the stream was closed under
|
||||
* normal conditions, e.g the whole file was read, or the OutputStream on the remote node
|
||||
* was closed normally.
|
||||
*/
|
||||
int CLOSE_REASON_NORMAL = 0;
|
||||
|
||||
/**
|
||||
* Value passed to {@link #onChannelClosed(Channel, int, int)}, {@link #onInputClosed(Channel, int, int)}
|
||||
* and {@link #onOutputClosed(Channel, int, int)} when the stream is closed due to the
|
||||
* remote node calling {@link Channel#close(GoogleApiClient)} or {@link Channel#close(GoogleApiClient, int)}.
|
||||
*/
|
||||
int CLOSE_REASON_REMOTE_CLOSE = 2;
|
||||
|
||||
/**
|
||||
* Called when a channel is closed. This can happen through an explicit call to {@link Channel#close(GoogleApiClient)}
|
||||
* or {@link #close(GoogleApiClient, int)} on either side of the connection, or due to
|
||||
* disconnecting from the remote node.
|
||||
*
|
||||
* @param closeReason the reason for the channel closing. One of {@link #CLOSE_REASON_DISCONNECTED},
|
||||
* {@link #CLOSE_REASON_REMOTE_CLOSE}, or {@link #CLOSE_REASON_LOCAL_CLOSE}.
|
||||
* @param appSpecificErrorCode the error code specified on {@link Channel#close(GoogleApiClient, int)},
|
||||
* or 0 if closeReason is {@link #CLOSE_REASON_DISCONNECTED}.
|
||||
*/
|
||||
void onChannelClosed(Channel channel, int closeReason, int appSpecificErrorCode);
|
||||
|
||||
/**
|
||||
* Called when a new channel is opened by a remote node.
|
||||
*/
|
||||
void onChannelOpened(Channel channel);
|
||||
|
||||
/**
|
||||
* Called when the input side of a channel is closed.
|
||||
*
|
||||
* @param closeReason the reason for the channel closing. One of {@link #CLOSE_REASON_DISCONNECTED},
|
||||
* {@link #CLOSE_REASON_REMOTE_CLOSE}, {@link #CLOSE_REASON_LOCAL_CLOSE}
|
||||
* or {@link #CLOSE_REASON_NORMAL}.
|
||||
* @param appSpecificErrorCode the error code specified on {@link Channel#close(GoogleApiClient, int)},
|
||||
* or 0 if closeReason is {@link #CLOSE_REASON_DISCONNECTED} or
|
||||
* {@link #CLOSE_REASON_NORMAL}.
|
||||
*/
|
||||
void onInputClosed(Channel channel, @CloseReason int closeReason, int appSpecificErrorCode);
|
||||
|
||||
/**
|
||||
* Called when the output side of a channel is closed.
|
||||
*
|
||||
* @param closeReason the reason for the channel closing. One of {@link #CLOSE_REASON_DISCONNECTED},
|
||||
* {@link #CLOSE_REASON_REMOTE_CLOSE}, {@link #CLOSE_REASON_LOCAL_CLOSE}
|
||||
* or {@link #CLOSE_REASON_NORMAL}.
|
||||
* @param appSpecificErrorCode the error code specified on {@link Channel#close(GoogleApiClient, int)},
|
||||
* or 0 if closeReason is {@link #CLOSE_REASON_DISCONNECTED} or
|
||||
* {@link #CLOSE_REASON_NORMAL}.
|
||||
*/
|
||||
void onOutputClosed(Channel channel, @CloseReason int closeReason, int appSpecificErrorCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* An annotation for values passed to {@link ChannelListener#onChannelClosed(Channel, int, int)},
|
||||
* and other methods on the {@link ChannelListener} interface. Annotated method parameters will
|
||||
* always take one of the following values:
|
||||
* <ul>
|
||||
* <li>{@link ChannelListener#CLOSE_REASON_DISCONNECTED}</li>
|
||||
* <li>{@link ChannelListener#CLOSE_REASON_NORMAL}</li>
|
||||
* <li>{@link ChannelListener#CLOSE_REASON_LOCAL_CLOSE}</li>
|
||||
* <li>{@link ChannelListener#CLOSE_REASON_REMOTE_CLOSE}</li>
|
||||
* </ul>
|
||||
*/
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@interface CloseReason {
|
||||
}
|
||||
|
||||
/**
|
||||
* Result of {@link #openChannel(GoogleApiClient, String, String)}.
|
||||
*/
|
||||
interface OpenChannelResult extends Result {
|
||||
/**
|
||||
* Returns the newly created channel, or {@code null}, if the connection couldn't be opened.
|
||||
*/
|
||||
Channel getChannel();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.wearable;
|
||||
|
||||
import com.google.android.gms.wearable.ChannelApi.ChannelListener;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* A subclass of {@link IOException} which can be thrown from the streams returned by
|
||||
* {@link Channel#getInputStream(GoogleApiClient)} and {@link Channel#getOutputStream(GoogleApiClient)}.
|
||||
*/
|
||||
public class ChannelIOException extends IOException {
|
||||
|
||||
private int closeReason;
|
||||
private int appSpecificErrorCode;
|
||||
|
||||
public ChannelIOException(String message, int closeReason, int appSpecificErrorCode) {
|
||||
super(message);
|
||||
this.closeReason = closeReason;
|
||||
this.appSpecificErrorCode = appSpecificErrorCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the app-specific error code passed to {@link Channel#close(GoogleApiClient, int)} if
|
||||
* that's the reason for the stream closing, or {@code 0} otherwise.
|
||||
*/
|
||||
public int getAppSpecificErrorCode() {
|
||||
return appSpecificErrorCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns one of {@link ChannelListener#CLOSE_REASON_NORMAL}, {@link ChannelListener#CLOSE_REASON_DISCONNECTED},
|
||||
* {@link ChannelListener#CLOSE_REASON_REMOTE_CLOSE}, or {@link ChannelListener#CLOSE_REASON_LOCAL_CLOSE},
|
||||
* to indicate the reason for the stream closing.
|
||||
*/
|
||||
public int getCloseReason() {
|
||||
return closeReason;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.wearable;
|
||||
|
||||
import org.microg.safeparcel.AutoSafeParcelable;
|
||||
import org.microg.safeparcel.SafeParceled;
|
||||
|
||||
public class ConnectionConfiguration extends AutoSafeParcelable {
|
||||
|
||||
@SafeParceled(1)
|
||||
private int versionCode = 1;
|
||||
@SafeParceled(2)
|
||||
public final String name;
|
||||
@SafeParceled(3)
|
||||
public final String address;
|
||||
@SafeParceled(4)
|
||||
public final int type;
|
||||
@SafeParceled(5)
|
||||
public final int role;
|
||||
@SafeParceled(6)
|
||||
public final boolean enabled;
|
||||
@SafeParceled(7)
|
||||
public boolean connected = false;
|
||||
@SafeParceled(8)
|
||||
public String peerNodeId;
|
||||
@SafeParceled(9)
|
||||
public boolean btlePriority = true;
|
||||
@SafeParceled(10)
|
||||
public String nodeId;
|
||||
|
||||
private ConnectionConfiguration() {
|
||||
name = address = null;
|
||||
type = role = 0;
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
public ConnectionConfiguration(String name, String address, int type, int role, boolean enabled) {
|
||||
this.name = name;
|
||||
this.address = address;
|
||||
this.type = type;
|
||||
this.role = role;
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public ConnectionConfiguration(String name, String address, int type, int role, boolean enabled, String nodeId) {
|
||||
this.name = name;
|
||||
this.address = address;
|
||||
this.type = type;
|
||||
this.role = role;
|
||||
this.enabled = enabled;
|
||||
this.nodeId = nodeId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder sb = new StringBuilder("ConnectionConfiguration{");
|
||||
sb.append("name='").append(name).append('\'');
|
||||
sb.append(", address='").append(address).append('\'');
|
||||
sb.append(", type=").append(type);
|
||||
sb.append(", role=").append(role);
|
||||
sb.append(", enabled=").append(enabled);
|
||||
sb.append(", connected=").append(connected);
|
||||
sb.append(", peerNodeId='").append(peerNodeId).append('\'');
|
||||
sb.append(", btlePriority=").append(btlePriority);
|
||||
sb.append(", nodeId='").append(nodeId).append('\'');
|
||||
sb.append('}');
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static final Creator<ConnectionConfiguration> CREATOR = new AutoCreator<ConnectionConfiguration>(ConnectionConfiguration.class);
|
||||
}
|
||||
|
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.wearable;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
|
||||
import com.google.android.gms.common.api.GoogleApiClient;
|
||||
import com.google.android.gms.common.api.PendingResult;
|
||||
import com.google.android.gms.common.api.Result;
|
||||
import com.google.android.gms.common.api.Status;
|
||||
import com.google.android.gms.common.data.Freezable;
|
||||
import com.google.android.gms.wearable.internal.PutDataRequest;
|
||||
|
||||
import org.microg.gms.common.PublicApi;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* Exposes an API for components to read or write data items and assets.
|
||||
* <p/>
|
||||
* A {@link DataItem} is synchronized across all devices in an Android Wear network. It is possible
|
||||
* to set data items while not connected to any nodes. Those data items will be synchronized when
|
||||
* the nodes eventually come online.
|
||||
* <p/>
|
||||
* Data items are private to the application that created them, and are only accessible by that
|
||||
* application on other nodes. They should generally be small in size, relying on assets for the
|
||||
* transfer of larger, more persistent data objects such as images.
|
||||
* <p/>
|
||||
* Each data item is identified by a URI, accessible with {@link DataItem#getUri()}, that indicates
|
||||
* the item's creator and path. Fully specified URIs follow the following format:
|
||||
* {@code wear://<node_id>/<path>}, where <node_id> is the node ID of the wearable node that
|
||||
* created the data item, and <path> is an application-defined path. This means that given a data
|
||||
* item's URI, calling {@link Uri#getHost()} will return the creator's node ID.
|
||||
* <p/>
|
||||
* In some of the methods below (such as {@link #getDataItems(GoogleApiClient, Uri)}), it is
|
||||
* possible to omit the node ID from the URI, and only leave a path. In that case, the URI may
|
||||
* refer to multiple data items, since multiple nodes may create data items with the same path.
|
||||
* Partially specified data item URIs follow the following format:
|
||||
* {@ocde wear:/<path>}
|
||||
* Note the single / after wear:.
|
||||
*/
|
||||
@PublicApi
|
||||
public interface DataApi {
|
||||
/**
|
||||
* Registers a listener to receive data item changed and deleted events. This call should be
|
||||
* balanced with a call to {@link #removeListener(GoogleApiClient, DataListener)}, to avoid
|
||||
* leaking resources.
|
||||
* <p/>
|
||||
* The listener will be notified of changes initiated by this node.
|
||||
*/
|
||||
PendingResult<Status> addListener(GoogleApiClient client, DataListener listener);
|
||||
|
||||
/**
|
||||
* Removes all specified data items from the Android Wear network.
|
||||
* <p/>
|
||||
* If uri is fully specified, this method will delete at most one data item. If {@code uri}
|
||||
* contains no host, multiple data items may be deleted, since different nodes may create data
|
||||
* items with the same path. See {@link DataApi} for details of the URI format.
|
||||
*/
|
||||
PendingResult<DeleteDataItemsResult> deleteDataItems(GoogleApiClient client, Uri uri);
|
||||
|
||||
/**
|
||||
* Retrieves a single {@link DataItem} from the Android Wear network. A fully qualified URI
|
||||
* must be specified. The URI's host must be the ID of the node that created the item.
|
||||
* <p/>
|
||||
* See {@link DataApi} for details of the URI format.
|
||||
*/
|
||||
PendingResult<DataItemResult> getDataItem(GoogleApiClient client, Uri uri);
|
||||
|
||||
/**
|
||||
* Retrieves all data items from the Android Wear network.
|
||||
* <p/>
|
||||
* Callers must call {@link DataItemBuffer#release()} on the returned buffer when finished
|
||||
* processing results.
|
||||
*/
|
||||
PendingResult<DataItemBuffer> getDataItems(GoogleApiClient client);
|
||||
|
||||
/**
|
||||
* Retrieves all data items matching the provided URI, from the Android Wear network.
|
||||
* <p/>
|
||||
* The URI must contain a path. If {@code uri} is fully specified, at most one data item will
|
||||
* be returned. If uri contains no host, multiple data items may be returned, since different
|
||||
* nodes may create data items with the same path. See {@link DataApi} for details of the URI
|
||||
* format.
|
||||
* <p/>
|
||||
* Callers must call {@link DataItemBuffer#release()} on the returned buffer when finished
|
||||
* processing results.
|
||||
*/
|
||||
PendingResult<DataItemBuffer> getDataItems(GoogleApiClient client, Uri uri);
|
||||
|
||||
/**
|
||||
* Retrieves a {@link ParcelFileDescriptor} pointing at the bytes of an asset. Only assets
|
||||
* previously stored in a {@link DataItem} may be retrieved.
|
||||
*/
|
||||
PendingResult<GetFdForAssetResult> getFdForAsset(GoogleApiClient client, DataItemAsset asset);
|
||||
|
||||
/**
|
||||
* Retrieves a {@link ParcelFileDescriptor} pointing at the bytes of an asset. Only assets
|
||||
* previously stored in a {@link DataItem} may be retrieved.
|
||||
*/
|
||||
PendingResult<GetFdForAssetResult> getFdForAsset(GoogleApiClient client, Asset asset);
|
||||
|
||||
/**
|
||||
* Adds a {@link DataItem} to the Android Wear network. The updated item is synchronized across
|
||||
* all devices.
|
||||
*/
|
||||
PendingResult<DataItemResult> putDataItem(GoogleApiClient client, PutDataRequest request);
|
||||
|
||||
/**
|
||||
* Removes a data listener which was previously added through
|
||||
* {@link #addListener(GoogleApiClient, DataListener)}.
|
||||
*/
|
||||
PendingResult<Status> removeListener(GoogleApiClient client, DataListener listener);
|
||||
|
||||
interface DataItemResult extends Result {
|
||||
/**
|
||||
* @return data item, or {@code null} if the item does not exit.
|
||||
*/
|
||||
DataItem getDataItem();
|
||||
}
|
||||
|
||||
interface DataListener {
|
||||
/**
|
||||
* Notification that a set of data items have been changed or deleted. The data buffer is
|
||||
* released upon completion of this method. If a caller wishes to use the events outside
|
||||
* this callback, they should be sure to {@link Freezable#freeze()} the DataEvent objects
|
||||
* they wish to use.
|
||||
*/
|
||||
void onDataChanged(DataEventBuffer dataEvents);
|
||||
}
|
||||
|
||||
interface DeleteDataItemsResult extends Result {
|
||||
/**
|
||||
* @return the number of items deleted by
|
||||
* {@link DataApi#deleteDataItems(GoogleApiClient, Uri)}.
|
||||
*/
|
||||
int getNumDeleted();
|
||||
}
|
||||
|
||||
interface GetFdForAssetResult extends Result {
|
||||
/**
|
||||
* @return a file descriptor for the requested asset.
|
||||
*/
|
||||
ParcelFileDescriptor getFd();
|
||||
|
||||
/**
|
||||
* @return an input stream wrapping the file descriptor. When this input stream is closed, the file descriptor is, as well.
|
||||
*/
|
||||
InputStream getInputStream();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.wearable;
|
||||
|
||||
import com.google.android.gms.common.data.Freezable;
|
||||
|
||||
import org.microg.gms.common.PublicApi;
|
||||
|
||||
/**
|
||||
* Data interface for data events.
|
||||
*/
|
||||
@PublicApi
|
||||
public interface DataEvent extends Freezable<DataEvent> {
|
||||
|
||||
/**
|
||||
* Indicates that the enclosing {@link DataEvent} was triggered by a data item being added or
|
||||
* changed.
|
||||
*/
|
||||
int TYPE_CHANGED = 1;
|
||||
|
||||
/**
|
||||
* Indicates that the enclosing {@link DataEvent} was triggered by a data item being deleted.
|
||||
*/
|
||||
int TYPE_DELETED = 2;
|
||||
|
||||
/**
|
||||
* @return the data item modified in this event. An event of {@link #TYPE_DELETED} will only
|
||||
* have its {@link DataItem#getUri} populated.
|
||||
*/
|
||||
DataItem getDataItem();
|
||||
|
||||
/**
|
||||
* @return the type of event this is. One of {@link #TYPE_CHANGED}, {@link #TYPE_DELETED}.
|
||||
*/
|
||||
int getType();
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.wearable;
|
||||
|
||||
import com.google.android.gms.common.api.Result;
|
||||
import com.google.android.gms.common.api.Status;
|
||||
import com.google.android.gms.common.data.AbstractDataBuffer;
|
||||
import com.google.android.gms.common.data.DataBuffer;
|
||||
import com.google.android.gms.common.data.DataHolder;
|
||||
|
||||
import org.microg.gms.common.PublicApi;
|
||||
|
||||
/**
|
||||
* Data structure holding references to a set of events.
|
||||
*/
|
||||
@PublicApi
|
||||
public class DataEventBuffer extends AbstractDataBuffer<DataEvent> implements Result {
|
||||
private Status status;
|
||||
|
||||
@PublicApi(exclude = true)
|
||||
public DataEventBuffer(DataHolder dataHolder) {
|
||||
super(dataHolder);
|
||||
status = new Status(dataHolder.getStatusCode());
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataEvent get(int position) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Status getStatus() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.wearable;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
import com.google.android.gms.common.data.Freezable;
|
||||
|
||||
import org.microg.gms.common.PublicApi;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* The base object of data stored in the Android Wear network. {@link DataItem} are replicated
|
||||
* across all devices in the network. It contains a small blob of data and associated assets.
|
||||
* <p/>
|
||||
* A {@link DataItem} is identified by its Uri, which contains its creator and a path.
|
||||
*/
|
||||
@PublicApi
|
||||
public interface DataItem extends Freezable<DataItem> {
|
||||
/**
|
||||
* A map of assets associated with this data item.
|
||||
*/
|
||||
Map<String, DataItemAsset> getAssets();
|
||||
|
||||
/**
|
||||
* An array of data stored at the specified {@link Uri}.
|
||||
*/
|
||||
byte[] getData();
|
||||
|
||||
/**
|
||||
* Returns the DataItem's Uri. {@link Uri#getHost()} returns the id of the node that created it.
|
||||
*/
|
||||
Uri getUri();
|
||||
|
||||
/**
|
||||
* Sets the data in a data item.
|
||||
* <p/>
|
||||
* The current maximum data item size limit is approximately 100k. Data items should generally be much smaller than this limit.
|
||||
*/
|
||||
DataItem setData(byte[] data);
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.wearable;
|
||||
|
||||
import com.google.android.gms.common.data.Freezable;
|
||||
|
||||
/**
|
||||
* A reference to an asset stored in a data item. Used to fetch file descriptors using
|
||||
* DataApi#getFdForAsset(GoogleApiClient, DataItemAsset).
|
||||
*/
|
||||
public interface DataItemAsset extends Freezable<DataItemAsset> {
|
||||
/**
|
||||
* @return the identifier used to address this asset in the context of an existing
|
||||
* {@link DataItem}.
|
||||
*/
|
||||
String getDataItemKey();
|
||||
|
||||
/**
|
||||
* @return the Android Wear-wide unique identifier for a particular asset.
|
||||
*/
|
||||
String getId();
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.wearable;
|
||||
|
||||
import com.google.android.gms.common.api.Result;
|
||||
import com.google.android.gms.common.api.Status;
|
||||
import com.google.android.gms.common.data.AbstractDataBuffer;
|
||||
import com.google.android.gms.common.data.DataBuffer;
|
||||
import com.google.android.gms.common.data.DataHolder;
|
||||
|
||||
import org.microg.gms.common.PublicApi;
|
||||
|
||||
@PublicApi
|
||||
public class DataItemBuffer extends AbstractDataBuffer<DataItem> implements Result {
|
||||
private Status status;
|
||||
|
||||
@PublicApi(exclude = true)
|
||||
public DataItemBuffer(DataHolder dataHolder) {
|
||||
super(dataHolder);
|
||||
status = new Status(dataHolder.getStatusCode());
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataItem get(int position) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Status getStatus() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,463 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.wearable;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import org.microg.gms.common.PublicApi;
|
||||
import org.microg.gms.wearable.databundle.DataBundleUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.microg.gms.wearable.databundle.DataBundleUtil.ASSET_TYPE_CODE;
|
||||
import static org.microg.gms.wearable.databundle.DataBundleUtil.BOOLEAN_TYPE_CODE;
|
||||
import static org.microg.gms.wearable.databundle.DataBundleUtil.BYTE_ARRAY_TYPE_CODE;
|
||||
import static org.microg.gms.wearable.databundle.DataBundleUtil.BYTE_TYPE_CODE;
|
||||
import static org.microg.gms.wearable.databundle.DataBundleUtil.DATAMAP_TYPE_CODE;
|
||||
import static org.microg.gms.wearable.databundle.DataBundleUtil.DOUBLE_TYPE_CODE;
|
||||
import static org.microg.gms.wearable.databundle.DataBundleUtil.FLOAT_ARRAY_TYPE_CODE;
|
||||
import static org.microg.gms.wearable.databundle.DataBundleUtil.FLOAT_TYPE_CODE;
|
||||
import static org.microg.gms.wearable.databundle.DataBundleUtil.INTEGER_TYPE_CODE;
|
||||
import static org.microg.gms.wearable.databundle.DataBundleUtil.LIST_TYPE_CODE;
|
||||
import static org.microg.gms.wearable.databundle.DataBundleUtil.LONG_ARRAY_TYPE_CODE;
|
||||
import static org.microg.gms.wearable.databundle.DataBundleUtil.LONG_TYPE_CODE;
|
||||
import static org.microg.gms.wearable.databundle.DataBundleUtil.STRING_ARRAY_TYPE_CODE;
|
||||
import static org.microg.gms.wearable.databundle.DataBundleUtil.STRING_TYPE_CODE;
|
||||
|
||||
/**
|
||||
* A map of data supported by {@link PutDataMapRequest} and {@link DataMapItem}s. DataMap may
|
||||
* convert to and from Bundles, but will drop any types not explicitly supported by DataMap in the
|
||||
* conversion process.
|
||||
*/
|
||||
@PublicApi
|
||||
public class DataMap {
|
||||
public static String TAG = "GmsDataMap";
|
||||
|
||||
private Map<String, Object> data = new HashMap<String, Object>();
|
||||
private Map<String, StoredType> types = new HashMap<String, StoredType>();
|
||||
|
||||
public DataMap() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return an ArrayList of DataMaps from an ArrayList of Bundles. Any elements in the Bundles not supported by DataMap will be dropped.
|
||||
*/
|
||||
public static ArrayList<DataMap> arrayListFromBundleArrayList(ArrayList<Bundle> bundleArrayList) {
|
||||
ArrayList<DataMap> res = new ArrayList<DataMap>();
|
||||
for (Bundle bundle : bundleArrayList) {
|
||||
res.add(fromBundle(bundle));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all elements from the mapping of this DataMap.
|
||||
*/
|
||||
public void clear() {
|
||||
data.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the given key is contained in the mapping of this DataMap.
|
||||
*/
|
||||
public boolean containsKey(String key) {
|
||||
return data.containsKey(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the given Object is a DataMap equivalent to this one.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return o instanceof DataMap && data.equals(((DataMap) o).data);
|
||||
}
|
||||
|
||||
public StoredType getType(String key) {
|
||||
return types.get(key);
|
||||
}
|
||||
|
||||
@PublicApi(exclude = true)
|
||||
public enum StoredType {
|
||||
Asset(ASSET_TYPE_CODE), Boolean(BOOLEAN_TYPE_CODE), Byte(BYTE_TYPE_CODE),
|
||||
ByteArray(BYTE_ARRAY_TYPE_CODE), DataMap(DATAMAP_TYPE_CODE), DataMapArrayList(DataMap),
|
||||
Double(DOUBLE_TYPE_CODE), Float(FLOAT_TYPE_CODE), FloatArray(FLOAT_ARRAY_TYPE_CODE),
|
||||
Integer(INTEGER_TYPE_CODE), IntegerArrayList(Integer), Long(LONG_TYPE_CODE),
|
||||
LongArray(LONG_ARRAY_TYPE_CODE), String(STRING_TYPE_CODE),
|
||||
StringArray(STRING_ARRAY_TYPE_CODE), StringArrayList(String);
|
||||
|
||||
private int typeCode;
|
||||
private StoredType listType;
|
||||
|
||||
StoredType(int typeCode) {
|
||||
this.typeCode = typeCode;
|
||||
}
|
||||
|
||||
StoredType(StoredType listType) {
|
||||
this.typeCode = LIST_TYPE_CODE;
|
||||
this.listType = listType;
|
||||
}
|
||||
|
||||
public int getTypeCode() {
|
||||
return typeCode;
|
||||
}
|
||||
|
||||
public StoredType getListType() {
|
||||
return listType;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a DataMap from a Bundle. The input Bundle is expected to contain only elements
|
||||
* supported by DataMap. Any elements in the Bundle not supported by DataMap will be dropped.
|
||||
*/
|
||||
public static DataMap fromBundle(Bundle bundle) {
|
||||
DataMap res = new DataMap();
|
||||
if (bundle != null) {
|
||||
for (String key : bundle.keySet()) {
|
||||
Object val = bundle.get(key);
|
||||
if (val instanceof Asset) {
|
||||
res.putAsset(key, (Asset) val);
|
||||
} else if (val instanceof Boolean) {
|
||||
res.putBoolean(key, (Boolean) val);
|
||||
} else if (val instanceof Byte) {
|
||||
res.putByte(key, (Byte) val);
|
||||
} else if (val instanceof byte[]) {
|
||||
res.putByteArray(key, (byte[]) val);
|
||||
} else if (val instanceof Bundle) {
|
||||
res.putDataMap(key, DataMap.fromBundle((Bundle) val));
|
||||
} else if (val instanceof Double) {
|
||||
res.putDouble(key, (Double) val);
|
||||
} else if (val instanceof Float) {
|
||||
res.putFloat(key, (Float) val);
|
||||
} else if (val instanceof float[]) {
|
||||
res.putFloatArray(key, (float[]) val);
|
||||
} else if (val instanceof Integer) {
|
||||
res.putInt(key, (Integer) val);
|
||||
} else if (val instanceof Long) {
|
||||
res.putLong(key, (Long) val);
|
||||
} else if (val instanceof long[]) {
|
||||
res.putLongArray(key, (long[]) val);
|
||||
} else if (val instanceof String) {
|
||||
res.putString(key, (String) val);
|
||||
} else if (val instanceof String[]) {
|
||||
res.putStringArray(key, (String[]) val);
|
||||
} else if (val instanceof ArrayList) {
|
||||
if (((ArrayList) val).isEmpty() || ((ArrayList) val).get(0) instanceof String) {
|
||||
res.putStringArrayList(key, (ArrayList<String>) val);
|
||||
} else if (((ArrayList) val).get(0) instanceof Bundle) {
|
||||
ArrayList<DataMap> dataMaps = new ArrayList<DataMap>();
|
||||
for (Bundle b : ((ArrayList<Bundle>) val)) {
|
||||
dataMaps.add(DataMap.fromBundle(b));
|
||||
}
|
||||
res.putDataMapArrayList(key, dataMaps);
|
||||
} else if (((ArrayList) val).get(0) instanceof Integer) {
|
||||
res.putIntegerArrayList(key, (ArrayList<Integer>) val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a DataMap from a byte[].
|
||||
*/
|
||||
public static DataMap fromByteArray(byte[] bytes) {
|
||||
return DataBundleUtil.readDataMap(bytes, Collections.<Asset>emptyList());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the entry with the given key as an object, or null
|
||||
*/
|
||||
public <T> T get(String key) {
|
||||
return (T) data.get(key);
|
||||
}
|
||||
|
||||
public Asset getAsset(String key) {
|
||||
return types.get(key) == StoredType.Asset ? (Asset) data.get(key) : null;
|
||||
}
|
||||
|
||||
public boolean getBoolean(String key) {
|
||||
return getBoolean(key, false);
|
||||
}
|
||||
|
||||
public boolean getBoolean(String key, boolean defaultValue) {
|
||||
return types.get(key) == StoredType.Boolean ? (Boolean) data.get(key) : defaultValue;
|
||||
}
|
||||
|
||||
public byte getByte(String key) {
|
||||
return getByte(key, (byte) 0);
|
||||
}
|
||||
|
||||
public byte getByte(String key, byte defaultValue) {
|
||||
return types.get(key) == StoredType.Byte ? (Byte) data.get(key) : defaultValue;
|
||||
}
|
||||
|
||||
public byte[] getByteArray(String key) {
|
||||
return types.get(key) == StoredType.ByteArray ? (byte[]) data.get(key) : null;
|
||||
}
|
||||
|
||||
public DataMap getDataMap(String key) {
|
||||
return types.get(key) == StoredType.DataMap ? (DataMap) data.get(key) : null;
|
||||
}
|
||||
|
||||
public ArrayList<DataMap> getDataMapArrayList(String key) {
|
||||
return types.get(key) == StoredType.DataMapArrayList ? (ArrayList<DataMap>) data.get(key) : null;
|
||||
}
|
||||
|
||||
public double getDouble(String key) {
|
||||
return getDouble(key, 0.0);
|
||||
}
|
||||
|
||||
public double getDouble(String key, double defaultValue) {
|
||||
return types.get(key) == StoredType.Double ? (Double) data.get(key) : defaultValue;
|
||||
}
|
||||
|
||||
public float getFloat(String key) {
|
||||
return getFloat(key, 0.0f);
|
||||
}
|
||||
|
||||
public float getFloat(String key, float defaultValue) {
|
||||
return types.get(key) == StoredType.Float ? (Float) data.get(key) : defaultValue;
|
||||
}
|
||||
|
||||
public float[] getFloatArray(String key) {
|
||||
return types.get(key) == StoredType.FloatArray ? (float[]) data.get(key) : null;
|
||||
}
|
||||
|
||||
public int getInt(String key) {
|
||||
return getInt(key, 0);
|
||||
}
|
||||
|
||||
public int getInt(String key, int defaultValue) {
|
||||
return types.get(key) == StoredType.Integer ? (Integer) data.get(key) : defaultValue;
|
||||
}
|
||||
|
||||
public ArrayList<Integer> getIntegerArrayList(String key) {
|
||||
return types.get(key) == StoredType.IntegerArrayList ? (ArrayList<Integer>) data.get(key) : null;
|
||||
}
|
||||
|
||||
public long getLong(String key) {
|
||||
return getLong(key, 0L);
|
||||
}
|
||||
|
||||
public long getLong(String key, long defaultValue) {
|
||||
return types.get(key) == StoredType.Long ? (Long) data.get(key) : defaultValue;
|
||||
}
|
||||
|
||||
public long[] getLongArray(String key) {
|
||||
return types.get(key) == StoredType.LongArray ? (long[]) data.get(key) : null;
|
||||
}
|
||||
|
||||
public String getString(String key) {
|
||||
return getString(key, null);
|
||||
}
|
||||
|
||||
public String getString(String key, String defaultValue) {
|
||||
return types.get(key) == StoredType.String ? (String) data.get(key) : defaultValue;
|
||||
}
|
||||
|
||||
public String[] getStringArray(String key) {
|
||||
return types.get(key) == StoredType.StringArray ? (String[]) data.get(key) : null;
|
||||
}
|
||||
|
||||
public ArrayList<String> getStringArrayList(String key) {
|
||||
return types.get(key) == StoredType.StringArrayList ? (ArrayList<String>) data.get(key) : null;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return data.hashCode();
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return data.isEmpty();
|
||||
}
|
||||
|
||||
public Set<String> keySet() {
|
||||
return data.keySet();
|
||||
}
|
||||
|
||||
public void putAll(DataMap dataMap) {
|
||||
for (String key : dataMap.keySet()) {
|
||||
data.put(key, dataMap.data.get(key));
|
||||
types.put(key, dataMap.types.get(key));
|
||||
}
|
||||
}
|
||||
|
||||
public void putAsset(String key, Asset value) {
|
||||
data.put(key, value);
|
||||
types.put(key, StoredType.Asset);
|
||||
}
|
||||
|
||||
public void putBoolean(String key, boolean value) {
|
||||
data.put(key, value);
|
||||
types.put(key, StoredType.Boolean);
|
||||
}
|
||||
|
||||
public void putByte(String key, byte value) {
|
||||
data.put(key, value);
|
||||
types.put(key, StoredType.Byte);
|
||||
}
|
||||
|
||||
public void putByteArray(String key, byte[] value) {
|
||||
data.put(key, value);
|
||||
types.put(key, StoredType.ByteArray);
|
||||
}
|
||||
|
||||
public void putDataMap(String key, DataMap value) {
|
||||
data.put(key, value);
|
||||
types.put(key, StoredType.DataMap);
|
||||
}
|
||||
|
||||
public void putDataMapArrayList(String key, ArrayList<DataMap> value) {
|
||||
data.put(key, value);
|
||||
types.put(key, StoredType.DataMapArrayList);
|
||||
}
|
||||
|
||||
public void putDouble(String key, double value) {
|
||||
data.put(key, value);
|
||||
types.put(key, StoredType.Double);
|
||||
}
|
||||
|
||||
public void putFloat(String key, float value) {
|
||||
data.put(key, value);
|
||||
types.put(key, StoredType.Float);
|
||||
}
|
||||
|
||||
public void putFloatArray(String key, float[] value) {
|
||||
data.put(key, value);
|
||||
types.put(key, StoredType.FloatArray);
|
||||
}
|
||||
|
||||
public void putInt(String key, int value) {
|
||||
data.put(key, value);
|
||||
types.put(key, StoredType.Integer);
|
||||
}
|
||||
|
||||
public void putIntegerArrayList(String key, ArrayList<Integer> value) {
|
||||
data.put(key, value);
|
||||
types.put(key, StoredType.IntegerArrayList);
|
||||
}
|
||||
|
||||
public void putLong(String key, long value) {
|
||||
data.put(key, value);
|
||||
types.put(key, StoredType.Long);
|
||||
}
|
||||
|
||||
public void putLongArray(String key, long[] value) {
|
||||
data.put(key, value);
|
||||
types.put(key, StoredType.LongArray);
|
||||
}
|
||||
|
||||
public void putString(String key, String value) {
|
||||
data.put(key, value);
|
||||
types.put(key, StoredType.String);
|
||||
}
|
||||
|
||||
public void putStringArray(String key, String[] value) {
|
||||
data.put(key, value);
|
||||
types.put(key, StoredType.StringArray);
|
||||
}
|
||||
|
||||
public void putStringArrayList(String key, ArrayList<String> value) {
|
||||
data.put(key, value);
|
||||
types.put(key, StoredType.StringArrayList);
|
||||
}
|
||||
|
||||
public Object remove(String key) {
|
||||
types.remove(key);
|
||||
return data.remove(key);
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return data.size();
|
||||
}
|
||||
|
||||
public Bundle toBundle() {
|
||||
Bundle bundle = new Bundle();
|
||||
for (String key : data.keySet()) {
|
||||
switch (types.get(key)) {
|
||||
case Asset:
|
||||
bundle.putParcelable(key, (Asset) data.get(key));
|
||||
break;
|
||||
case Boolean:
|
||||
bundle.putBoolean(key, (Boolean) data.get(key));
|
||||
break;
|
||||
case Byte:
|
||||
bundle.putByte(key, (Byte) data.get(key));
|
||||
break;
|
||||
case ByteArray:
|
||||
bundle.putByteArray(key, (byte[]) data.get(key));
|
||||
break;
|
||||
case DataMap:
|
||||
bundle.putBundle(key, ((DataMap) data.get(key)).toBundle());
|
||||
break;
|
||||
case DataMapArrayList:
|
||||
ArrayList<Bundle> bundles = new ArrayList<Bundle>();
|
||||
for (DataMap dataMap : ((ArrayList<DataMap>) data.get(key))) {
|
||||
bundles.add(dataMap.toBundle());
|
||||
}
|
||||
bundle.putParcelableArrayList(key, bundles);
|
||||
break;
|
||||
case Double:
|
||||
bundle.putDouble(key, (Double) data.get(key));
|
||||
break;
|
||||
case Float:
|
||||
bundle.putFloat(key, (Float) data.get(key));
|
||||
break;
|
||||
case FloatArray:
|
||||
bundle.putFloatArray(key, (float[]) data.get(key));
|
||||
break;
|
||||
case Integer:
|
||||
bundle.putInt(key, (Integer) data.get(key));
|
||||
break;
|
||||
case IntegerArrayList:
|
||||
bundle.putIntegerArrayList(key, (ArrayList<Integer>) data.get(key));
|
||||
break;
|
||||
case Long:
|
||||
bundle.putLong(key, (Long) data.get(key));
|
||||
break;
|
||||
case LongArray:
|
||||
bundle.putLongArray(key, (long[]) data.get(key));
|
||||
break;
|
||||
case String:
|
||||
bundle.putString(key, (String) data.get(key));
|
||||
break;
|
||||
case StringArray:
|
||||
bundle.putStringArray(key, (String[]) data.get(key));
|
||||
break;
|
||||
case StringArrayList:
|
||||
bundle.putStringArrayList(key, (ArrayList<String>) data.get(key));
|
||||
break;
|
||||
}
|
||||
}
|
||||
return bundle;
|
||||
}
|
||||
|
||||
public byte[] toByteArray() {
|
||||
return DataBundleUtil.createBytes(this);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "DataMap{size=" + size() + "}";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.wearable;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
import org.microg.gms.common.PublicApi;
|
||||
|
||||
/**
|
||||
* Creates a new dataItem-like object containing structured and serializable data.
|
||||
*/
|
||||
@PublicApi
|
||||
public class DataMapItem {
|
||||
/**
|
||||
* Provides a {@link DataMapItem} wrapping a dataItem.
|
||||
*
|
||||
* @param dataItem the base for the wrapped {@link DataMapItem}. {@code dataItem} should not
|
||||
* be modified after wrapping it.
|
||||
*/
|
||||
public static DataMapItem fromDataItem(DataItem dataItem) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public DataMap getDataMap() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Uri getUri() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.wearable;
|
||||
|
||||
import com.google.android.gms.common.api.GoogleApiClient;
|
||||
import com.google.android.gms.common.api.PendingResult;
|
||||
import com.google.android.gms.common.api.Result;
|
||||
import com.google.android.gms.common.api.Status;
|
||||
|
||||
import org.microg.gms.common.PublicApi;
|
||||
|
||||
@PublicApi
|
||||
public interface MessageApi {
|
||||
|
||||
/**
|
||||
* A value returned by {@link SendMessageResult#getRequestId()} when
|
||||
* {@link #sendMessage(GoogleApiClient, String, String, byte[])} fails.
|
||||
*/
|
||||
int UNKNOWN_REQUEST_ID = -1;
|
||||
|
||||
/**
|
||||
* Registers a listener to be notified of received messages. Calls to this method should
|
||||
* balanced with {@link #removeListener(GoogleApiClient, MessageListener)} to avoid leaking
|
||||
* resources.
|
||||
* <p/>
|
||||
* Callers wishing to be notified of events in the background should use {@link WearableListenerService}.
|
||||
*/
|
||||
PendingResult<Status> addListener(GoogleApiClient client, MessageListener listener);
|
||||
|
||||
/**
|
||||
* Removes a message listener which was previously added through
|
||||
* {@link #addListener(GoogleApiClient, MessageListener)}.
|
||||
*/
|
||||
PendingResult<Status> removeListener(GoogleApiClient client, MessageListener listener);
|
||||
|
||||
/**
|
||||
* Sends {@code byte[]} data to the specified node.
|
||||
*
|
||||
* @param nodeId identifier for a particular node on the Android Wear network. Valid targets
|
||||
* may be obtained through {@link NodeApi#getConnectedNodes(GoogleApiClient)}
|
||||
* or from the host in {@link DataItem#getUri()}.
|
||||
* @param path identifier used to specify a particular endpoint at the receiving node
|
||||
* @param data small array of information to pass to the target node. Generally not larger
|
||||
* than 100k
|
||||
*/
|
||||
PendingResult<SendMessageResult> sendMessage(GoogleApiClient client, String nodeId,
|
||||
String path, byte[] data);
|
||||
|
||||
/**
|
||||
* Used with {@link MessageApi#addListener(GoogleApiClient, MessageListener)} to receive
|
||||
* message events.
|
||||
* <p/>
|
||||
* Callers wishing to be notified of events in the background should use
|
||||
* {@link WearableListenerService}.
|
||||
*/
|
||||
interface MessageListener {
|
||||
/**
|
||||
* Notification that a message has been received.
|
||||
*/
|
||||
void onMessageReceived(MessageEvent messageEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains the request id assigned to the message. On failure, the id will be
|
||||
* {@link MessageApi#UNKNOWN_REQUEST_ID} and the status will be unsuccessful.
|
||||
*/
|
||||
interface SendMessageResult extends Result {
|
||||
/**
|
||||
* @return an ID used to identify the sent message. If {@link #getStatus()} is not
|
||||
* successful, this value will be {@link MessageApi#UNKNOWN_REQUEST_ID}.
|
||||
*/
|
||||
int getRequestId();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.wearable;
|
||||
|
||||
import org.microg.gms.common.PublicApi;
|
||||
|
||||
@PublicApi
|
||||
public interface MessageEvent {
|
||||
/**
|
||||
* @return the data passed by the message.
|
||||
*/
|
||||
byte[] getData();
|
||||
|
||||
/**
|
||||
* @return the path the message is being delivered to
|
||||
*/
|
||||
String getPath();
|
||||
|
||||
/**
|
||||
* @return the request id of the message, generated by the sender
|
||||
*/
|
||||
int getRequestId();
|
||||
|
||||
/**
|
||||
* @return the node ID of the sender.
|
||||
*/
|
||||
String getSourceNodeId();
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.wearable;
|
||||
|
||||
import org.microg.gms.common.PublicApi;
|
||||
|
||||
/**
|
||||
* Information about a particular node in the Android Wear network.
|
||||
*/
|
||||
@PublicApi
|
||||
public interface Node {
|
||||
/**
|
||||
* Returns a human readable description of the node. Sometimes generated from the Bluetooth
|
||||
* device name
|
||||
*/
|
||||
String getDisplayName();
|
||||
|
||||
/**
|
||||
* Returns an opaque string that represents a node in the Android Wear network.
|
||||
*/
|
||||
String getId();
|
||||
|
||||
/**
|
||||
* Indicates that this node can be considered geographically nearby the local node.
|
||||
*/
|
||||
boolean isNearby();
|
||||
}
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.wearable;
|
||||
|
||||
import com.google.android.gms.common.api.GoogleApiClient;
|
||||
import com.google.android.gms.common.api.PendingResult;
|
||||
import com.google.android.gms.common.api.Result;
|
||||
import com.google.android.gms.common.api.Status;
|
||||
|
||||
import org.microg.gms.common.PublicApi;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Exposes an API for to learn about local or connected Nodes.
|
||||
* <p/>
|
||||
* Node events are delivered to all applications on a device.
|
||||
*/
|
||||
@PublicApi
|
||||
public interface NodeApi {
|
||||
|
||||
/**
|
||||
* Registers a listener to receive all node events. Calls to this method should balanced with
|
||||
* {@link #removeListener(GoogleApiClient, NodeListener)}, to avoid leaking resources.
|
||||
* <p/>
|
||||
* Callers wishing to be notified of node events in the background should use WearableListenerService.
|
||||
*/
|
||||
PendingResult<Status> addListener(GoogleApiClient client, NodeListener listener);
|
||||
|
||||
/**
|
||||
* Gets a list of nodes to which this device is currently connected.
|
||||
* <p/>
|
||||
* The returned list will not include the {@link #getLocalNode(GoogleApiClient) local node}.
|
||||
*/
|
||||
PendingResult<GetConnectedNodesResult> getConnectedNodes(GoogleApiClient client);
|
||||
|
||||
/**
|
||||
* Gets the {@link Node} that refers to this device. The information in the returned Node
|
||||
* can be passed to other devices using the {@link MessageApi}, for example.
|
||||
*/
|
||||
PendingResult<GetLocalNodeResult> getLocalNode(GoogleApiClient client);
|
||||
|
||||
/**
|
||||
* Removes a listener which was previously added through
|
||||
* {@link #addListener(GoogleApiClient, NodeListener)}.
|
||||
*/
|
||||
PendingResult<Status> removeListener(GoogleApiClient client, NodeListener listener);
|
||||
|
||||
|
||||
/**
|
||||
* Contains a list of connected nodes.
|
||||
*/
|
||||
interface GetConnectedNodesResult extends Result {
|
||||
/**
|
||||
* @return a list of connected nodes. This list doesn't include the local node.
|
||||
*/
|
||||
List<Node> getNodes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains the name and id that represents this device.
|
||||
*/
|
||||
interface GetLocalNodeResult extends Result {
|
||||
/**
|
||||
* @return a {@link Node} object which represents this device.
|
||||
*/
|
||||
Node getNode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Used with {@link NodeApi#addListener(GoogleApiClient, NodeListener)} to receive node events.
|
||||
*/
|
||||
interface NodeListener {
|
||||
/**
|
||||
* Notification that a peer has been connected.
|
||||
*/
|
||||
void onPeerConnected(Node peer);
|
||||
|
||||
/**
|
||||
* Notification that a peer has been disconnected.
|
||||
*/
|
||||
void onPeerDisconnected(Node peer);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.wearable;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
import com.google.android.gms.wearable.internal.PutDataRequest;
|
||||
|
||||
import org.microg.gms.common.PublicApi;
|
||||
|
||||
/**
|
||||
* PutDataMapRequest is a DataMap-aware version of {@link PutDataRequest}.
|
||||
*/
|
||||
@PublicApi
|
||||
public class PutDataMapRequest {
|
||||
|
||||
private DataMapItem dataMapItem;
|
||||
|
||||
private PutDataMapRequest(DataMapItem dataMapItem) {
|
||||
this.dataMapItem = dataMapItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link PutDataRequest} containing the data and assets in this
|
||||
* {@link PutDataMapRequest}.
|
||||
*/
|
||||
public PutDataRequest asPutDataRequest() {
|
||||
// TODO
|
||||
return PutDataRequest.create((Uri) null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link PutDataMapRequest} with the provided, complete, path.
|
||||
*/
|
||||
public static PutDataMapRequest create(String path) {
|
||||
// TODO
|
||||
return new PutDataMapRequest(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link PutDataMapRequest} from a {@link DataMapItem} using the provided source.
|
||||
*/
|
||||
public static PutDataMapRequest createFromDataMapItem(DataMapItem source) {
|
||||
return new PutDataMapRequest(source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link PutDataMapRequest} with a randomly generated id prefixed with the provided
|
||||
* path.
|
||||
*/
|
||||
public static PutDataMapRequest createWithAutoAppendedId(String pathPrefix) {
|
||||
// TODO
|
||||
return new PutDataMapRequest(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the structured data associated with this data item.
|
||||
*/
|
||||
public DataMap getDataMap() {
|
||||
return dataMapItem.getDataMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a {@link Uri} for the pending data item. If this is a modification of an existing
|
||||
* data item, {@link Uri#getHost()} will return the id of the node that originally created it.
|
||||
* Otherwise, a new data item will be created with the requesting device's node.
|
||||
*/
|
||||
public Uri getUri() {
|
||||
return dataMapItem.getUri();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.wearable;
|
||||
|
||||
import com.google.android.gms.common.api.Api;
|
||||
import com.google.android.gms.common.api.GoogleApiClient;
|
||||
|
||||
import org.microg.gms.common.PublicApi;
|
||||
import org.microg.gms.wearable.DataApiImpl;
|
||||
import org.microg.gms.wearable.MessageApiImpl;
|
||||
import org.microg.gms.wearable.NodeApiImpl;
|
||||
import org.microg.gms.wearable.WearableApiClientBuilder;
|
||||
|
||||
/**
|
||||
* An API for the Android Wear platform.
|
||||
*/
|
||||
@PublicApi
|
||||
public class Wearable {
|
||||
/**
|
||||
* Token to pass to {@link GoogleApiClient.Builder#addApi(Api)} to enable the Wearable features.
|
||||
*/
|
||||
public static final Api<WearableOptions> API = new Api<WearableOptions>(new WearableApiClientBuilder());
|
||||
|
||||
public static final DataApi DataApi = new DataApiImpl();
|
||||
public static final MessageApi MessageApi = new MessageApiImpl();
|
||||
public static final NodeApi NodeApi = new NodeApiImpl();
|
||||
|
||||
public static class WearableOptions implements Api.ApiOptions.Optional {
|
||||
/**
|
||||
* Special option for microG to allow implementation of a FOSS first party Android Wear app
|
||||
*/
|
||||
@PublicApi(exclude = true)
|
||||
public boolean firstPartyMode = false;
|
||||
|
||||
public static class Builder {
|
||||
public WearableOptions build() {
|
||||
return new WearableOptions();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,269 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.wearable;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.os.Binder;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.android.gms.common.data.DataHolder;
|
||||
import com.google.android.gms.wearable.internal.AmsEntityUpdateParcelable;
|
||||
import com.google.android.gms.wearable.internal.AncsNotificationParcelable;
|
||||
import com.google.android.gms.wearable.internal.CapabilityInfoParcelable;
|
||||
import com.google.android.gms.wearable.internal.ChannelEventParcelable;
|
||||
import com.google.android.gms.wearable.internal.IWearableListener;
|
||||
import com.google.android.gms.wearable.internal.MessageEventParcelable;
|
||||
import com.google.android.gms.wearable.internal.NodeParcelable;
|
||||
|
||||
import org.microg.gms.common.PublicApi;
|
||||
import org.microg.gms.wearable.ChannelImpl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.microg.gms.common.Constants.GMS_PACKAGE_NAME;
|
||||
|
||||
@PublicApi
|
||||
public abstract class WearableListenerService extends Service implements CapabilityApi.CapabilityListener, ChannelApi.ChannelListener, DataApi.DataListener, MessageApi.MessageListener, NodeApi.NodeListener {
|
||||
private static final String BIND_LISTENER_INTENT_ACTION = "com.google.android.gms.wearable.BIND_LISTENER";
|
||||
private static final String TAG = "GmsWearListenerSvc";
|
||||
|
||||
private HandlerThread handlerThread;
|
||||
private IWearableListener listener;
|
||||
private ServiceHandler serviceHandler;
|
||||
private Object lock = new Object();
|
||||
private boolean disconnected = false;
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
if (BIND_LISTENER_INTENT_ACTION.equals(intent.getAction())) {
|
||||
return listener.asBinder();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCapabilityChanged(CapabilityInfo capabilityInfo) {
|
||||
}
|
||||
|
||||
public void onConnectedNodes(List<Node> connectedNodes) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChannelClosed(Channel channel, int closeReason, int appSpecificErrorCode) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChannelOpened(Channel channel) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
handlerThread = new HandlerThread("WearableListenerService");
|
||||
handlerThread.start();
|
||||
serviceHandler = new ServiceHandler(handlerThread.getLooper());
|
||||
listener = new Listener();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDataChanged(DataEventBuffer dataEvents) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
synchronized (lock) {
|
||||
if (serviceHandler == null) {
|
||||
throw new IllegalStateException("serviceHandler not set, did you override onCreate() but forget to call super.onCreate()?");
|
||||
}
|
||||
serviceHandler.getLooper().quit();
|
||||
}
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@PublicApi(exclude = true)
|
||||
public void onEntityUpdate(AmsEntityUpdate entityUpdate) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInputClosed(Channel channel, @ChannelApi.CloseReason int closeReason, int appSpecificErrorCode) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessageReceived(MessageEvent messageEvent) {
|
||||
}
|
||||
|
||||
@PublicApi(exclude = true)
|
||||
public void onNotificationReceived(AncsNotification notification) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOutputClosed(Channel channel, @ChannelApi.CloseReason int closeReason, int appSpecificErrorCode) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPeerConnected(Node peer) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPeerDisconnected(Node peer) {
|
||||
}
|
||||
|
||||
private class Listener extends IWearableListener.Stub {
|
||||
private int knownGoodUid = -1;
|
||||
|
||||
private boolean post(Runnable runnable) {
|
||||
int callingUid = Binder.getCallingUid();
|
||||
if (callingUid != knownGoodUid) {
|
||||
// TODO: Verify Gms is calling
|
||||
String[] packagesForUid = getPackageManager().getPackagesForUid(callingUid);
|
||||
if (packagesForUid != null) {
|
||||
if (Arrays.asList(packagesForUid).contains(GMS_PACKAGE_NAME)) {
|
||||
knownGoodUid = callingUid;
|
||||
} else {
|
||||
throw new SecurityException("Caller is not Services Core");
|
||||
}
|
||||
}
|
||||
}
|
||||
synchronized (lock) {
|
||||
if (disconnected) {
|
||||
return false;
|
||||
}
|
||||
serviceHandler.post(runnable);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDataChanged(final DataHolder data) throws RemoteException {
|
||||
post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
WearableListenerService.this.onDataChanged(new DataEventBuffer(data));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessageReceived(final MessageEventParcelable messageEvent) throws RemoteException {
|
||||
post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
WearableListenerService.this.onMessageReceived(messageEvent);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPeerConnected(final NodeParcelable node) throws RemoteException {
|
||||
post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
WearableListenerService.this.onPeerConnected(node);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPeerDisconnected(final NodeParcelable node) throws RemoteException {
|
||||
post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
WearableListenerService.this.onPeerDisconnected(node);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectedNodes(final List<NodeParcelable> nodes) throws RemoteException {
|
||||
post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
WearableListenerService.this.onConnectedNodes(new ArrayList<Node>(nodes));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectedCapabilityChanged(final CapabilityInfoParcelable capabilityInfo) throws RemoteException {
|
||||
post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
WearableListenerService.this.onCapabilityChanged(capabilityInfo);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNotificationReceived(final AncsNotificationParcelable notification) throws RemoteException {
|
||||
post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
WearableListenerService.this.onNotificationReceived(notification);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEntityUpdate(final AmsEntityUpdateParcelable update) throws RemoteException {
|
||||
post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
WearableListenerService.this.onEntityUpdate(update);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChannelEvent(final ChannelEventParcelable channelEvent) throws RemoteException {
|
||||
post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
switch (channelEvent.eventType) {
|
||||
case 1:
|
||||
WearableListenerService.this.onChannelOpened(new ChannelImpl(channelEvent.channel));
|
||||
break;
|
||||
case 2:
|
||||
WearableListenerService.this.onChannelClosed(new ChannelImpl(channelEvent.channel), channelEvent.closeReason, channelEvent.appSpecificErrorCode);
|
||||
break;
|
||||
case 3:
|
||||
WearableListenerService.this.onInputClosed(new ChannelImpl(channelEvent.channel), channelEvent.closeReason, channelEvent.appSpecificErrorCode);
|
||||
break;
|
||||
case 4:
|
||||
WearableListenerService.this.onOutputClosed(new ChannelImpl(channelEvent.channel), channelEvent.closeReason, channelEvent.appSpecificErrorCode);
|
||||
break;
|
||||
default:
|
||||
Log.w(TAG, "Unknown ChannelEvent.eventType");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private class ServiceHandler extends Handler {
|
||||
public ServiceHandler(Looper looper) {
|
||||
super(looper);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.wearable;
|
||||
|
||||
import com.google.android.gms.common.api.CommonStatusCodes;
|
||||
|
||||
import org.microg.gms.common.PublicApi;
|
||||
|
||||
/**
|
||||
* Error codes for wearable API failures. These values may be returned by APIs to indicate the
|
||||
* success or failure of a request.
|
||||
*/
|
||||
@PublicApi
|
||||
public class WearableStatusCodes extends CommonStatusCodes {
|
||||
/**
|
||||
* Indicates that the targeted node is not accessible in the wearable network.
|
||||
*/
|
||||
public static final int TARGET_NODE_NOT_CONNECTED = 4000;
|
||||
/**
|
||||
* Indicates that the specified listener is already registered.
|
||||
*/
|
||||
public static final int DUPLICATE_LISTENER = 4001;
|
||||
/**
|
||||
* Indicates that the specified listener is not recognized.
|
||||
*/
|
||||
public static final int UNKNOWN_LISTENER = 4002;
|
||||
/**
|
||||
* Indicates that the data item was too large to set.
|
||||
*/
|
||||
public static final int DATA_ITEM_TOO_LARGE = 4003;
|
||||
/**
|
||||
* Indicates that the targeted node is not a valid node in the wearable network.
|
||||
*/
|
||||
public static final int INVALID_TARGET_NODE = 4004;
|
||||
/**
|
||||
* Indicates that the requested asset is unavailable.
|
||||
*/
|
||||
public static final int ASSET_UNAVAILABLE = 4005;
|
||||
/**
|
||||
* Indicates that the specified capability already exists.
|
||||
*/
|
||||
public static final int DUPLICATE_CAPABILITY = 4006;
|
||||
/**
|
||||
* Indicates that the specified capability is not recognized.
|
||||
*/
|
||||
public static final int UNKNOWN_CAPABILITY = 4007;
|
||||
/**
|
||||
* Indicates that the WiFi credential sync no credential fetched.
|
||||
*/
|
||||
public static final int WIFI_CREDENTIAL_SYNC_NO_CREDENTIAL_FETCHED = 4008;
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
import android.content.IntentFilter;
|
||||
|
||||
import org.microg.safeparcel.AutoSafeParcelable;
|
||||
import org.microg.safeparcel.SafeParceled;
|
||||
|
||||
public class AddListenerRequest extends AutoSafeParcelable {
|
||||
@SafeParceled(1)
|
||||
private int versionCode = 1;
|
||||
@SafeParceled(2)
|
||||
public final IWearableListener listener;
|
||||
@SafeParceled(3)
|
||||
public final IntentFilter[] intentFilters;
|
||||
@SafeParceled(4)
|
||||
public final String channelTokenString;
|
||||
|
||||
private AddListenerRequest() {
|
||||
listener = null;
|
||||
intentFilters = null;
|
||||
channelTokenString = null;
|
||||
}
|
||||
|
||||
public AddListenerRequest(IWearableListener listener, IntentFilter[] intentFilters, String channelTokenString) {
|
||||
this.listener = listener;
|
||||
this.intentFilters = intentFilters;
|
||||
this.channelTokenString = channelTokenString;
|
||||
}
|
||||
|
||||
public static final Creator<AddListenerRequest> CREATOR = new AutoCreator<AddListenerRequest>(AddListenerRequest.class);
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright (C) 2019 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
import org.microg.safeparcel.AutoSafeParcelable;
|
||||
import org.microg.safeparcel.SafeParceled;
|
||||
|
||||
public class AddLocalCapabilityResponse extends AutoSafeParcelable {
|
||||
@SafeParceled(1)
|
||||
private int versionCode = 1;
|
||||
@SafeParceled(2)
|
||||
private int status = 0;
|
||||
|
||||
private AddLocalCapabilityResponse() {
|
||||
}
|
||||
|
||||
public AddLocalCapabilityResponse(int status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public static final Creator<AddLocalCapabilityResponse> CREATOR = new AutoCreator<AddLocalCapabilityResponse>(AddLocalCapabilityResponse.class);
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
import com.google.android.gms.wearable.AmsEntityUpdate;
|
||||
|
||||
import org.microg.safeparcel.AutoSafeParcelable;
|
||||
|
||||
public class AmsEntityUpdateParcelable extends AutoSafeParcelable implements AmsEntityUpdate {
|
||||
public static final Creator<AmsEntityUpdateParcelable> CREATOR = new AutoCreator<AmsEntityUpdateParcelable>(AmsEntityUpdateParcelable.class);
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
import com.google.android.gms.wearable.AncsNotification;
|
||||
|
||||
import org.microg.safeparcel.AutoSafeParcelable;
|
||||
|
||||
public class AncsNotificationParcelable extends AutoSafeParcelable implements AncsNotification {
|
||||
public static final Creator<AncsNotificationParcelable> CREATOR = new AutoCreator<AncsNotificationParcelable>(AncsNotificationParcelable.class);
|
||||
}
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
import com.google.android.gms.wearable.CapabilityInfo;
|
||||
import com.google.android.gms.wearable.Node;
|
||||
|
||||
import org.microg.safeparcel.AutoSafeParcelable;
|
||||
import org.microg.safeparcel.SafeParceled;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class CapabilityInfoParcelable extends AutoSafeParcelable implements CapabilityInfo {
|
||||
@SafeParceled(1)
|
||||
private int versionCode = 1;
|
||||
|
||||
@SafeParceled(2)
|
||||
private String name;
|
||||
|
||||
@SafeParceled(value = 3, subClass = NodeParcelable.class)
|
||||
private List<NodeParcelable> nodeParcelables;
|
||||
|
||||
private Set<Node> nodes;
|
||||
private Object lock = new Object();
|
||||
|
||||
private CapabilityInfoParcelable() {
|
||||
}
|
||||
|
||||
public CapabilityInfoParcelable(String name, List<NodeParcelable> nodeParcelables) {
|
||||
this.name = name;
|
||||
this.nodeParcelables = nodeParcelables;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Set<Node> getNodes() {
|
||||
if (nodes == null) {
|
||||
nodes = new HashSet<Node>(nodeParcelables);
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) return true;
|
||||
if (other == null || getClass() != other.getClass()) return false;
|
||||
|
||||
CapabilityInfoParcelable that = (CapabilityInfoParcelable) other;
|
||||
|
||||
if (versionCode != that.versionCode) return false;
|
||||
if (name != null ? !name.equals(that.name) : that.name != null) return false;
|
||||
return nodeParcelables != null ? nodeParcelables.equals(that.nodeParcelables) : that.nodeParcelables == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = versionCode;
|
||||
result = 31 * result + (name != null ? name.hashCode() : 0);
|
||||
result = 31 * result + (nodeParcelables != null ? nodeParcelables.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CapabilityInfo{" + name + ", " + nodeParcelables + "}";
|
||||
}
|
||||
|
||||
public static final Creator<CapabilityInfoParcelable> CREATOR = new AutoCreator<CapabilityInfoParcelable>(CapabilityInfoParcelable.class);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
import org.microg.safeparcel.AutoSafeParcelable;
|
||||
import org.microg.safeparcel.SafeParceled;
|
||||
|
||||
public class ChannelEventParcelable extends AutoSafeParcelable {
|
||||
|
||||
@SafeParceled(1)
|
||||
private int versionCode = 1;
|
||||
@SafeParceled(2)
|
||||
public ChannelParcelable channel;
|
||||
@SafeParceled(3)
|
||||
public int eventType;
|
||||
@SafeParceled(4)
|
||||
public int closeReason;
|
||||
@SafeParceled(5)
|
||||
public int appSpecificErrorCode;
|
||||
|
||||
public static final Creator<ChannelEventParcelable> CREATOR = new AutoCreator<ChannelEventParcelable>(ChannelEventParcelable.class);
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
import org.microg.safeparcel.AutoSafeParcelable;
|
||||
import org.microg.safeparcel.SafeParceled;
|
||||
|
||||
public class ChannelParcelable extends AutoSafeParcelable {
|
||||
@SafeParceled(1)
|
||||
private int versionCode = 1;
|
||||
@SafeParceled(2)
|
||||
public String token;
|
||||
@SafeParceled(3)
|
||||
public String nodeId;
|
||||
@SafeParceled(4)
|
||||
public String path;
|
||||
|
||||
private ChannelParcelable() {
|
||||
}
|
||||
|
||||
public ChannelParcelable(String token, String nodeId, String path) {
|
||||
this.token = token;
|
||||
this.nodeId = nodeId;
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public static final Creator<ChannelParcelable> CREATOR = new AutoCreator<ChannelParcelable>(ChannelParcelable.class);
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright (C) 2019 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
import org.microg.safeparcel.AutoSafeParcelable;
|
||||
import org.microg.safeparcel.SafeParceled;
|
||||
|
||||
public class ChannelReceiveFileResponse extends AutoSafeParcelable {
|
||||
@SafeParceled(1)
|
||||
private int versionCode = 1;
|
||||
public static final Creator<ChannelReceiveFileResponse> CREATOR = new AutoCreator<ChannelReceiveFileResponse>(ChannelReceiveFileResponse.class);
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright (C) 2019 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
import org.microg.safeparcel.AutoSafeParcelable;
|
||||
import org.microg.safeparcel.SafeParceled;
|
||||
|
||||
public class ChannelSendFileResponse extends AutoSafeParcelable {
|
||||
@SafeParceled(1)
|
||||
private int versionCode = 1;
|
||||
public static final Creator<ChannelSendFileResponse> CREATOR = new AutoCreator<ChannelSendFileResponse>(ChannelSendFileResponse.class);
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright (C) 2019 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
import org.microg.safeparcel.AutoSafeParcelable;
|
||||
import org.microg.safeparcel.SafeParceled;
|
||||
|
||||
public class CloseChannelResponse extends AutoSafeParcelable {
|
||||
@SafeParceled(1)
|
||||
private int versionCode = 1;
|
||||
public static final Creator<CloseChannelResponse> CREATOR = new AutoCreator<CloseChannelResponse>(CloseChannelResponse.class);
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
import com.google.android.gms.wearable.DataItemAsset;
|
||||
|
||||
import org.microg.safeparcel.AutoSafeParcelable;
|
||||
import org.microg.safeparcel.SafeParceled;
|
||||
|
||||
public class DataItemAssetParcelable extends AutoSafeParcelable implements DataItemAsset {
|
||||
|
||||
@SafeParceled(1)
|
||||
private int versionCode = 1;
|
||||
@SafeParceled(2)
|
||||
private String id;
|
||||
@SafeParceled(3)
|
||||
private String key;
|
||||
|
||||
private DataItemAssetParcelable() {
|
||||
}
|
||||
|
||||
public DataItemAssetParcelable(String id, String key) {
|
||||
this.id = id;
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDataItemKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataItemAsset freeze() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDataValid() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static final Creator<DataItemAssetParcelable> CREATOR = new AutoCreator<DataItemAssetParcelable>(DataItemAssetParcelable.class);
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.google.android.gms.wearable.DataItem;
|
||||
import com.google.android.gms.wearable.DataItemAsset;
|
||||
|
||||
import org.microg.safeparcel.AutoSafeParcelable;
|
||||
import org.microg.safeparcel.SafeParceled;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class DataItemParcelable extends AutoSafeParcelable implements DataItem {
|
||||
@SafeParceled(1)
|
||||
private int versionCode = 1;
|
||||
@SafeParceled(2)
|
||||
private Uri uri;
|
||||
@SafeParceled(4)
|
||||
private Bundle assets = new Bundle();
|
||||
@SafeParceled(5)
|
||||
public byte[] data;
|
||||
|
||||
private DataItemParcelable() {
|
||||
}
|
||||
|
||||
public DataItemParcelable(Uri uri) {
|
||||
this(uri, new HashMap<String, DataItemAssetParcelable>());
|
||||
}
|
||||
|
||||
public DataItemParcelable(Uri uri, Map<String, DataItemAssetParcelable> assets) {
|
||||
this.uri = uri;
|
||||
for (String key : assets.keySet()) {
|
||||
this.assets.putParcelable(key, assets.get(key));
|
||||
}
|
||||
data = null;
|
||||
}
|
||||
|
||||
public Map<String, DataItemAsset> getAssets() {
|
||||
Map<String, DataItemAsset> assets = new HashMap<String, DataItemAsset>();
|
||||
this.assets.setClassLoader(DataItemAssetParcelable.class.getClassLoader());
|
||||
for (String key : this.assets.keySet()) {
|
||||
assets.put(key, (DataItemAssetParcelable) this.assets.getParcelable(key));
|
||||
}
|
||||
return assets;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Uri getUri() {
|
||||
return uri;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataItem setData(byte[] data) {
|
||||
this.data = data;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataItem freeze() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDataValid() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static final Creator<DataItemParcelable> CREATOR = new AutoCreator<DataItemParcelable>(DataItemParcelable.class);
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
import org.microg.safeparcel.AutoSafeParcelable;
|
||||
import org.microg.safeparcel.SafeParceled;
|
||||
|
||||
public class DeleteDataItemsResponse extends AutoSafeParcelable {
|
||||
|
||||
@SafeParceled(1)
|
||||
private int versionCode = 1;
|
||||
@SafeParceled(2)
|
||||
private int status;
|
||||
@SafeParceled(3)
|
||||
private int count;
|
||||
|
||||
private DeleteDataItemsResponse() {
|
||||
}
|
||||
|
||||
public DeleteDataItemsResponse(int status, int count) {
|
||||
this.status = status;
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public static final Creator<DeleteDataItemsResponse> CREATOR = new AutoCreator<DeleteDataItemsResponse>(DeleteDataItemsResponse.class);
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright (C) 2019 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
import org.microg.safeparcel.AutoSafeParcelable;
|
||||
import org.microg.safeparcel.SafeParceled;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class GetAllCapabilitiesResponse extends AutoSafeParcelable {
|
||||
@Field(1)
|
||||
private int versionCode = 1;
|
||||
@Field(2)
|
||||
public int statusCode;
|
||||
@Field(3)
|
||||
public List<CapabilityInfoParcelable> capabilities;
|
||||
|
||||
public static final Creator<GetAllCapabilitiesResponse> CREATOR = findCreator(GetAllCapabilitiesResponse.class);
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright (C) 2019 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
import org.microg.safeparcel.AutoSafeParcelable;
|
||||
import org.microg.safeparcel.SafeParceled;
|
||||
|
||||
public class GetCapabilityResponse extends AutoSafeParcelable {
|
||||
@SafeParceled(1)
|
||||
private int versionCode = 1;
|
||||
@SafeParceled(2)
|
||||
private int status;
|
||||
@SafeParceled(3)
|
||||
private CapabilityInfoParcelable capabilityInfo;
|
||||
|
||||
private GetCapabilityResponse() {
|
||||
}
|
||||
|
||||
public GetCapabilityResponse(int status, CapabilityInfoParcelable capabilityInfo) {
|
||||
this.status = status;
|
||||
this.capabilityInfo = capabilityInfo;
|
||||
}
|
||||
|
||||
public static final Creator<GetCapabilityResponse> CREATOR = new AutoCreator<GetCapabilityResponse>(GetCapabilityResponse.class);
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright (C) 2019 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
import org.microg.safeparcel.AutoSafeParcelable;
|
||||
import org.microg.safeparcel.SafeParceled;
|
||||
|
||||
public class GetChannelInputStreamResponse extends AutoSafeParcelable {
|
||||
@SafeParceled(1)
|
||||
private int versionCode = 1;
|
||||
public static final Creator<GetChannelInputStreamResponse> CREATOR = new AutoCreator<GetChannelInputStreamResponse>(GetChannelInputStreamResponse.class);
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright (C) 2019 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
import org.microg.safeparcel.AutoSafeParcelable;
|
||||
import org.microg.safeparcel.SafeParceled;
|
||||
|
||||
public class GetChannelOutputStreamResponse extends AutoSafeParcelable {
|
||||
@SafeParceled(1)
|
||||
private int versionCode = 1;
|
||||
public static final Creator<GetChannelOutputStreamResponse> CREATOR = new AutoCreator<GetChannelOutputStreamResponse>(GetChannelOutputStreamResponse.class);
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright (C) 2019 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
import org.microg.safeparcel.AutoSafeParcelable;
|
||||
import org.microg.safeparcel.SafeParceled;
|
||||
|
||||
public class GetCloudSyncOptInOutDoneResponse extends AutoSafeParcelable {
|
||||
@SafeParceled(1)
|
||||
private int versionCode = 1;
|
||||
public static final Creator<GetCloudSyncOptInOutDoneResponse> CREATOR = new AutoCreator<GetCloudSyncOptInOutDoneResponse>(GetCloudSyncOptInOutDoneResponse.class);
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright (C) 2019 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
import org.microg.safeparcel.AutoSafeParcelable;
|
||||
import org.microg.safeparcel.SafeParceled;
|
||||
|
||||
public class GetCloudSyncOptInStatusResponse extends AutoSafeParcelable {
|
||||
@SafeParceled(1)
|
||||
private int versionCode = 1;
|
||||
public static final Creator<GetCloudSyncOptInStatusResponse> CREATOR = new AutoCreator<GetCloudSyncOptInStatusResponse>(GetCloudSyncOptInStatusResponse.class);
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
import org.microg.safeparcel.AutoSafeParcelable;
|
||||
import org.microg.safeparcel.SafeParceled;
|
||||
|
||||
public class GetCloudSyncSettingResponse extends AutoSafeParcelable {
|
||||
@SafeParceled(1)
|
||||
private int versionCode = 1;
|
||||
@SafeParceled(2)
|
||||
public int statusCode;
|
||||
@SafeParceled(3)
|
||||
public boolean cloudSyncEnabled;
|
||||
|
||||
private GetCloudSyncSettingResponse() {}
|
||||
|
||||
public GetCloudSyncSettingResponse(int statusCode, boolean cloudSyncEnabled) {
|
||||
this.statusCode = statusCode;
|
||||
this.cloudSyncEnabled = cloudSyncEnabled;
|
||||
}
|
||||
|
||||
public static final Creator<GetCloudSyncSettingResponse> CREATOR = new AutoCreator<GetCloudSyncSettingResponse>(GetCloudSyncSettingResponse.class);
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2017 microG Project Team
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.wearable.internal;
|
||||
|
||||
import com.google.android.gms.wearable.ConnectionConfiguration;
|
||||
|
||||
import org.microg.safeparcel.AutoSafeParcelable;
|
||||
import org.microg.safeparcel.SafeParceled;
|
||||
|
||||
public class GetConfigResponse extends AutoSafeParcelable {
|
||||
@SafeParceled(1)
|
||||
private int versionCode = 1;
|
||||
@SafeParceled(2)
|
||||
public final int statusCode;
|
||||
@SafeParceled(3)
|
||||
public final ConnectionConfiguration connectionConfiguration;
|
||||
|
||||
private GetConfigResponse() {
|
||||
statusCode = 0;
|
||||
connectionConfiguration = null;
|
||||
}
|
||||
|
||||
public GetConfigResponse(int statusCode, ConnectionConfiguration connectionConfiguration) {
|
||||
this.statusCode = statusCode;
|
||||
this.connectionConfiguration = connectionConfiguration;
|
||||
}
|
||||
|
||||
public static final Creator<GetConfigResponse> CREATOR = new AutoCreator<GetConfigResponse>(GetConfigResponse.class);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue