mirror of
https://github.com/syncthing/syncthing-android.git
synced 2024-11-22 20:31:16 +00:00
Fix incorrect remote device syncing status UI (fixes #1062)
This commit is contained in:
parent
7e3c6c0b8f
commit
f13ed587d7
9 changed files with 291 additions and 108 deletions
|
@ -87,7 +87,7 @@ public class FolderListFragment extends ListFragment implements SyncthingService
|
|||
mAdapter.clear();
|
||||
List<Folder> folders = activity.getApi().getFolders();
|
||||
mAdapter.addAll(folders);
|
||||
mAdapter.updateModel(activity.getApi());
|
||||
mAdapter.updateFolderStatus(activity.getApi());
|
||||
mAdapter.notifyDataSetChanged();
|
||||
setListShown(true);
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ public class GetRequest extends ApiRequest {
|
|||
public static final String URI_VERSION = "/rest/system/version";
|
||||
public static final String URI_SYSTEM = "/rest/system/status";
|
||||
public static final String URI_CONNECTIONS = "/rest/system/connections";
|
||||
public static final String URI_MODEL = "/rest/db/status";
|
||||
public static final String URI_STATUS = "/rest/db/status";
|
||||
public static final String URI_DEVICEID = "/rest/svc/deviceid";
|
||||
public static final String URI_REPORT = "/rest/svc/report";
|
||||
public static final String URI_EVENTS = "/rest/events";
|
||||
|
|
|
@ -0,0 +1,141 @@
|
|||
package com.nutomic.syncthingandroid.model;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This class caches remote folder and device synchronization
|
||||
* completion indicators defined in {@link CompletionInfo#CompletionInfo}
|
||||
* according to syncthing's REST "/completion" JSON result schema.
|
||||
* Completion model of syncthing's web UI is completion[deviceId][folderId]
|
||||
*/
|
||||
public class Completion {
|
||||
|
||||
private static final String TAG = "Completion";
|
||||
|
||||
HashMap<String, HashMap<String, CompletionInfo>> deviceFolderMap =
|
||||
new HashMap<String, HashMap<String, CompletionInfo>>();
|
||||
|
||||
/**
|
||||
* Removes a folder from the cache model.
|
||||
*/
|
||||
private void removeFolder(String folderId) {
|
||||
for (HashMap<String, CompletionInfo> folderMap : deviceFolderMap.values()) {
|
||||
if (folderMap.containsKey(folderId)) {
|
||||
folderMap.remove(folderId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates device and folder information in the cache model
|
||||
* after a config update.
|
||||
*/
|
||||
public void updateFromConfig(List<Device> newDevices, List<Folder> newFolders) {
|
||||
HashMap<String, CompletionInfo> folderMap;
|
||||
|
||||
// Handle devices that were removed from the config.
|
||||
List<String> removedDevices = new ArrayList<>();;
|
||||
Boolean deviceFound;
|
||||
for (String deviceId : deviceFolderMap.keySet()) {
|
||||
deviceFound = false;
|
||||
for (Device device : newDevices) {
|
||||
if (device.deviceID.equals(deviceId)) {
|
||||
deviceFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!deviceFound) {
|
||||
removedDevices.add(deviceId);
|
||||
}
|
||||
}
|
||||
for (String deviceId : removedDevices) {
|
||||
Log.v(TAG, "updateFromConfig: Remove device '" + deviceId + "' from cache model");
|
||||
deviceFolderMap.remove(deviceId);
|
||||
}
|
||||
|
||||
// Handle devices that were added to the config.
|
||||
for (Device device : newDevices) {
|
||||
if (!deviceFolderMap.containsKey(device.deviceID)) {
|
||||
Log.v(TAG, "updateFromConfig: Add device '" + device.deviceID + "' to cache model");
|
||||
deviceFolderMap.put(device.deviceID, new HashMap<String, CompletionInfo>());
|
||||
}
|
||||
}
|
||||
|
||||
// Handle folders that were removed from the config.
|
||||
List<String> removedFolders = new ArrayList<>();;
|
||||
Boolean folderFound;
|
||||
for (Map.Entry<String, HashMap<String, CompletionInfo>> device : deviceFolderMap.entrySet()) {
|
||||
for (String folderId : device.getValue().keySet()) {
|
||||
folderFound = false;
|
||||
for (Folder folder : newFolders) {
|
||||
if (folder.id.equals(folderId)) {
|
||||
folderFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!folderFound) {
|
||||
removedFolders.add(folderId);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (String folderId : removedFolders) {
|
||||
Log.v(TAG, "updateFromConfig: Remove folder '" + folderId + "' from cache model");
|
||||
removeFolder(folderId);
|
||||
}
|
||||
|
||||
// Handle folders that were added to the config.
|
||||
for (Folder folder : newFolders) {
|
||||
for (Device device : newDevices) {
|
||||
if (folder.getDevice(device.deviceID) != null) {
|
||||
// folder is shared with device.
|
||||
folderMap = deviceFolderMap.get(device.deviceID);
|
||||
if (!folderMap.containsKey(folder.id)) {
|
||||
Log.v(TAG, "updateFromConfig: Add folder '" + folder.id +
|
||||
"' shared with device '" + device.deviceID + "' to cache model.");
|
||||
folderMap.put(folder.id, new CompletionInfo());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates remote device sync completion percentage across all folders
|
||||
* shared with the device.
|
||||
*/
|
||||
public int getDeviceCompletion(String deviceId) {
|
||||
int folderCount = 0;
|
||||
double sumCompletion = 0;
|
||||
HashMap<String, CompletionInfo> folderMap = deviceFolderMap.get(deviceId);
|
||||
if (folderMap != null) {
|
||||
for (Map.Entry<String, CompletionInfo> folder : folderMap.entrySet()) {
|
||||
sumCompletion += folder.getValue().completion;
|
||||
folderCount++;
|
||||
}
|
||||
}
|
||||
if (folderCount == 0) {
|
||||
return 100;
|
||||
} else {
|
||||
return (int) Math.floor(sumCompletion / folderCount);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set completionInfo within the completion[deviceId][folderId] model.
|
||||
*/
|
||||
public void setCompletionInfo(String deviceId, String folderId,
|
||||
CompletionInfo completionInfo) {
|
||||
// Add device parent node if it does not exist.
|
||||
if (!deviceFolderMap.containsKey(deviceId)) {
|
||||
deviceFolderMap.put(deviceId, new HashMap<String, CompletionInfo>());
|
||||
}
|
||||
// Add folder or update existing folder entry.
|
||||
deviceFolderMap.get(deviceId).put(folderId, completionInfo);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package com.nutomic.syncthingandroid.model;
|
||||
|
||||
/**
|
||||
* According to syncthing REST API
|
||||
* https://docs.syncthing.net/rest/db-completion-get.html
|
||||
*
|
||||
* completion is also returned by the events API
|
||||
* https://docs.syncthing.net/events/foldercompletion.html
|
||||
*
|
||||
*/
|
||||
public class CompletionInfo {
|
||||
public double completion = 100;
|
||||
|
||||
/**
|
||||
* The following values are only returned by the REST API call
|
||||
* to ""/completion". We will need them in the future to show
|
||||
* more statistics in the device UI.
|
||||
*/
|
||||
// public long globalBytes = 0;
|
||||
// public long needBytes = 0;
|
||||
// public long needDeletes = 0;
|
||||
// public long needItems = 0;
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
package com.nutomic.syncthingandroid.model;
|
||||
|
||||
public class Model {
|
||||
public class FolderStatus {
|
||||
public long globalBytes;
|
||||
public long globalDeleted;
|
||||
public long globalDirectories;
|
|
@ -13,10 +13,12 @@ import android.provider.MediaStore;
|
|||
import android.util.Log;
|
||||
|
||||
import com.annimon.stream.Stream;
|
||||
import com.nutomic.syncthingandroid.BuildConfig;
|
||||
import com.nutomic.syncthingandroid.R;
|
||||
import com.nutomic.syncthingandroid.SyncthingApp;
|
||||
import com.nutomic.syncthingandroid.activities.DeviceActivity;
|
||||
import com.nutomic.syncthingandroid.activities.FolderActivity;
|
||||
import com.nutomic.syncthingandroid.model.CompletionInfo;
|
||||
import com.nutomic.syncthingandroid.model.Device;
|
||||
import com.nutomic.syncthingandroid.model.Event;
|
||||
import com.nutomic.syncthingandroid.model.Folder;
|
||||
|
@ -93,6 +95,9 @@ public class EventProcessor implements SyncthingService.OnWebGuiAvailableListene
|
|||
*/
|
||||
@Override
|
||||
public void onEvent(Event event) {
|
||||
String deviceId;
|
||||
String folderId;
|
||||
|
||||
switch (event.type) {
|
||||
case "ConfigSaved":
|
||||
if (mApi != null) {
|
||||
|
@ -101,8 +106,8 @@ public class EventProcessor implements SyncthingService.OnWebGuiAvailableListene
|
|||
}
|
||||
break;
|
||||
case "DeviceRejected":
|
||||
String deviceId = (String) event.data.get("device");
|
||||
Log.d(TAG, "Unknwon device " + deviceId + " wants to connect");
|
||||
deviceId = (String) event.data.get("device");
|
||||
Log.d(TAG, "Unknown device " + deviceId + " wants to connect");
|
||||
|
||||
Intent intent = new Intent(mContext, DeviceActivity.class)
|
||||
.putExtra(DeviceActivity.EXTRA_IS_CREATE, true)
|
||||
|
@ -117,9 +122,16 @@ public class EventProcessor implements SyncthingService.OnWebGuiAvailableListene
|
|||
|
||||
notify(title, pi);
|
||||
break;
|
||||
case "FolderCompletion":
|
||||
deviceId = (String) event.data.get("device");
|
||||
folderId = (String) event.data.get("folder");
|
||||
CompletionInfo completionInfo = new CompletionInfo();
|
||||
completionInfo.completion = (Double) event.data.get("completion");
|
||||
mApi.setCompletionInfo(deviceId, folderId, completionInfo);
|
||||
break;
|
||||
case "FolderRejected":
|
||||
deviceId = (String) event.data.get("device");
|
||||
String folderId = (String) event.data.get("folder");
|
||||
folderId = (String) event.data.get("folder");
|
||||
String folderLabel = (String) event.data.get("folderLabel");
|
||||
Log.d(TAG, "Device " + deviceId + " wants to share folder " + folderId);
|
||||
|
||||
|
@ -170,6 +182,25 @@ public class EventProcessor implements SyncthingService.OnWebGuiAvailableListene
|
|||
case "Ping":
|
||||
// Ignored.
|
||||
break;
|
||||
case "DeviceConnected":
|
||||
case "DeviceDisconnected":
|
||||
case "DeviceDiscovered":
|
||||
case "DownloadProgress":
|
||||
case "FolderPaused":
|
||||
case "FolderScanProgress":
|
||||
case "FolderSummary":
|
||||
case "ItemStarted":
|
||||
case "LocalIndexUpdated":
|
||||
case "LoginAttempt":
|
||||
case "RemoteDownloadProgress":
|
||||
case "RemoteIndexUpdated":
|
||||
case "Starting":
|
||||
case "StartupComplete":
|
||||
case "StateChanged":
|
||||
if (BuildConfig.DEBUG) {
|
||||
Log.v(TAG, "Ignored event " + event.type + ", data " + event.data);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Log.v(TAG, "Unhandled event " + event.type);
|
||||
}
|
||||
|
|
|
@ -24,11 +24,13 @@ import com.nutomic.syncthingandroid.http.GetRequest;
|
|||
import com.nutomic.syncthingandroid.http.PostConfigRequest;
|
||||
import com.nutomic.syncthingandroid.http.PostScanRequest;
|
||||
import com.nutomic.syncthingandroid.model.Config;
|
||||
import com.nutomic.syncthingandroid.model.Completion;
|
||||
import com.nutomic.syncthingandroid.model.CompletionInfo;
|
||||
import com.nutomic.syncthingandroid.model.Connections;
|
||||
import com.nutomic.syncthingandroid.model.Device;
|
||||
import com.nutomic.syncthingandroid.model.Event;
|
||||
import com.nutomic.syncthingandroid.model.Folder;
|
||||
import com.nutomic.syncthingandroid.model.Model;
|
||||
import com.nutomic.syncthingandroid.model.FolderStatus;
|
||||
import com.nutomic.syncthingandroid.model.Options;
|
||||
import com.nutomic.syncthingandroid.model.SystemInfo;
|
||||
import com.nutomic.syncthingandroid.model.SystemVersion;
|
||||
|
@ -95,10 +97,14 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener {
|
|||
private long mPreviousConnectionTime = 0;
|
||||
|
||||
/**
|
||||
* Stores the latest result of {@link #getModel} for each folder, for calculating device
|
||||
* percentage in {@link #getConnections}.
|
||||
* Stores the latest result of {@link #getFolderStatus} for each folder
|
||||
*/
|
||||
private final HashMap<String, Model> mCachedModelInfo = new HashMap<>();
|
||||
private HashMap<String, FolderStatus> mCachedFolderStatuses = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Stores the latest result of device and folder completion events.
|
||||
*/
|
||||
private Completion mCompletion = new Completion();
|
||||
|
||||
@Inject NotificationHandler mNotificationHandler;
|
||||
|
||||
|
@ -143,11 +149,7 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener {
|
|||
tryIsAvailable();
|
||||
});
|
||||
new GetRequest(mContext, mUrl, GetRequest.URI_CONFIG, mApiKey, null, result -> {
|
||||
Log.v(TAG, "onWebGuiAvailable: " + result);
|
||||
mConfig = new Gson().fromJson(result, Config.class);
|
||||
if (mConfig == null) {
|
||||
throw new RuntimeException("config is null: " + result);
|
||||
}
|
||||
onReloadConfigComplete(result);
|
||||
tryIsAvailable();
|
||||
});
|
||||
getSystemInfo(info -> {
|
||||
|
@ -157,13 +159,18 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener {
|
|||
}
|
||||
|
||||
public void reloadConfig() {
|
||||
new GetRequest(mContext, mUrl, GetRequest.URI_CONFIG, mApiKey, null, result -> {
|
||||
Log.v(TAG, "reloadConfig: " + result);
|
||||
mConfig = new Gson().fromJson(result, Config.class);
|
||||
if (mConfig == null) {
|
||||
throw new RuntimeException("config is null: " + result);
|
||||
}
|
||||
});
|
||||
new GetRequest(mContext, mUrl, GetRequest.URI_CONFIG, mApiKey, null, this::onReloadConfigComplete);
|
||||
}
|
||||
|
||||
private void onReloadConfigComplete(String result) {
|
||||
Log.v(TAG, "onReloadConfigComplete: " + result);
|
||||
mConfig = new Gson().fromJson(result, Config.class);
|
||||
if (mConfig == null) {
|
||||
throw new RuntimeException("config is null: " + result);
|
||||
}
|
||||
|
||||
// Update cached device and folder information stored in the mCompletion model.
|
||||
mCompletion.updateFromConfig(getDevices(true), getFolders());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -241,6 +248,7 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener {
|
|||
|
||||
public void removeFolder(String id) {
|
||||
removeFolderInternal(id);
|
||||
// mCompletion will be updated after the ConfigSaved event.
|
||||
sendConfig();
|
||||
// Remove saved data from share activity for this folder.
|
||||
PreferenceManager.getDefaultSharedPreferences(mContext).edit()
|
||||
|
@ -300,6 +308,7 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener {
|
|||
|
||||
public void removeDevice(String deviceId) {
|
||||
removeDeviceInternal(deviceId);
|
||||
// mCompletion will be updated after the ConfigSaved event.
|
||||
sendConfig();
|
||||
}
|
||||
|
||||
|
@ -374,7 +383,7 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener {
|
|||
mPreviousConnectionTime = now;
|
||||
Connections connections = new Gson().fromJson(result, Connections.class);
|
||||
for (Map.Entry<String, Connections.Connection> e : connections.connections.entrySet()) {
|
||||
e.getValue().completion = getDeviceCompletion(e.getKey());
|
||||
e.getValue().completion = mCompletion.getDeviceCompletion(e.getKey());
|
||||
|
||||
Connections.Connection prev =
|
||||
(mPreviousConnections.isPresent() && mPreviousConnections.get().connections.containsKey(e.getKey()))
|
||||
|
@ -390,46 +399,14 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates completion percentage for the given device using {@link #mCachedModelInfo}.
|
||||
*/
|
||||
private int getDeviceCompletion(String deviceId) {
|
||||
int folderCount = 0;
|
||||
float percentageSum = 0;
|
||||
// Syncthing UI limits pending deletes to 95% completion of a device
|
||||
int maxPercentage = 100;
|
||||
for (Map.Entry<String, Model> modelInfo : mCachedModelInfo.entrySet()) {
|
||||
boolean isShared = false;
|
||||
for (Folder r : getFolders()) {
|
||||
if (r.getDevice(deviceId) != null) {
|
||||
isShared = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isShared) {
|
||||
long global = modelInfo.getValue().globalBytes;
|
||||
long local = modelInfo.getValue().inSyncBytes;
|
||||
if (modelInfo.getValue().needFiles == 0 && modelInfo.getValue().needDeletes > 0)
|
||||
maxPercentage = 95;
|
||||
percentageSum += (global != 0)
|
||||
? (local * 100f) / global
|
||||
: 100f;
|
||||
folderCount++;
|
||||
}
|
||||
}
|
||||
return (folderCount != 0)
|
||||
? Math.min(Math.round(percentageSum / folderCount), maxPercentage)
|
||||
: 100;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns status information about the folder with the given id.
|
||||
*/
|
||||
public void getModel(final String folderId, final OnResultListener2<String, Model> listener) {
|
||||
new GetRequest(mContext, mUrl, GetRequest.URI_MODEL, mApiKey,
|
||||
public void getFolderStatus(final String folderId, final OnResultListener2<String, FolderStatus> listener) {
|
||||
new GetRequest(mContext, mUrl, GetRequest.URI_STATUS, mApiKey,
|
||||
ImmutableMap.of("folder", folderId), result -> {
|
||||
Model m = new Gson().fromJson(result, Model.class);
|
||||
mCachedModelInfo.put(folderId, m);
|
||||
FolderStatus m = new Gson().fromJson(result, FolderStatus.class);
|
||||
mCachedFolderStatuses.put(folderId, m);
|
||||
listener.onResult(folderId, m);
|
||||
});
|
||||
}
|
||||
|
@ -494,6 +471,14 @@ public class RestApi implements SyncthingService.OnWebGuiAvailableListener {
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Updates cached folder and device completion info according to event data.
|
||||
*/
|
||||
public void setCompletionInfo(String deviceId, String folderId, CompletionInfo completionInfo) {
|
||||
mCompletion.setCompletionInfo(deviceId, folderId, completionInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns prettyfied usage report.
|
||||
*/
|
||||
|
|
|
@ -271,7 +271,7 @@ public class ConfigXml {
|
|||
}
|
||||
|
||||
/**
|
||||
* Set model name as device name for Syncthing.
|
||||
* Set device model name as device name for Syncthing.
|
||||
*
|
||||
* We need to iterate through XML nodes manually, as mConfig.getDocumentElement() will also
|
||||
* return nested elements inside folder element. We have to check that we only rename the
|
||||
|
@ -300,12 +300,12 @@ public class ConfigXml {
|
|||
private boolean changeDefaultFolder() {
|
||||
Element folder = (Element) mConfig.getDocumentElement()
|
||||
.getElementsByTagName("folder").item(0);
|
||||
String model = Build.MODEL
|
||||
String deviceModel = Build.MODEL
|
||||
.replace(" ", "_")
|
||||
.toLowerCase(Locale.US)
|
||||
.replaceAll("[^a-z0-9_-]", "");
|
||||
folder.setAttribute("label", mContext.getString(R.string.default_folder_label));
|
||||
folder.setAttribute("id", mContext.getString(R.string.default_folder_id, model));
|
||||
folder.setAttribute("id", mContext.getString(R.string.default_folder_id, deviceModel));
|
||||
folder.setAttribute("path", Environment
|
||||
.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getAbsolutePath());
|
||||
folder.setAttribute("type", "readonly");
|
||||
|
|
|
@ -15,11 +15,10 @@ import android.widget.ArrayAdapter;
|
|||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.nutomic.syncthingandroid.BuildConfig;
|
||||
import com.nutomic.syncthingandroid.R;
|
||||
import com.nutomic.syncthingandroid.databinding.ItemFolderListBinding;
|
||||
import com.nutomic.syncthingandroid.model.Folder;
|
||||
import com.nutomic.syncthingandroid.model.Model;
|
||||
import com.nutomic.syncthingandroid.model.FolderStatus;
|
||||
import com.nutomic.syncthingandroid.service.RestApi;
|
||||
import com.nutomic.syncthingandroid.util.Util;
|
||||
|
||||
|
@ -36,7 +35,7 @@ public class FoldersAdapter extends ArrayAdapter<Folder> {
|
|||
|
||||
private static final String TAG = "FoldersAdapter";
|
||||
|
||||
private final HashMap<String, Model> mModels = new HashMap<>();
|
||||
private final HashMap<String, FolderStatus> mLocalFolderStatuses = new HashMap<>();
|
||||
|
||||
public FoldersAdapter(Context context) {
|
||||
super(context, 0);
|
||||
|
@ -50,7 +49,6 @@ public class FoldersAdapter extends ArrayAdapter<Folder> {
|
|||
: DataBindingUtil.bind(convertView);
|
||||
|
||||
Folder folder = getItem(position);
|
||||
Model model = mModels.get(folder.id);
|
||||
binding.label.setText(TextUtils.isEmpty(folder.label) ? folder.id : folder.label);
|
||||
binding.directory.setText(folder.path);
|
||||
binding.openFolder.setOnClickListener(v -> {
|
||||
|
@ -73,48 +71,53 @@ public class FoldersAdapter extends ArrayAdapter<Folder> {
|
|||
}
|
||||
});
|
||||
|
||||
if (model != null) {
|
||||
int percentage = (model.globalBytes != 0)
|
||||
? Math.round(100 * model.inSyncBytes / model.globalBytes)
|
||||
: 100;
|
||||
long neededItems = model.needFiles + model.needDirectories + model.needSymlinks + model.needDeletes;
|
||||
if (model.state.equals("idle") && neededItems > 0) {
|
||||
binding.state.setText(getContext().getString(R.string.status_outofsync));
|
||||
binding.state.setTextColor(ContextCompat.getColor(getContext(), R.color.text_red));
|
||||
} else {
|
||||
if (folder.paused) {
|
||||
binding.state.setText(getContext().getString(R.string.state_paused));
|
||||
binding.state.setTextColor(ContextCompat.getColor(getContext(), R.color.text_black));
|
||||
} else {
|
||||
binding.state.setText(getLocalizedState(getContext(), model.state, percentage));
|
||||
switch(model.state) {
|
||||
case "idle":
|
||||
binding.state.setTextColor(ContextCompat.getColor(getContext(), R.color.text_green));
|
||||
break;
|
||||
case "scanning":
|
||||
case "syncing":
|
||||
binding.state.setTextColor(ContextCompat.getColor(getContext(), R.color.text_blue));
|
||||
break;
|
||||
default:
|
||||
binding.state.setTextColor(ContextCompat.getColor(getContext(), R.color.text_red));
|
||||
}
|
||||
}
|
||||
}
|
||||
binding.items.setVisibility(VISIBLE);
|
||||
binding.items.setText(getContext().getResources()
|
||||
.getQuantityString(R.plurals.files, (int) model.inSyncFiles, model.inSyncFiles, model.globalFiles));
|
||||
binding.size.setVisibility(VISIBLE);
|
||||
binding.size.setText(getContext().getString(R.string.folder_size_format,
|
||||
Util.readableFileSize(getContext(), model.inSyncBytes),
|
||||
Util.readableFileSize(getContext(), model.globalBytes)));
|
||||
setTextOrHide(binding.invalid, model.invalid);
|
||||
} else {
|
||||
updateFolderStatusView(binding, folder);
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
private void updateFolderStatusView(ItemFolderListBinding binding, Folder folder) {
|
||||
FolderStatus folderStatus = mLocalFolderStatuses.get(folder.id);
|
||||
if (folderStatus == null) {
|
||||
binding.items.setVisibility(GONE);
|
||||
binding.size.setVisibility(GONE);
|
||||
setTextOrHide(binding.invalid, folder.invalid);
|
||||
return;
|
||||
}
|
||||
|
||||
return binding.getRoot();
|
||||
int percentage = (folderStatus.globalBytes != 0)
|
||||
? Math.round(100 * folderStatus.inSyncBytes / folderStatus.globalBytes)
|
||||
: 100;
|
||||
long neededItems = folderStatus.needFiles + folderStatus.needDirectories + folderStatus.needSymlinks + folderStatus.needDeletes;
|
||||
if (folderStatus.state.equals("idle") && neededItems > 0) {
|
||||
binding.state.setText(getContext().getString(R.string.status_outofsync));
|
||||
binding.state.setTextColor(ContextCompat.getColor(getContext(), R.color.text_red));
|
||||
} else {
|
||||
if (folder.paused) {
|
||||
binding.state.setText(getContext().getString(R.string.state_paused));
|
||||
binding.state.setTextColor(ContextCompat.getColor(getContext(), R.color.text_black));
|
||||
} else {
|
||||
binding.state.setText(getLocalizedState(getContext(), folderStatus.state, percentage));
|
||||
switch(folderStatus.state) {
|
||||
case "idle":
|
||||
binding.state.setTextColor(ContextCompat.getColor(getContext(), R.color.text_green));
|
||||
break;
|
||||
case "scanning":
|
||||
case "syncing":
|
||||
binding.state.setTextColor(ContextCompat.getColor(getContext(), R.color.text_blue));
|
||||
break;
|
||||
default:
|
||||
binding.state.setTextColor(ContextCompat.getColor(getContext(), R.color.text_red));
|
||||
}
|
||||
}
|
||||
}
|
||||
binding.items.setVisibility(VISIBLE);
|
||||
binding.items.setText(getContext().getResources()
|
||||
.getQuantityString(R.plurals.files, (int) folderStatus.inSyncFiles, folderStatus.inSyncFiles, folderStatus.globalFiles));
|
||||
binding.size.setVisibility(VISIBLE);
|
||||
binding.size.setText(getContext().getString(R.string.folder_size_format,
|
||||
Util.readableFileSize(getContext(), folderStatus.inSyncBytes),
|
||||
Util.readableFileSize(getContext(), folderStatus.globalBytes)));
|
||||
setTextOrHide(binding.invalid, folderStatus.invalid);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -132,16 +135,16 @@ public class FoldersAdapter extends ArrayAdapter<Folder> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Requests updated model info from the api for all visible items.
|
||||
* Requests updated folder status from the api for all visible items.
|
||||
*/
|
||||
public void updateModel(RestApi api) {
|
||||
public void updateFolderStatus(RestApi api) {
|
||||
for (int i = 0; i < getCount(); i++) {
|
||||
api.getModel(getItem(i).id, this::onReceiveModel);
|
||||
api.getFolderStatus(getItem(i).id, this::onReceiveFolderStatus);
|
||||
}
|
||||
}
|
||||
|
||||
private void onReceiveModel(String folderId, Model model) {
|
||||
mModels.put(folderId, model);
|
||||
private void onReceiveFolderStatus(String folderId, FolderStatus folderStatus) {
|
||||
mLocalFolderStatuses.put(folderId, folderStatus);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue