1
0
Fork 0
mirror of https://github.com/syncthing/syncthing-android.git synced 2024-11-26 22:31:16 +00:00

Use Gson for connections

This commit is contained in:
Felix Ableitner 2016-11-17 20:01:51 +09:00
parent a201cd8789
commit dcdb6e1129
7 changed files with 78 additions and 102 deletions

View file

@ -1,10 +1,8 @@
package com.nutomic.syncthingandroid.test; package com.nutomic.syncthingandroid.test;
import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.support.annotation.NonNull;
import com.nutomic.syncthingandroid.model.Connection; import com.nutomic.syncthingandroid.model.Connections;
import com.nutomic.syncthingandroid.model.Device; import com.nutomic.syncthingandroid.model.Device;
import com.nutomic.syncthingandroid.model.Folder; import com.nutomic.syncthingandroid.model.Folder;
import com.nutomic.syncthingandroid.model.Model; import com.nutomic.syncthingandroid.model.Model;
@ -12,7 +10,6 @@ import com.nutomic.syncthingandroid.model.SystemInfo;
import com.nutomic.syncthingandroid.service.RestApi; import com.nutomic.syncthingandroid.service.RestApi;
import java.net.URL; import java.net.URL;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -54,7 +51,7 @@ public class MockRestApi extends RestApi {
} }
@Override @Override
public void getConnections(OnResultListener1<Map<String, Connection>> listener) { public void getConnections(OnResultListener1<Map<String, Connections>> listener) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }

View file

@ -12,7 +12,9 @@ import android.text.Editable;
import android.text.TextUtils; import android.text.TextUtils;
import android.text.TextWatcher; import android.text.TextWatcher;
import android.util.Log; import android.util.Log;
import android.view.*; import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.CompoundButton; import android.widget.CompoundButton;
import android.widget.EditText; import android.widget.EditText;
import android.widget.TextView; import android.widget.TextView;
@ -22,7 +24,7 @@ import com.google.gson.Gson;
import com.google.zxing.integration.android.IntentIntegrator; import com.google.zxing.integration.android.IntentIntegrator;
import com.google.zxing.integration.android.IntentResult; import com.google.zxing.integration.android.IntentResult;
import com.nutomic.syncthingandroid.R; import com.nutomic.syncthingandroid.R;
import com.nutomic.syncthingandroid.model.Connection; import com.nutomic.syncthingandroid.model.Connections;
import com.nutomic.syncthingandroid.model.Device; import com.nutomic.syncthingandroid.model.Device;
import com.nutomic.syncthingandroid.service.SyncthingService; import com.nutomic.syncthingandroid.service.SyncthingService;
import com.nutomic.syncthingandroid.util.Compression; import com.nutomic.syncthingandroid.util.Compression;
@ -32,7 +34,6 @@ import com.nutomic.syncthingandroid.util.Util;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map;
import static android.text.TextUtils.isEmpty; import static android.text.TextUtils.isEmpty;
import static android.view.View.GONE; import static android.view.View.GONE;
@ -218,13 +219,13 @@ public class DeviceActivity extends SyncthingActivity implements View.OnClickLis
* NOTE: This is only called once on startup, should be called more often to properly display * NOTE: This is only called once on startup, should be called more often to properly display
* version/address changes. * version/address changes.
*/ */
public void onReceiveConnections(Map<String, Connection> connections) { public void onReceiveConnections(Connections connections) {
boolean viewsExist = mSyncthingVersionView != null && mCurrentAddressView != null; boolean viewsExist = mSyncthingVersionView != null && mCurrentAddressView != null;
if (viewsExist && connections.containsKey(mDevice.deviceID)) { if (viewsExist && connections.connections.containsKey(mDevice.deviceID)) {
mCurrentAddressView.setVisibility(VISIBLE); mCurrentAddressView.setVisibility(VISIBLE);
mSyncthingVersionView.setVisibility(VISIBLE); mSyncthingVersionView.setVisibility(VISIBLE);
mCurrentAddressView.setText(connections.get(mDevice.deviceID).address); mCurrentAddressView.setText(connections.connections.get(mDevice.deviceID).address);
mSyncthingVersionView.setText(connections.get(mDevice.deviceID).clientVersion); mSyncthingVersionView.setText(connections.connections.get(mDevice.deviceID).clientVersion);
} }
} }

