mirror of
https://github.com/TeamVanced/VancedMicroG
synced 2024-12-22 18:47:46 +01:00
Update Wearable
- Add support for deleting dataitems - Refactoring - Update wearable-lib
This commit is contained in:
parent
4ea10d9049
commit
db013606e0
2
extern/Wearable
vendored
2
extern/Wearable
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 5c24adaa3928de68167ce31c0fa5b9b1a3256677
|
Subproject commit 666ef2a8628b1c76701a2d5f138d4fd90751b5e9
|
@ -17,6 +17,7 @@
|
|||||||
package org.microg.gms.common;
|
package org.microg.gms.common;
|
||||||
|
|
||||||
import android.os.IInterface;
|
import android.os.IInterface;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import java.lang.reflect.InvocationHandler;
|
import java.lang.reflect.InvocationHandler;
|
||||||
@ -25,17 +26,22 @@ import java.lang.reflect.Method;
|
|||||||
import java.lang.reflect.Proxy;
|
import java.lang.reflect.Proxy;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
public class MultiListenerProxy<T extends IInterface> implements InvocationHandler {
|
public class MultiListenerProxy<T extends IInterface> implements InvocationHandler {
|
||||||
private static final String TAG = "GmsMultiListener";
|
private static final String TAG = "GmsMultiListener";
|
||||||
|
|
||||||
public static <T extends IInterface> T get(Class<T> tClass, final Collection<T> listeners) {
|
public static <T extends IInterface> T get(Class<T> tClass, final Collection<T> listeners) {
|
||||||
return (T) Proxy.newProxyInstance(tClass.getClassLoader(), new Class[]{tClass}, new MultiListenerProxy<T>(listeners));
|
return get(tClass, new CollectionListenerPool<T>(listeners));
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Collection<T> listeners;
|
public static <T extends IInterface> T get(Class<T> tClass, final ListenerPool<T> listenerPool) {
|
||||||
|
return (T) Proxy.newProxyInstance(tClass.getClassLoader(), new Class[]{tClass}, new MultiListenerProxy<T>(listenerPool));
|
||||||
|
}
|
||||||
|
|
||||||
private MultiListenerProxy(Collection<T> listeners) {
|
private final ListenerPool<T> listeners;
|
||||||
|
|
||||||
|
private MultiListenerProxy(ListenerPool<T> listeners) {
|
||||||
this.listeners = listeners;
|
this.listeners = listeners;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,14 +50,175 @@ public class MultiListenerProxy<T extends IInterface> implements InvocationHandl
|
|||||||
for (T listener : new HashSet<T>(listeners)) {
|
for (T listener : new HashSet<T>(listeners)) {
|
||||||
try {
|
try {
|
||||||
method.invoke(listener, args);
|
method.invoke(listener, args);
|
||||||
} catch (IllegalAccessException e) {
|
} catch (Exception e) {
|
||||||
Log.w(TAG, e);
|
Log.w(TAG, e);
|
||||||
listeners.remove(listener);
|
listeners.remove(listener);
|
||||||
} catch (InvocationTargetException e) {
|
|
||||||
Log.w(TAG, e.getTargetException());
|
|
||||||
listeners.remove(listener);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static abstract class ListenerPool<T> implements Collection<T> {
|
||||||
|
@Override
|
||||||
|
public boolean addAll(Collection<? extends T> collection) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean add(T object) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsAll(Collection<?> collection) {
|
||||||
|
for (Object o : collection) {
|
||||||
|
if (!contains(o)) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeAll(Collection<?> collection) {
|
||||||
|
boolean x = true;
|
||||||
|
for (Object o : collection) {
|
||||||
|
if (!remove(o)) x = false;
|
||||||
|
}
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean retainAll(Collection<?> collection) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Object[] toArray() {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public <T1> T1[] toArray(T1[] array) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class CollectionListenerPool<T> extends ListenerPool<T> {
|
||||||
|
|
||||||
|
private Collection<T> listeners;
|
||||||
|
|
||||||
|
public CollectionListenerPool(Collection<T> listeners) {
|
||||||
|
this.listeners = listeners;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
listeners.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(Object object) {
|
||||||
|
return listeners.contains(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return listeners.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Iterator<T> iterator() {
|
||||||
|
return listeners.iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean remove(Object object) {
|
||||||
|
return listeners.remove(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return listeners.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class MultiCollectionListenerPool<T> extends ListenerPool<T> {
|
||||||
|
private Collection<? extends Collection<T>> multiCol;
|
||||||
|
|
||||||
|
public MultiCollectionListenerPool(Collection<? extends Collection<T>> multiCol) {
|
||||||
|
this.multiCol = multiCol;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
for (Collection<T> ts : multiCol) {
|
||||||
|
ts.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(Object object) {
|
||||||
|
for (Collection<T> ts : multiCol) {
|
||||||
|
if (ts.contains(object)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
for (Collection<T> ts : multiCol) {
|
||||||
|
if (!ts.isEmpty()) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Iterator<T> iterator() {
|
||||||
|
final Iterator<? extends Collection<T>> interMed = multiCol.iterator();
|
||||||
|
return new Iterator<T>() {
|
||||||
|
private Iterator<T> med;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
while ((med == null || !med.hasNext()) && interMed.hasNext()) {
|
||||||
|
med = interMed.next().iterator();
|
||||||
|
}
|
||||||
|
return med != null && med.hasNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T next() {
|
||||||
|
while (med == null || !med.hasNext()) {
|
||||||
|
med = interMed.next().iterator();
|
||||||
|
}
|
||||||
|
return med.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove() {
|
||||||
|
med.remove();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean remove(Object object) {
|
||||||
|
for (Collection<T> ts : multiCol) {
|
||||||
|
if (ts.remove(object)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
int sum = 0;
|
||||||
|
for (Collection<T> ts : multiCol) {
|
||||||
|
sum += ts.size();
|
||||||
|
}
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2013-2016 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++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -58,7 +58,7 @@ public class MessageHandler extends ServerMessageListener {
|
|||||||
.networkId(networkId)
|
.networkId(networkId)
|
||||||
.peerAndroidId(androidId)
|
.peerAndroidId(androidId)
|
||||||
.unknown4(3)
|
.unknown4(3)
|
||||||
.unknown5(1)
|
.peerVersion(1)
|
||||||
.build());
|
.build());
|
||||||
this.wearable = wearable;
|
this.wearable = wearable;
|
||||||
this.thisNodeId = config.nodeId;
|
this.thisNodeId = config.nodeId;
|
||||||
@ -83,6 +83,12 @@ public class MessageHandler extends ServerMessageListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDisconnected() {
|
||||||
|
wearable.onDisconnectReceived(getConnection(), thisNodeId, getRemoteConnect());
|
||||||
|
super.onDisconnected();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSetAsset(SetAsset setAsset) {
|
public void onSetAsset(SetAsset setAsset) {
|
||||||
Log.d(TAG, "onSetAsset: " + setAsset);
|
Log.d(TAG, "onSetAsset: " + setAsset);
|
||||||
@ -132,8 +138,7 @@ public class MessageHandler extends ServerMessageListener {
|
|||||||
@Override
|
@Override
|
||||||
public void onRpcRequest(Request rpcRequest) {
|
public void onRpcRequest(Request rpcRequest) {
|
||||||
Log.d(TAG, "onRpcRequest: " + rpcRequest);
|
Log.d(TAG, "onRpcRequest: " + rpcRequest);
|
||||||
if (TextUtils.isEmpty(rpcRequest.targetNodeId)) {
|
if (TextUtils.isEmpty(rpcRequest.targetNodeId) || rpcRequest.targetNodeId.equals(thisNodeId)) {
|
||||||
// TODO: That's probably not how it should go!
|
|
||||||
MessageEventParcelable messageEvent = new MessageEventParcelable();
|
MessageEventParcelable messageEvent = new MessageEventParcelable();
|
||||||
messageEvent.data = rpcRequest.rawData != null ? rpcRequest.rawData.toByteArray() : null;
|
messageEvent.data = rpcRequest.rawData != null ? rpcRequest.rawData.toByteArray() : null;
|
||||||
messageEvent.path = rpcRequest.path;
|
messageEvent.path = rpcRequest.path;
|
||||||
|
@ -1,68 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2013-2015 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.util.Log;
|
|
||||||
|
|
||||||
import com.google.android.gms.wearable.ConnectionConfiguration;
|
|
||||||
|
|
||||||
import org.microg.wearable.SocketWearableConnection;
|
|
||||||
import org.microg.wearable.WearableConnection;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.ServerSocket;
|
|
||||||
import java.net.Socket;
|
|
||||||
|
|
||||||
class NetworkConnectionThread extends Thread {
|
|
||||||
private static final String TAG = "GmsWearNetConnThr";
|
|
||||||
private static final int WEAR_TCP_PORT = 5601;
|
|
||||||
|
|
||||||
private ConnectionConfiguration config;
|
|
||||||
private ServerSocket socket;
|
|
||||||
private MessageHandler handler;
|
|
||||||
private WearableConnection wearableConnection;
|
|
||||||
|
|
||||||
public NetworkConnectionThread(WearableImpl wearable, ConnectionConfiguration config) {
|
|
||||||
this.config = config;
|
|
||||||
this.handler = new MessageHandler(wearable, config);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void close() {
|
|
||||||
try {
|
|
||||||
socket.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public WearableConnection getWearableConnection() {
|
|
||||||
return wearableConnection;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
socket = new ServerSocket(WEAR_TCP_PORT);
|
|
||||||
Log.d(TAG, "Listening for connections on TCP :" + WEAR_TCP_PORT);
|
|
||||||
Socket accepted = socket.accept();
|
|
||||||
(wearableConnection = new SocketWearableConnection(accepted, handler)).run();
|
|
||||||
Log.d(TAG, "Connection terminated, me too");
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -25,6 +25,8 @@ import android.text.TextUtils;
|
|||||||
|
|
||||||
import com.google.android.gms.wearable.Asset;
|
import com.google.android.gms.wearable.Asset;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class NodeDatabaseHelper extends SQLiteOpenHelper {
|
public class NodeDatabaseHelper extends SQLiteOpenHelper {
|
||||||
@ -34,8 +36,11 @@ public class NodeDatabaseHelper extends SQLiteOpenHelper {
|
|||||||
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 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 static final int VERSION = 9;
|
||||||
|
|
||||||
|
private ClockworkNodePreferences clockworkNodePreferences;
|
||||||
|
|
||||||
public NodeDatabaseHelper(Context context) {
|
public NodeDatabaseHelper(Context context) {
|
||||||
super(context, DB_NAME, null, VERSION);
|
super(context, DB_NAME, null, VERSION);
|
||||||
|
clockworkNodePreferences = new ClockworkNodePreferences(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -198,27 +203,32 @@ public class NodeDatabaseHelper extends SQLiteOpenHelper {
|
|||||||
return getReadableDatabase().query("dataItemsAndAssets", GDIBHAP_FIELDS, selection, new String[]{nodeId, Long.toString(seqId)}, null, null, "seqId", null);
|
return getReadableDatabase().query("dataItemsAndAssets", GDIBHAP_FIELDS, selection, new String[]{nodeId, Long.toString(seqId)}, null, null, "seqId", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized int deleteDataItems(String packageName, String signatureDigest, String host, String path) {
|
public synchronized List<DataItemRecord> deleteDataItems(String packageName, String signatureDigest, String host, String path) {
|
||||||
|
List<DataItemRecord> updated = new ArrayList<DataItemRecord>();
|
||||||
SQLiteDatabase db = getWritableDatabase();
|
SQLiteDatabase db = getWritableDatabase();
|
||||||
db.beginTransaction();
|
db.beginTransaction();
|
||||||
Cursor cursor = getDataItemsByHostAndPath(db, packageName, signatureDigest, host, path);
|
Cursor cursor = getDataItemsByHostAndPath(db, packageName, signatureDigest, host, path);
|
||||||
int n = 0;
|
|
||||||
while (cursor.moveToNext()) {
|
while (cursor.moveToNext()) {
|
||||||
DataItemRecord record = DataItemRecord.fromCursor(cursor);
|
DataItemRecord record = DataItemRecord.fromCursor(cursor);
|
||||||
record.deleted = true;
|
record.deleted = true;
|
||||||
record.assetsAreReady = true;
|
record.assetsAreReady = true;
|
||||||
record.dataItem.data = null;
|
record.dataItem.data = null;
|
||||||
|
record.seqId = clockworkNodePreferences.getNextSeqId();
|
||||||
|
record.v1SeqId = record.seqId;
|
||||||
updateRecord(db, cursor.getString(0), record);
|
updateRecord(db, cursor.getString(0), record);
|
||||||
n++;
|
updated.add(record);
|
||||||
}
|
}
|
||||||
db.setTransactionSuccessful();
|
db.setTransactionSuccessful();
|
||||||
db.endTransaction();
|
db.endTransaction();
|
||||||
return n;
|
return updated;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getCurrentSeqId(String sourceNode) {
|
public long getCurrentSeqId(String sourceNode) {
|
||||||
if (TextUtils.isEmpty(sourceNode)) return 1;
|
if (TextUtils.isEmpty(sourceNode)) return 1;
|
||||||
SQLiteDatabase db = getReadableDatabase();
|
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");
|
Cursor cursor = db.query("dataItemsAndAssets", new String[]{"seqId"}, "sourceNode=?", new String[]{sourceNode}, null, null, "seqId DESC", "1");
|
||||||
long res = 1;
|
long res = 1;
|
||||||
if (cursor != null) {
|
if (cursor != null) {
|
||||||
|
@ -17,10 +17,10 @@
|
|||||||
package org.microg.gms.wearable;
|
package org.microg.gms.wearable;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Base64;
|
import android.util.Base64;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
@ -37,6 +37,7 @@ import com.google.android.gms.wearable.internal.PutDataRequest;
|
|||||||
import org.microg.gms.common.MultiListenerProxy;
|
import org.microg.gms.common.MultiListenerProxy;
|
||||||
import org.microg.gms.common.PackageUtils;
|
import org.microg.gms.common.PackageUtils;
|
||||||
import org.microg.gms.common.Utils;
|
import org.microg.gms.common.Utils;
|
||||||
|
import org.microg.wearable.SocketConnectionThread;
|
||||||
import org.microg.wearable.WearableConnection;
|
import org.microg.wearable.WearableConnection;
|
||||||
import org.microg.wearable.proto.AckAsset;
|
import org.microg.wearable.proto.AckAsset;
|
||||||
import org.microg.wearable.proto.AppKey;
|
import org.microg.wearable.proto.AppKey;
|
||||||
@ -54,12 +55,14 @@ import java.security.MessageDigest;
|
|||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
import okio.ByteString;
|
import okio.ByteString;
|
||||||
|
|
||||||
@ -67,48 +70,28 @@ public class WearableImpl {
|
|||||||
|
|
||||||
private static final String TAG = "GmsWear";
|
private static final String TAG = "GmsWear";
|
||||||
|
|
||||||
private static final String CLOCKWORK_NODE_PREFERENCES = "cw_node";
|
private static final int WEAR_TCP_PORT = 5601;
|
||||||
private static final String CLOCKWORK_NODE_PREFERENCE_NODE_ID = "node_id";
|
|
||||||
private static final String CLOCKWORK_NODE_PREFERENCE_NEXT_SEQ_ID_BLOCK = "nextSeqIdBlock";
|
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
private final NodeDatabaseHelper nodeDatabase;
|
private final NodeDatabaseHelper nodeDatabase;
|
||||||
private final ConfigurationDatabaseHelper configDatabase;
|
private final ConfigurationDatabaseHelper configDatabase;
|
||||||
private final Set<IWearableListener> listeners = new HashSet<IWearableListener>();
|
private final Map<String, List<IWearableListener>> listeners = new HashMap<String, List<IWearableListener>>();
|
||||||
private final Set<Node> connectedNodes = new HashSet<Node>();
|
private final Set<Node> connectedNodes = new HashSet<Node>();
|
||||||
private final Set<WearableConnection> activeConnections = new HashSet<WearableConnection>();
|
private final Set<WearableConnection> activeConnections = new HashSet<WearableConnection>();
|
||||||
private NetworkConnectionThread nct;
|
private SocketConnectionThread sct;
|
||||||
private ConnectionConfiguration[] configurations;
|
private ConnectionConfiguration[] configurations;
|
||||||
private boolean configurationsUpdated = false;
|
private boolean configurationsUpdated = false;
|
||||||
|
private ClockworkNodePreferences clockworkNodePreferences;
|
||||||
private long seqIdBlock;
|
|
||||||
private long seqIdInBlock = -1;
|
|
||||||
|
|
||||||
public WearableImpl(Context context, NodeDatabaseHelper nodeDatabase, ConfigurationDatabaseHelper configDatabase) {
|
public WearableImpl(Context context, NodeDatabaseHelper nodeDatabase, ConfigurationDatabaseHelper configDatabase) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.nodeDatabase = nodeDatabase;
|
this.nodeDatabase = nodeDatabase;
|
||||||
this.configDatabase = configDatabase;
|
this.configDatabase = configDatabase;
|
||||||
|
this.clockworkNodePreferences = new ClockworkNodePreferences(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getLocalNodeId() {
|
public String getLocalNodeId() {
|
||||||
SharedPreferences preferences = context.getSharedPreferences(CLOCKWORK_NODE_PREFERENCES, Context.MODE_PRIVATE);
|
return clockworkNodePreferences.getLocalNodeId();
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
private synchronized long getNextSeqId() {
|
|
||||||
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++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public DataItemRecord putDataItem(String packageName, String signatureDigest, String source, DataItemInternal dataItem) {
|
public DataItemRecord putDataItem(String packageName, String signatureDigest, String source, DataItemInternal dataItem) {
|
||||||
@ -118,7 +101,7 @@ public class WearableImpl {
|
|||||||
record.deleted = false;
|
record.deleted = false;
|
||||||
record.source = source;
|
record.source = source;
|
||||||
record.dataItem = dataItem;
|
record.dataItem = dataItem;
|
||||||
record.v1SeqId = getNextSeqId();
|
record.v1SeqId = clockworkNodePreferences.getNextSeqId();
|
||||||
if (record.source.equals(getLocalNodeId())) record.seqId = record.v1SeqId;
|
if (record.source.equals(getLocalNodeId())) record.seqId = record.v1SeqId;
|
||||||
nodeDatabase.putRecord(record);
|
nodeDatabase.putRecord(record);
|
||||||
return record;
|
return record;
|
||||||
@ -127,7 +110,11 @@ public class WearableImpl {
|
|||||||
public DataItemRecord putDataItem(DataItemRecord record) {
|
public DataItemRecord putDataItem(DataItemRecord record) {
|
||||||
nodeDatabase.putRecord(record);
|
nodeDatabase.putRecord(record);
|
||||||
try {
|
try {
|
||||||
getAllListeners().onDataChanged(getDataItemsByUri(record.dataItem.uri, record.packageName));
|
if (listeners.containsKey(record.packageName)) {
|
||||||
|
MultiListenerProxy.get(IWearableListener.class, listeners.get(record.packageName)).onDataChanged(getDataItemForRecord(record));
|
||||||
|
} else {
|
||||||
|
|
||||||
|
}
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
Log.w(TAG, e);
|
Log.w(TAG, e);
|
||||||
}
|
}
|
||||||
@ -272,7 +259,7 @@ public class WearableImpl {
|
|||||||
RootMessage announceMessage = new RootMessage.Builder().setAsset(new SetAsset.Builder()
|
RootMessage announceMessage = new RootMessage.Builder().setAsset(new SetAsset.Builder()
|
||||||
.digest(asset.getDigest())
|
.digest(asset.getDigest())
|
||||||
.appkeys(new AppKeys(Collections.singletonList(new AppKey(record.packageName, record.signatureDigest))))
|
.appkeys(new AppKeys(Collections.singletonList(new AppKey(record.packageName, record.signatureDigest))))
|
||||||
.build()).unknown13(true).build();
|
.build()).hasAsset(true).build();
|
||||||
connection.writeMessage(announceMessage);
|
connection.writeMessage(announceMessage);
|
||||||
File assetFile = createAssetFile(asset.getDigest());
|
File assetFile = createAssetFile(asset.getDigest());
|
||||||
String fileName = calculateDigest(announceMessage.toByteArray());
|
String fileName = calculateDigest(announceMessage.toByteArray());
|
||||||
@ -346,6 +333,16 @@ public class WearableImpl {
|
|||||||
onPeerConnected(new NodeParcelable(connect.id, connect.name));
|
onPeerConnected(new NodeParcelable(connect.id, connect.name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void onDisconnectReceived(WearableConnection connection, String nodeId, Connect connect) {
|
||||||
|
for (ConnectionConfiguration config : getConfigurations()) {
|
||||||
|
if (config.nodeId.equals(nodeId)) {
|
||||||
|
config.connected = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Log.d(TAG, "Removing connection from list of open connections: " + connection);
|
||||||
|
activeConnections.remove(connection);
|
||||||
|
onPeerDisconnected(new NodeParcelable(connect.id, connect.name));
|
||||||
|
}
|
||||||
|
|
||||||
public List<NodeParcelable> getConnectedNodesParcelableList() {
|
public List<NodeParcelable> getConnectedNodesParcelableList() {
|
||||||
List<NodeParcelable> nodes = new ArrayList<NodeParcelable>();
|
List<NodeParcelable> nodes = new ArrayList<NodeParcelable>();
|
||||||
@ -356,7 +353,7 @@ public class WearableImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public IWearableListener getAllListeners() {
|
public IWearableListener getAllListeners() {
|
||||||
return MultiListenerProxy.get(IWearableListener.class, listeners);
|
return MultiListenerProxy.get(IWearableListener.class, new MultiListenerProxy.MultiCollectionListenerPool<IWearableListener>(listeners.values()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onPeerConnected(NodeParcelable node) {
|
public void onPeerConnected(NodeParcelable node) {
|
||||||
@ -369,6 +366,16 @@ public class WearableImpl {
|
|||||||
addConnectedNode(node);
|
addConnectedNode(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void onPeerDisconnected(NodeParcelable node) {
|
||||||
|
Log.d(TAG, "onPeerDisconnected: " + node);
|
||||||
|
try {
|
||||||
|
getAllListeners().onPeerDisconnected(node);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
Log.w(TAG, e);
|
||||||
|
}
|
||||||
|
removeConnectedNode(node.getId());
|
||||||
|
}
|
||||||
|
|
||||||
public void onConnectedNodes(List<NodeParcelable> nodes) {
|
public void onConnectedNodes(List<NodeParcelable> nodes) {
|
||||||
Log.d(TAG, "onConnectedNodes: " + nodes);
|
Log.d(TAG, "onConnectedNodes: " + nodes);
|
||||||
try {
|
try {
|
||||||
@ -406,7 +413,13 @@ public class WearableImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public DataHolder getDataItemsByUri(Uri uri, String packageName) {
|
public DataHolder getDataItemsByUri(Uri uri, String packageName) {
|
||||||
Cursor dataHolderItems = nodeDatabase.getDataItemsForDataHolderByHostAndPath(packageName, PackageUtils.firstSignatureDigest(context, packageName), uri.getHost(), uri.getPath());
|
String firstSignature;
|
||||||
|
try {
|
||||||
|
firstSignature = PackageUtils.firstSignatureDigest(context, packageName);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Cursor dataHolderItems = nodeDatabase.getDataItemsForDataHolderByHostAndPath(packageName, firstSignature, uri.getHost(), uri.getPath());
|
||||||
while (dataHolderItems.moveToNext()) {
|
while (dataHolderItems.moveToNext()) {
|
||||||
Log.d(TAG, "getDataItems[]: path=" + Uri.parse(dataHolderItems.getString(1)).getPath());
|
Log.d(TAG, "getDataItems[]: path=" + Uri.parse(dataHolderItems.getString(1)).getPath());
|
||||||
}
|
}
|
||||||
@ -415,30 +428,45 @@ public class WearableImpl {
|
|||||||
return DataHolder.fromCursor(dataHolderItems, 0, null);
|
return DataHolder.fromCursor(dataHolderItems, 0, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addListener(IWearableListener listener) {
|
public DataHolder getDataItemForRecord(DataItemRecord record) {
|
||||||
listeners.add(listener);
|
Cursor dataHolderItems = nodeDatabase.getDataItemsForDataHolderByHostAndPath(record.packageName, record.signatureDigest, record.dataItem.uri.getHost(), record.dataItem.uri.getPath());
|
||||||
|
while (dataHolderItems.moveToNext()) {
|
||||||
|
Log.d(TAG, "getDataItems[]: path=" + Uri.parse(dataHolderItems.getString(1)).getPath());
|
||||||
|
}
|
||||||
|
dataHolderItems.moveToFirst();
|
||||||
|
dataHolderItems.moveToPrevious();
|
||||||
|
return DataHolder.fromCursor(dataHolderItems, 0, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void addListener(String packageName, IWearableListener listener) {
|
||||||
|
if (!listeners.containsKey(packageName)) {
|
||||||
|
listeners.put(packageName, new ArrayList<IWearableListener>());
|
||||||
|
}
|
||||||
|
listeners.get(packageName).add(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeListener(IWearableListener listener) {
|
public void removeListener(IWearableListener listener) {
|
||||||
listeners.add(listener);
|
for (List<IWearableListener> list : listeners.values()) {
|
||||||
|
list.remove(listener);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void enableConnection(String name) {
|
public void enableConnection(String name) {
|
||||||
configDatabase.setEnabledState(name, true);
|
configDatabase.setEnabledState(name, true);
|
||||||
configurationsUpdated = true;
|
configurationsUpdated = true;
|
||||||
if (name.equals("server") && nct == null) {
|
if (name.equals("server") && sct == null) {
|
||||||
(nct = new NetworkConnectionThread(this, configDatabase.getConfiguration(name))).start();
|
(sct = SocketConnectionThread.serverListen(WEAR_TCP_PORT, new MessageHandler(this, configDatabase.getConfiguration(name)))).start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void disableConnection(String name) {
|
public void disableConnection(String name) {
|
||||||
configDatabase.setEnabledState(name, false);
|
configDatabase.setEnabledState(name, false);
|
||||||
configurationsUpdated = true;
|
configurationsUpdated = true;
|
||||||
if (name.equals("server") && nct != null) {
|
if (name.equals("server") && sct != null) {
|
||||||
activeConnections.remove(nct.getWearableConnection());
|
activeConnections.remove(sct.getWearableConnection());
|
||||||
nct.close();
|
sct.close();
|
||||||
nct.interrupt();
|
sct.interrupt();
|
||||||
nct = null;
|
sct = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -455,7 +483,11 @@ public class WearableImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int deleteDataItems(Uri uri, String packageName) {
|
public int deleteDataItems(Uri uri, String packageName) {
|
||||||
return nodeDatabase.deleteDataItems(packageName, PackageUtils.firstSignatureDigest(context, packageName), uri.getHost(), uri.getPath());
|
List<DataItemRecord> records = nodeDatabase.deleteDataItems(packageName, PackageUtils.firstSignatureDigest(context, packageName), uri.getHost(), uri.getPath());
|
||||||
|
for (DataItemRecord record : records) {
|
||||||
|
syncRecordToAll(record);
|
||||||
|
}
|
||||||
|
return records.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendMessageReceived(MessageEventParcelable messageEvent) {
|
public void sendMessageReceived(MessageEventParcelable messageEvent) {
|
||||||
|
@ -20,6 +20,7 @@ import android.content.Context;
|
|||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
|
import android.support.v7.app.NotificationCompat;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.google.android.gms.common.api.Status;
|
import com.google.android.gms.common.api.Status;
|
||||||
@ -113,7 +114,7 @@ public class WearableServiceImpl extends IWearableService.Stub {
|
|||||||
public void addListener(IWearableCallbacks callbacks, AddListenerRequest request) throws RemoteException {
|
public void addListener(IWearableCallbacks callbacks, AddListenerRequest request) throws RemoteException {
|
||||||
Log.d(TAG, "addListener[nyp]: " + request);
|
Log.d(TAG, "addListener[nyp]: " + request);
|
||||||
if (request.listener != null) {
|
if (request.listener != null) {
|
||||||
wearable.addListener(request.listener);
|
wearable.addListener(packageName, request.listener);
|
||||||
}
|
}
|
||||||
callbacks.onStatus(Status.SUCCESS);
|
callbacks.onStatus(Status.SUCCESS);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user