View file

@ -15,7 +15,7 @@ import com.nutomic.syncthingandroid.R;
import com.nutomic.syncthingandroid.activities.MainActivity; import com.nutomic.syncthingandroid.activities.MainActivity;
import com.nutomic.syncthingandroid.activities.SettingsActivity; import com.nutomic.syncthingandroid.activities.SettingsActivity;
import com.nutomic.syncthingandroid.activities.WebGuiActivity; import com.nutomic.syncthingandroid.activities.WebGuiActivity;
import com.nutomic.syncthingandroid.model.Connection; import com.nutomic.syncthingandroid.model.Connections;
import com.nutomic.syncthingandroid.model.SystemInfo; import com.nutomic.syncthingandroid.model.SystemInfo;
import com.nutomic.syncthingandroid.model.SystemVersion; import com.nutomic.syncthingandroid.model.SystemVersion;
import com.nutomic.syncthingandroid.service.RestApi; import com.nutomic.syncthingandroid.service.RestApi;
@ -187,8 +187,8 @@ public class DrawerFragment extends Fragment implements View.OnClickListener {
/** /**
* Populates views with status received via {@link RestApi#getConnections}. * Populates views with status received via {@link RestApi#getConnections}.
*/ */
private void onReceiveConnections(Map<String, Connection> connections) { private void onReceiveConnections(Connections connections) {
Connection c = connections.get(RestApi.TOTAL_STATS); Connections.Connection c = connections.total;
mDownload.setText(Util.readableTransferRate(mActivity, c.inBits)); mDownload.setText(Util.readableTransferRate(mActivity, c.inBits));
mUpload.setText(Util.readableTransferRate(mActivity, c.outBits)); mUpload.setText(Util.readableTransferRate(mActivity, c.outBits));
} }

View file

@ -1,13 +0,0 @@
package com.nutomic.syncthingandroid.model;
public class Connection {
public String at;
public long inBytesTotal;
public long outBytesTotal;
public long inBits;
public long outBits;
public String address;
public String clientVersion;
public int completion;
public boolean connected;
}

View file

@ -0,0 +1,34 @@
package com.nutomic.syncthingandroid.model;
import java.util.Map;
public class Connections {
public Connection total;
public Map<String, Connection> connections;
public static class Connection {
public boolean paused;
public String clientVersion;
public String at;
public boolean connected;
public long inBytesTotal;
public long outBytesTotal;
public String type;
public String address;
// These fields are not sent from Syncthing, but are populated on the client side.
public int completion;
public long inBits;
public long outBits;
public void setTransferRate(Connection previous, long msElapsed) {
long secondsElapsed = msElapsed / 1000;
long inBytes = 8 * (inBytesTotal - previous.inBytesTotal) / secondsElapsed;
long outBytes = 8 * (outBytesTotal - previous.outBytesTotal) / secondsElapsed;
inBits = Math.max(0, inBytes);
outBits = Math.max(0, outBytes);
}
}
}

View file

@ -7,6 +7,7 @@ import android.content.Intent;
import android.util.Log; import android.util.Log;
import com.google.common.base.Objects; import com.google.common.base.Objects;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.reflect.TypeToken; import com.google.common.reflect.TypeToken;
import com.google.gson.Gson; import com.google.gson.Gson;
@ -21,7 +22,7 @@ import com.nutomic.syncthingandroid.http.GetTask;
import com.nutomic.syncthingandroid.http.PostConfigTask; import com.nutomic.syncthingandroid.http.PostConfigTask;
import com.nutomic.syncthingandroid.http.PostScanTask; import com.nutomic.syncthingandroid.http.PostScanTask;
import com.nutomic.syncthingandroid.model.Config; import com.nutomic.syncthingandroid.model.Config;
import com.nutomic.syncthingandroid.model.Connection; import com.nutomic.syncthingandroid.model.Connections;
import com.nutomic.syncthingandroid.model.Device; import com.nutomic.syncthingandroid.model.Device;
import com.nutomic.syncthingandroid.model.Event; import com.nutomic.syncthingandroid.model.Event;
import com.nutomic.syncthingandroid.model.Folder; import com.nutomic.syncthingandroid.model.Folder;
@ -31,10 +32,6 @@ import com.nutomic.syncthingandroid.model.SystemInfo;
import com.nutomic.syncthingandroid.model.SystemVersion; import com.nutomic.syncthingandroid.model.SystemVersion;
import com.nutomic.syncthingandroid.util.FolderObserver; import com.nutomic.syncthingandroid.util.FolderObserver;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.net.URL; import java.net.URL;
import java.util.HashMap; import java.util.HashMap;
@ -51,12 +48,6 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener,
private static final String TAG = "RestApi"; private static final String TAG = "RestApi";
/**
* Key of the map element containing connection info for the local device, in the return
* value of {@link #getConnections}
*/
public static final String TOTAL_STATS = "total";
public interface OnConfigChangedListener { public interface OnConfigChangedListener {
void onConfigChanged(); void onConfigChanged();
} }
@ -83,7 +74,7 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener,
* Stores the result of the last successful request to {@link GetTask#URI_CONNECTIONS}, * Stores the result of the last successful request to {@link GetTask#URI_CONNECTIONS},
* or an empty Map. * or an empty Map.
*/ */
private Map<String, Connection> mPreviousConnections = new HashMap<>(); private Optional<Connections> mPreviousConnections = Optional.absent();
/** /**
* Stores the timestamp of the last successful request to {@link GetTask#URI_CONNECTIONS}. * Stores the timestamp of the last successful request to {@link GetTask#URI_CONNECTIONS}.
@ -342,62 +333,31 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener,
/** /**
* Returns connection info for the local device and all connected devices. * Returns connection info for the local device and all connected devices.
* <p/>
* Use the key {@link #TOTAL_STATS} to get connection info for the local device.
*
* The result is cached internally. Do not modify it or any of its contents.
*/ */
public void getConnections(final OnResultListener1<Map<String, Connection>> listener) { public void getConnections(final OnResultListener1<Connections> listener) {
new GetTask(mUrl, GetTask.URI_CONNECTIONS, mHttpsCertPath, mApiKey, null, result -> { new GetTask(mUrl, GetTask.URI_CONNECTIONS, mHttpsCertPath, mApiKey, null, result -> {
Long now = System.currentTimeMillis(); Long now = System.currentTimeMillis();
Long timeElapsed = (now - mPreviousConnectionTime) / 1000; Long msElapsed = now - mPreviousConnectionTime;
if (timeElapsed < 1) { if (msElapsed < SyncthingService.GUI_UPDATE_INTERVAL) {
listener.onResult(mPreviousConnections); listener.onResult(deepCopy(mPreviousConnections.get(), Connections.class));
return; return;
} }
try { mPreviousConnectionTime = now;
JSONObject json = new JSONObject(result); Connections connections = new Gson().fromJson(result, Connections.class);
Map<String, JSONObject> jsonConnections = new HashMap<>(); for (Map.Entry<String, Connections.Connection> e : connections.connections.entrySet()) {
jsonConnections.put(TOTAL_STATS, json.getJSONObject(TOTAL_STATS)); e.getValue().completion = getDeviceCompletion(e.getKey());
JSONArray extConnections = json.getJSONObject("connections").names();
if (extConnections != null) {
for (int i = 0; i < extConnections.length(); i++) {
String deviceId = extConnections.get(i).toString();
jsonConnections.put(deviceId, json.getJSONObject("connections").getJSONObject(deviceId));
}
}
Map<String, Connection> connections = new HashMap<>();
for (Map.Entry<String, JSONObject> jsonConnection : jsonConnections.entrySet()) {
String deviceId = jsonConnection.getKey();
Connection c = new Connection();
JSONObject conn = jsonConnection.getValue();
c.address = deviceId;
c.at = conn.getString("at");
c.inBytesTotal = conn.getLong("inBytesTotal");
c.outBytesTotal = conn.getLong("outBytesTotal");
c.address = conn.getString("address");
c.clientVersion = conn.getString("clientVersion");
c.completion = getDeviceCompletion(deviceId);
c.connected = conn.getBoolean("connected");
Connection prev = (mPreviousConnections.containsKey(deviceId)) Connections.Connection prev = mPreviousConnections
? mPreviousConnections.get(deviceId) .transform(c -> c.connections.get(e.getKey()))
: new Connection(); .or(new Connections.Connection());
mPreviousConnectionTime = now; e.getValue().setTransferRate(prev, msElapsed);
c.inBits = Math.max(0, 8 *
(conn.getLong("inBytesTotal") - prev.inBytesTotal) / timeElapsed);
c.outBits = Math.max(0, 8 *
(conn.getLong("outBytesTotal") - prev.outBytesTotal) / timeElapsed);
connections.put(deviceId, c);
}
mPreviousConnections = connections;
listener.onResult(mPreviousConnections);
} catch (JSONException e) {
Log.w(TAG, "Failed to parse connections", e);
} }
Connections.Connection prev =
mPreviousConnections.transform(c -> c.total).or(new Connections.Connection());
connections.total.setTransferRate(prev, msElapsed);
mPreviousConnections = Optional.of(connections);
listener.onResult(deepCopy(connections, Connections.class));
}).execute(); }).execute();
} }

View file

@ -10,22 +10,19 @@ import android.view.ViewGroup;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
import android.widget.TextView; import android.widget.TextView;
import com.google.common.base.Optional;
import com.nutomic.syncthingandroid.R; import com.nutomic.syncthingandroid.R;
import com.nutomic.syncthingandroid.model.Connection; import com.nutomic.syncthingandroid.model.Connections;
import com.nutomic.syncthingandroid.model.Device; import com.nutomic.syncthingandroid.model.Device;
import com.nutomic.syncthingandroid.service.RestApi; import com.nutomic.syncthingandroid.service.RestApi;
import com.nutomic.syncthingandroid.util.Util; import com.nutomic.syncthingandroid.util.Util;
import java.util.HashMap;
import java.util.Map;
/** /**
* Generates item views for device items. * Generates item views for device items.
*/ */
public class DevicesAdapter extends ArrayAdapter<Device> { public class DevicesAdapter extends ArrayAdapter<Device> {
private Map<String, Connection> mConnections = private Optional<Connections> mConnections = Optional.absent();
new HashMap<>();
public DevicesAdapter(Context context) { public DevicesAdapter(Context context) {
super(context, R.layout.item_device_list); super(context, R.layout.item_device_list);
@ -46,21 +43,21 @@ public class DevicesAdapter extends ArrayAdapter<Device> {
TextView upload = (TextView) convertView.findViewById(R.id.upload); TextView upload = (TextView) convertView.findViewById(R.id.upload);
String deviceId = getItem(position).deviceID; String deviceId = getItem(position).deviceID;
Connection conn = mConnections.get(deviceId); Optional<Connections.Connection> conn = mConnections.transform(a -> a.connections.get(deviceId));
name.setText(getItem(position).getDisplayName()); name.setText(getItem(position).getDisplayName());
Resources r = getContext().getResources(); Resources r = getContext().getResources();
if (conn != null && conn.connected) { if (conn.isPresent() && conn.get().connected) {
if (conn.completion == 100) { if (conn.get().completion == 100) {
status.setText(r.getString(R.string.device_up_to_date)); status.setText(r.getString(R.string.device_up_to_date));
status.setTextColor(ContextCompat.getColor(getContext(), R.color.text_green)); status.setTextColor(ContextCompat.getColor(getContext(), R.color.text_green));
} }
else { else {
status.setText(r.getString(R.string.device_syncing, conn.completion)); status.setText(r.getString(R.string.device_syncing, conn.get().completion));
status.setTextColor(ContextCompat.getColor(getContext(), R.color.text_blue)); status.setTextColor(ContextCompat.getColor(getContext(), R.color.text_blue));
} }
download.setText(Util.readableTransferRate(getContext(), conn.inBits)); download.setText(Util.readableTransferRate(getContext(), conn.get().inBits));
upload.setText(Util.readableTransferRate(getContext(), conn.outBits)); upload.setText(Util.readableTransferRate(getContext(), conn.get().outBits));
} }
else { else {
download.setText(Util.readableTransferRate(getContext(), 0)); download.setText(Util.readableTransferRate(getContext(), 0));
@ -81,8 +78,8 @@ public class DevicesAdapter extends ArrayAdapter<Device> {
} }
} }
public void onReceiveConnections(Map<String, Connection> connections) { public void onReceiveConnections(Connections connections) {
mConnections = connections; mConnections = Optional.of(connections);
notifyDataSetChanged(); notifyDataSetChanged();
} }
} }