build: Remove deprecated dataBinding = true (#1553). migrate to KSP

This commit is contained in:
Catfriend1 2025-07-31 12:34:44 +02:00 committed by GitHub
parent fa591388ed
commit cf4a2a725e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 417 additions and 365 deletions

View File

@ -3,7 +3,7 @@ plugins {
alias(libs.plugins.aboutLibraries)
alias(libs.plugins.compose.compiler)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.kapt)
alias(libs.plugins.ksp)
}
dependencies {
@ -35,7 +35,7 @@ dependencies {
implementation(libs.volley)
implementation(libs.zxing.android.embedded) { isTransitive = false }
implementation(libs.zxing.core)
kapt(libs.dagger.compiler)
ksp(libs.dagger.compiler)
}
android {
@ -47,7 +47,6 @@ android {
buildFeatures {
compose = true
dataBinding = true
}
defaultConfig {

View File

@ -15,11 +15,9 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.databinding.DataBindingUtil;
import androidx.preference.PreferenceManager;
import com.nutomic.syncthingandroid.R;
import com.nutomic.syncthingandroid.databinding.ItemDeviceListBinding;
import com.nutomic.syncthingandroid.model.Connection;
import com.nutomic.syncthingandroid.model.Connections;
import com.nutomic.syncthingandroid.model.Device;
@ -62,59 +60,85 @@ public class DevicesAdapter extends ArrayAdapter<Device> {
mRestApi = restApi;
}
static class ViewHolder {
TextView name;
TextView lastSeen;
TextView sharedFoldersTitle;
TextView sharedFolders;
ProgressBar progressBar;
TextView status;
TextView bandwidthUpDown;
View rateInOutView;
}
@Override
@NonNull
public View getView(int position, View convertView, @NonNull ViewGroup parent) {
ItemDeviceListBinding binding = (convertView == null)
? DataBindingUtil.inflate(LayoutInflater.from(mContext), R.layout.item_device_list, parent, false)
: DataBindingUtil.bind(convertView);
ViewHolder holder;
if (convertView == null) {
convertView = LayoutInflater.from(mContext).inflate(R.layout.item_device_list, parent, false);
holder = new ViewHolder();
holder.name = convertView.findViewById(R.id.name);
holder.lastSeen = convertView.findViewById(R.id.lastSeen);
holder.sharedFoldersTitle = convertView.findViewById(R.id.sharedFoldersTitle);
holder.sharedFolders = convertView.findViewById(R.id.sharedFolders);
holder.progressBar = convertView.findViewById(R.id.progressBar);
holder.status = convertView.findViewById(R.id.status);
holder.bandwidthUpDown = convertView.findViewById(R.id.bandwidthUpDown);
holder.rateInOutView = convertView.findViewById(R.id.rateInOutContainer);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
Device device = getItem(position);
binding.name.setText(getItem(position).getDisplayName());
holder.name.setText(device.getDisplayName());
updateDeviceStatusView(binding, device);
return binding.getRoot();
updateDeviceStatusView(holder, device);
return convertView;
}
@SuppressLint("SetTextI18n")
private void updateDeviceStatusView(ItemDeviceListBinding binding, Device device) {
View rateInOutView = binding.getRoot().findViewById(R.id.rateInOutContainer);
private void updateDeviceStatusView(ViewHolder holder, Device device) {
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(mContext);
String deviceLastSeen = sharedPreferences.getString(
Constants.PREF_CACHE_DEVICE_LASTSEEN_PREFIX + device.deviceID, ""
);
final String TIMESTAMP_NEVER_SEEN = "1970-01-01T00:00:00Z";
binding.lastSeen.setText(mContext.getString(R.string.device_last_seen,
holder.lastSeen.setText(mContext.getString(R.string.device_last_seen,
TextUtils.isEmpty(deviceLastSeen) || deviceLastSeen.equals(TIMESTAMP_NEVER_SEEN) ?
mContext.getString(R.string.device_last_seen_never) : Util.formatDateTime(deviceLastSeen))
);
List<Folder> sharedFolders = mConfigRouter.getSharedFolders(device.deviceID);
if (sharedFolders.size() == 0) {
binding.sharedFoldersTitle.setText(R.string.device_state_unused);
binding.sharedFolders.setVisibility(GONE);
holder.sharedFoldersTitle.setText(R.string.device_state_unused);
holder.sharedFolders.setVisibility(GONE);
} else {
binding.sharedFoldersTitle.setText(R.string.shared_folders_title_colon);
binding.sharedFolders.setVisibility(VISIBLE);
binding.sharedFolders.setText("\u2022 " + TextUtils.join("\n\u2022 ", sharedFolders));
holder.sharedFoldersTitle.setText(R.string.shared_folders_title_colon);
holder.sharedFolders.setVisibility(VISIBLE);
holder.sharedFolders.setText("\u2022 " + TextUtils.join("\n\u2022 ", sharedFolders));
}
if (device.paused) {
binding.progressBar.setVisibility(GONE);
rateInOutView.setVisibility(GONE);
binding.status.setVisibility(VISIBLE);
binding.status.setText(R.string.device_paused);
binding.status.setTextColor(ContextCompat.getColor(getContext(), R.color.text_purple));
holder.progressBar.setVisibility(GONE);
holder.rateInOutView.setVisibility(GONE);
holder.status.setVisibility(VISIBLE);
holder.status.setText(R.string.device_paused);
holder.status.setTextColor(ContextCompat.getColor(getContext(), R.color.text_purple));
return;
}
if (mRestApi == null || !mRestApi.isConfigLoaded()) {
// Syncthing is not running.
binding.progressBar.setVisibility(GONE);
rateInOutView.setVisibility(GONE);
binding.status.setText(R.string.device_disconnected);
binding.status.setTextColor(ContextCompat.getColor(getContext(), R.color.text_red));
holder.progressBar.setVisibility(GONE);
holder.rateInOutView.setVisibility(GONE);
holder.status.setText(R.string.device_disconnected);
holder.status.setTextColor(ContextCompat.getColor(getContext(), R.color.text_red));
return;
}
@ -123,7 +147,7 @@ public class DevicesAdapter extends ArrayAdapter<Device> {
final double needBytes = mRestApi.getRemoteDeviceNeedBytes(device.deviceID);
if (conn.connected) {
binding.status.setVisibility(VISIBLE);
holder.status.setVisibility(VISIBLE);
String bandwidthUpDownText = "\u21f5 "; // down+up arrow
bandwidthUpDownText += mContext.getString(R.string.download_title);
@ -133,11 +157,11 @@ public class DevicesAdapter extends ArrayAdapter<Device> {
bandwidthUpDownText += mContext.getString(R.string.upload_title);
bandwidthUpDownText += " \u02c4 "; // up arrow
bandwidthUpDownText += Util.readableTransferRate(getContext(), conn.outBits);
binding.bandwidthUpDown.setText(bandwidthUpDownText);
rateInOutView.setVisibility(VISIBLE);
holder.bandwidthUpDown.setText(bandwidthUpDownText);
holder.rateInOutView.setVisibility(VISIBLE);
Boolean syncingState = !(completion == 100);
binding.progressBar.setVisibility(syncingState ? VISIBLE : GONE);
holder.progressBar.setVisibility(syncingState ? VISIBLE : GONE);
if (!syncingState) {
/**
* UI polish - We distinguish the following cases:
@ -146,40 +170,39 @@ public class DevicesAdapter extends ArrayAdapter<Device> {
*/
if ((conn.inBits + conn.outBits) >= ACTIVE_SYNC_BITS_PER_SECOND_THRESHOLD) {
// case a) device_syncing
binding.status.setText(R.string.state_syncing_general);
binding.status.setTextColor(ContextCompat.getColor(getContext(), R.color.text_blue));
holder.status.setText(R.string.state_syncing_general);
holder.status.setTextColor(ContextCompat.getColor(getContext(), R.color.text_blue));
} else {
// case b) device_up_to_date
binding.status.setText(R.string.device_up_to_date);
binding.status.setTextColor(ContextCompat.getColor(getContext(), R.color.text_green));
holder.status.setText(R.string.device_up_to_date);
holder.status.setTextColor(ContextCompat.getColor(getContext(), R.color.text_green));
}
} else {
binding.progressBar.setProgress(completion);
binding.status.setText(
holder.progressBar.setProgress(completion);
holder.status.setText(
mContext.getString(R.string.device_syncing_percent_bytes,
completion,
Util.readableFileSize(getContext(), needBytes)
)
);
binding.status.setTextColor(ContextCompat.getColor(getContext(), R.color.text_blue));
holder.status.setTextColor(ContextCompat.getColor(getContext(), R.color.text_blue));
}
return;
}
// !conn.connected
binding.progressBar.setVisibility(GONE);
rateInOutView.setVisibility(GONE);
binding.status.setVisibility(VISIBLE);
holder.progressBar.setVisibility(GONE);
holder.rateInOutView.setVisibility(GONE);
holder.status.setVisibility(VISIBLE);
if (needBytes == 0) {
binding.status.setText(R.string.device_disconnected);
holder.status.setText(R.string.device_disconnected);
} else {
binding.status.setText(
holder.status.setText(
mContext.getString(R.string.device_disconnected_not_synced,
Util.readableFileSize(getContext(), needBytes)
)
);
}
binding.status.setTextColor(ContextCompat.getColor(getContext(), R.color.text_red));
return;
holder.status.setTextColor(ContextCompat.getColor(getContext(), R.color.text_red));
}
}

View File

@ -8,15 +8,15 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.core.content.ContextCompat;
import androidx.databinding.DataBindingUtil;
import com.nutomic.syncthingandroid.R;
import com.nutomic.syncthingandroid.databinding.ItemFolderListBinding;
import com.nutomic.syncthingandroid.model.CachedFolderStatus;
import com.nutomic.syncthingandroid.model.Folder;
import com.nutomic.syncthingandroid.model.FolderStatus;
@ -51,19 +51,54 @@ public class FoldersAdapter extends ArrayAdapter<Folder> {
mRestApi = restApi;
}
static class ViewHolder {
TextView label;
TextView directory;
TextView items;
TextView state;
TextView revert;
TextView override;
TextView invalid;
TextView lastItemFinishedItem;
TextView lastItemFinishedTime;
TextView conflicts;
ProgressBar progressBar;
ImageView openFolder;
}
@Override
@NonNull
public View getView(int position, View convertView, @NonNull ViewGroup parent) {
ItemFolderListBinding binding = (convertView == null)
? DataBindingUtil.inflate(LayoutInflater.from(mContext), R.layout.item_folder_list, parent, false)
: DataBindingUtil.bind(convertView);
ViewHolder holder;
if (convertView == null) {
convertView = LayoutInflater.from(mContext).inflate(R.layout.item_folder_list, parent, false);
holder = new ViewHolder();
holder.label = convertView.findViewById(R.id.label);
holder.directory = convertView.findViewById(R.id.directory);
holder.items = convertView.findViewById(R.id.items);
holder.state = convertView.findViewById(R.id.state);
holder.revert = convertView.findViewById(R.id.revert);
holder.override = convertView.findViewById(R.id.override);
holder.invalid = convertView.findViewById(R.id.invalid);
holder.lastItemFinishedItem = convertView.findViewById(R.id.lastItemFinishedItem);
holder.lastItemFinishedTime = convertView.findViewById(R.id.lastItemFinishedTime);
holder.conflicts = convertView.findViewById(R.id.conflicts);
holder.progressBar = convertView.findViewById(R.id.progressBar);
holder.openFolder = convertView.findViewById(R.id.openFolder);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
Folder folder = getItem(position);
binding.label.setText(TextUtils.isEmpty(folder.label) ? folder.id : folder.label);
binding.directory.setText(getShortPathForUI(folder.path));
binding.override.setOnClickListener(view -> { onClickOverride(view, folder); } );
binding.revert.setOnClickListener(view -> { onClickRevert(view, folder); } );
binding.openFolder.setOnClickListener(view -> { FileUtils.openFolder(mContext, folder.path); } );
holder.label.setText(TextUtils.isEmpty(folder.label) ? folder.id : folder.label);
holder.directory.setText(getShortPathForUI(folder.path));
holder.override.setOnClickListener(view -> { onClickOverride(view, folder); });
holder.revert.setOnClickListener(view -> { onClickRevert(view, folder); });
holder.openFolder.setOnClickListener(view -> { FileUtils.openFolder(mContext, folder.path); });
// Update folder icon.
int drawableId = R.drawable.baseline_folder_24;
@ -79,23 +114,23 @@ public class FoldersAdapter extends ArrayAdapter<Folder> {
break;
default:
}
binding.openFolder.setImageResource(drawableId);
holder.openFolder.setImageResource(drawableId);
updateFolderStatusView(binding, folder);
return binding.getRoot();
updateFolderStatusView(holder, folder);
return convertView;
}
private void updateFolderStatusView(ItemFolderListBinding binding, Folder folder) {
if (mRestApi == null || !mRestApi.isConfigLoaded()) {
binding.conflicts.setVisibility(GONE);
binding.lastItemFinishedItem.setVisibility(GONE);
binding.lastItemFinishedTime.setVisibility(GONE);
binding.items.setVisibility(GONE);
binding.override.setVisibility(GONE);
binding.progressBar.setVisibility(GONE);
binding.revert.setVisibility(GONE);
binding.state.setVisibility(GONE);
setTextOrHide(binding.invalid, folder.invalid);
private void updateFolderStatusView(ViewHolder holder, Folder folder) {
if (mRestApi == null || !mRestApi.isConfigLoaded()) {
holder.conflicts.setVisibility(GONE);
holder.lastItemFinishedItem.setVisibility(GONE);
holder.lastItemFinishedTime.setVisibility(GONE);
holder.items.setVisibility(GONE);
holder.override.setVisibility(GONE);
holder.progressBar.setVisibility(GONE);
holder.revert.setVisibility(GONE);
holder.state.setVisibility(GONE);
setTextOrHide(holder.invalid, folder.invalid);
return;
}
@ -109,9 +144,9 @@ public class FoldersAdapter extends ArrayAdapter<Folder> {
long neededItems = folderStatus.needFiles + folderStatus.needDirectories + folderStatus.needSymlinks + folderStatus.needDeletes;
boolean outOfSync = folderStatus.state.equals("idle") && neededItems > 0;
boolean overrideButtonVisible = folder.type.equals(Constants.FOLDER_TYPE_SEND_ONLY) && outOfSync;
binding.override.setVisibility(overrideButtonVisible ? VISIBLE : GONE);
holder.override.setVisibility(overrideButtonVisible ? VISIBLE : GONE);
binding.progressBar.setVisibility(folderStatus.state.equals("syncing") ? VISIBLE : GONE);
holder.progressBar.setVisibility(folderStatus.state.equals("syncing") ? VISIBLE : GONE);
boolean revertButtonVisible = false;
if (folder.type.equals(Constants.FOLDER_TYPE_RECEIVE_ONLY)) {
@ -119,96 +154,90 @@ public class FoldersAdapter extends ArrayAdapter<Folder> {
} else if (folder.type.equals(Constants.FOLDER_TYPE_RECEIVE_ENCRYPTED)) {
revertButtonVisible = ((folderStatus.receiveOnlyTotalItems - folderStatus.receiveOnlyChangedDeletes) > 0);
}
binding.revert.setText(mContext.getString(folder.type.equals(Constants.FOLDER_TYPE_RECEIVE_ONLY) ?
holder.revert.setText(mContext.getString(folder.type.equals(Constants.FOLDER_TYPE_RECEIVE_ONLY) ?
R.string.revert_local_changes :
R.string.delete_unexpected_items
));
binding.revert.setVisibility(revertButtonVisible ? VISIBLE : GONE);
));
holder.revert.setVisibility(revertButtonVisible ? VISIBLE : GONE);
binding.state.setVisibility(VISIBLE);
holder.state.setVisibility(VISIBLE);
if (outOfSync) {
binding.state.setText(mContext.getString(R.string.status_outofsync));
binding.state.setTextColor(ContextCompat.getColor(mContext, R.color.text_red));
holder.state.setText(mContext.getString(R.string.status_outofsync));
holder.state.setTextColor(ContextCompat.getColor(mContext, R.color.text_red));
} else if (failedItems) {
binding.state.setText(mContext.getString(R.string.state_failed_items, folderStatus.errors));
binding.state.setTextColor(ContextCompat.getColor(mContext, R.color.text_red));
holder.state.setText(mContext.getString(R.string.state_failed_items, folderStatus.errors));
holder.state.setTextColor(ContextCompat.getColor(mContext, R.color.text_red));
} else {
if (folder.paused) {
binding.state.setText(mContext.getString(R.string.state_paused));
binding.state.setTextColor(ContextCompat.getColor(mContext, R.color.text_purple));
holder.state.setText(mContext.getString(R.string.state_paused));
holder.state.setTextColor(ContextCompat.getColor(mContext, R.color.text_purple));
} else {
switch(folderStatus.state) {
case "clean-waiting":
binding.state.setText(mContext.getString(R.string.state_clean_waiting));
binding.state.setTextColor(ContextCompat.getColor(mContext, R.color.text_orange));
holder.state.setText(R.string.state_clean_waiting);
holder.state.setTextColor(ContextCompat.getColor(mContext, R.color.text_orange));
break;
case "cleaning":
binding.state.setText(mContext.getString(R.string.state_cleaning));
binding.state.setTextColor(ContextCompat.getColor(mContext, R.color.text_blue));
holder.state.setText(R.string.state_cleaning);
holder.state.setTextColor(ContextCompat.getColor(mContext, R.color.text_blue));
break;
case "idle":
if (folder.getDeviceCount() <= 1) {
// Special case: The folder is IDLE and UNSHARED.
binding.state.setText(mContext.getString(R.string.state_unshared));
binding.state.setTextColor(ContextCompat.getColor(mContext, R.color.text_orange));
holder.state.setText(R.string.state_unshared);
holder.state.setTextColor(ContextCompat.getColor(mContext, R.color.text_orange));
} else if (revertButtonVisible) {
binding.state.setText(mContext.getString(R.string.state_local_additions));
binding.state.setTextColor(ContextCompat.getColor(mContext, R.color.text_green));
holder.state.setText(R.string.state_local_additions);
holder.state.setTextColor(ContextCompat.getColor(mContext, R.color.text_green));
} else {
binding.state.setText(mContext.getString(R.string.state_up_to_date));
binding.state.setTextColor(ContextCompat.getColor(mContext, R.color.text_green));
holder.state.setText(R.string.state_up_to_date);
holder.state.setTextColor(ContextCompat.getColor(mContext, R.color.text_green));
}
break;
case "scan-waiting":
binding.state.setText(mContext.getString(R.string.state_scan_waiting));
binding.state.setTextColor(ContextCompat.getColor(mContext, R.color.text_orange));
holder.state.setText(R.string.state_scan_waiting);
holder.state.setTextColor(ContextCompat.getColor(mContext, R.color.text_orange));
break;
case "scanning":
binding.state.setText(mContext.getString(R.string.state_scanning));
binding.state.setTextColor(ContextCompat.getColor(mContext, R.color.text_blue));
holder.state.setText(R.string.state_scanning);
holder.state.setTextColor(ContextCompat.getColor(mContext, R.color.text_blue));
break;
case "sync-waiting":
binding.state.setText(mContext.getString(R.string.state_sync_waiting));
binding.state.setTextColor(ContextCompat.getColor(mContext, R.color.text_orange));
holder.state.setText(R.string.state_sync_waiting);
holder.state.setTextColor(ContextCompat.getColor(mContext, R.color.text_orange));
break;
case "syncing":
binding.progressBar.setProgress((int) cachedFolderStatus.completion);
binding.state.setText(
mContext.getString(
R.string.state_syncing,
(int) cachedFolderStatus.completion
)
);
binding.state.setTextColor(ContextCompat.getColor(mContext, R.color.text_blue));
holder.progressBar.setProgress((int) cachedFolderStatus.completion);
holder.state.setText(mContext.getString(R.string.state_syncing, (int) cachedFolderStatus.completion));
holder.state.setTextColor(ContextCompat.getColor(mContext, R.color.text_blue));
break;
case "sync-preparing":
binding.state.setText(mContext.getString(R.string.state_sync_preparing));
binding.state.setTextColor(ContextCompat.getColor(mContext, R.color.text_blue));
holder.state.setText(R.string.state_sync_preparing);
holder.state.setTextColor(ContextCompat.getColor(mContext, R.color.text_blue));
break;
case "error":
if (TextUtils.isEmpty(folderStatus.error)) {
binding.state.setText(mContext.getString(R.string.state_error));
holder.state.setText(R.string.state_error);
} else {
binding.state.setText(mContext.getString(R.string.state_error_message, folderStatus.error));
holder.state.setText(mContext.getString(R.string.state_error_message, folderStatus.error));
}
binding.state.setTextColor(ContextCompat.getColor(mContext, R.color.text_red));
holder.state.setTextColor(ContextCompat.getColor(mContext, R.color.text_red));
break;
case "unknown":
binding.state.setText(mContext.getString(R.string.state_unknown));
binding.state.setTextColor(ContextCompat.getColor(mContext, R.color.text_red));
holder.state.setText(R.string.state_unknown);
holder.state.setTextColor(ContextCompat.getColor(mContext, R.color.text_red));
break;
default:
binding.state.setText(folderStatus.state);
binding.state.setTextColor(ContextCompat.getColor(mContext, R.color.text_red));
holder.state.setText(folderStatus.state);
holder.state.setTextColor(ContextCompat.getColor(mContext, R.color.text_red));
}
}
}
showConflictsUI(binding, cachedFolderStatus.discoveredConflictFiles);
showConflictsUI(holder.conflicts, cachedFolderStatus.discoveredConflictFiles);
showLastItemFinishedUI(holder.lastItemFinishedItem, holder.lastItemFinishedTime, cachedFolderStatus);
showLastItemFinishedUI(binding, cachedFolderStatus);
binding.items.setVisibility(folder.paused ? GONE : VISIBLE);
holder.items.setVisibility(folder.paused ? GONE : VISIBLE);
String itemsAndSize = "\u2211 ";
itemsAndSize += mContext.getResources()
.getQuantityString(R.plurals.files, (int) folderStatus.inSyncFiles, folderStatus.inSyncFiles, folderStatus.globalFiles);
@ -216,42 +245,40 @@ public class FoldersAdapter extends ArrayAdapter<Folder> {
itemsAndSize += mContext.getString(R.string.folder_size_format,
Util.readableFileSize(mContext, folderStatus.inSyncBytes),
Util.readableFileSize(mContext, folderStatus.globalBytes));
binding.items.setText(itemsAndSize);
holder.items.setText(itemsAndSize);
setTextOrHide(binding.invalid, folderStatus.invalid);
setTextOrHide(holder.invalid, folderStatus.invalid);
}
private void showConflictsUI(ItemFolderListBinding binding,
final String[] discoveredConflictFiles) {
Integer conflictFileCount = discoveredConflictFiles.length;
private void showConflictsUI(TextView view, final String[] discoveredConflictFiles) {
int conflictFileCount = discoveredConflictFiles.length;
if (conflictFileCount == 0) {
binding.conflicts.setVisibility(GONE);
view.setVisibility(GONE);
return;
}
String itemCountAndFirst = "\u26a0 ";
itemCountAndFirst += mContext.getResources()
.getQuantityString(R.plurals.conflicts, (int) conflictFileCount, conflictFileCount);
.getQuantityString(R.plurals.conflicts, conflictFileCount, conflictFileCount);
itemCountAndFirst += "\n\u292e ";
itemCountAndFirst += discoveredConflictFiles[0];
if (conflictFileCount > 1) {
itemCountAndFirst += "\n\u2026";
}
binding.conflicts.setText(itemCountAndFirst);
binding.conflicts.setVisibility(VISIBLE);
return;
view.setText(itemCountAndFirst);
view.setVisibility(VISIBLE);
}
private void showLastItemFinishedUI(ItemFolderListBinding binding,
final CachedFolderStatus cachedFolderStatus) {
private void showLastItemFinishedUI(TextView itemView, TextView timeView, final CachedFolderStatus cachedFolderStatus) {
if (TextUtils.isEmpty(cachedFolderStatus.lastItemFinishedAction) ||
TextUtils.isEmpty(cachedFolderStatus.lastItemFinishedItem) ||
TextUtils.isEmpty(cachedFolderStatus.lastItemFinishedTime)) {
binding.lastItemFinishedItem.setVisibility(GONE);
binding.lastItemFinishedTime.setVisibility(GONE);
itemView.setVisibility(GONE);
timeView.setVisibility(GONE);
return;
}
String finishedItemText = "\u21cc";
switch (cachedFolderStatus.lastItemFinishedAction) {
case "delete":
@ -267,16 +294,13 @@ public class FoldersAdapter extends ArrayAdapter<Folder> {
finishedItemText += " \u2049";
}
finishedItemText += " " + Util.getPathEllipsis(cachedFolderStatus.lastItemFinishedItem);
itemView.setText(finishedItemText);
itemView.setVisibility(VISIBLE);
binding.lastItemFinishedItem.setText(finishedItemText);
binding.lastItemFinishedItem.setVisibility(VISIBLE);
String finishedItemTime = "\u21cc\u231a";
finishedItemTime += Util.formatTime(cachedFolderStatus.lastItemFinishedTime);
binding.lastItemFinishedTime.setText(finishedItemTime);
binding.lastItemFinishedTime.setVisibility(VISIBLE);
return;
String finishedTimeText = "\u21cc\u231a";
finishedTimeText += Util.formatTime(cachedFolderStatus.lastItemFinishedTime);
timeView.setText(finishedTimeText);
timeView.setVisibility(VISIBLE);
}
private void setTextOrHide(TextView view, String text) {

View File

@ -1,115 +1,111 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android">
<RelativeLayout xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:descendantFocusability="blocksDescendants">
<RelativeLayout
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/inner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:descendantFocusability="blocksDescendants">
android:paddingBottom="8dp"
android:paddingLeft="@dimen/abc_action_bar_content_inset_material"
android:paddingRight="@dimen/abc_action_bar_content_inset_material"
android:paddingTop="8dp">
<RelativeLayout
android:id="@+id/inner"
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:ellipsize="end"
android:lines="1"
android:textAppearance="?textAppearanceListItemPrimary" />
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="8dp"
android:paddingLeft="@dimen/abc_action_bar_content_inset_material"
android:paddingRight="@dimen/abc_action_bar_content_inset_material"
android:paddingTop="8dp">
android:layout_below="@id/name"
android:indeterminate="false"
android:max="100"
android:progress="0"
android:ellipsize="end"
android:theme="@style/progressBarBlue" />
<RelativeLayout
android:id="@+id/deviceInfoContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/progressBar">
<TextView
android:id="@+id/name"
android:id="@+id/lastSeen"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:ellipsize="end"
android:lines="1"
android:textAppearance="?textAppearanceListItemPrimary" />
android:textAppearance="?textAppearanceListItemSecondary" />
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
<TextView
android:id="@+id/sharedFoldersTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/name"
android:indeterminate="false"
android:max="100"
android:progress="0"
android:ellipsize="end"
android:theme="@style/progressBarBlue" />
android:layout_below="@id/lastSeen"
android:text="@string/shared_folders_title_colon"
android:textAppearance="?textAppearanceListItemSecondary" />
<RelativeLayout
android:id="@+id/deviceInfoContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/progressBar">
<TextView
android:id="@+id/lastSeen"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?textAppearanceListItemSecondary" />
<TextView
android:id="@+id/sharedFoldersTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/lastSeen"
android:text="@string/shared_folders_title_colon"
android:textAppearance="?textAppearanceListItemSecondary" />
<TextView
android:id="@+id/sharedFolders"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/sharedFoldersTitle"
android:textAppearance="?textAppearanceListItemSecondary" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/rateInOutContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/deviceInfoContainer">
<TextView
android:id="@+id/bandwidthUpDown"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?textAppearanceListItemSecondary" />
</RelativeLayout>
<TextView
android:id="@+id/sharedFolders"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/sharedFoldersTitle"
android:textAppearance="?textAppearanceListItemSecondary" />
</RelativeLayout>
<TextView
android:id="@+id/status"
android:ellipsize="end"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toStartOf="@+id/open_device"
android:paddingBottom="12dp"
android:paddingTop="12dp"
android:lines="1"
android:textAppearance="?textAppearanceListItemSmall"
tools:ignore="RelativeOverlap" />
<RelativeLayout
android:id="@+id/rateInOutContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/deviceInfoContainer">
<ImageButton
android:id="@+id/open_device"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignTop="@+id/inner"
android:background="@null"
android:contentDescription="@null"
android:paddingBottom="9dp"
android:paddingEnd="20dp"
android:paddingStart="30dp"
android:paddingTop="9dp"
app:srcCompat="@drawable/ic_phonelink_black_24dp_active" />
<TextView
android:id="@+id/bandwidthUpDown"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?textAppearanceListItemSecondary" />
</RelativeLayout>
</RelativeLayout>
</layout>
<TextView
android:id="@+id/status"
android:ellipsize="end"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toStartOf="@+id/open_device"
android:paddingBottom="12dp"
android:paddingTop="12dp"
android:lines="1"
android:textAppearance="?textAppearanceListItemSmall"
tools:ignore="RelativeOverlap" />
<ImageButton
android:id="@+id/open_device"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignTop="@+id/inner"
android:background="@null"
android:contentDescription="@null"
android:paddingBottom="9dp"
android:paddingEnd="20dp"
android:paddingStart="30dp"
android:paddingTop="9dp"
app:srcCompat="@drawable/ic_phonelink_black_24dp_active" />
</RelativeLayout>

View File

@ -1,151 +1,147 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android">
<RelativeLayout xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:descendantFocusability="blocksDescendants">
<RelativeLayout
android:id="@+id/inner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:descendantFocusability="blocksDescendants">
android:paddingBottom="8dp"
android:paddingLeft="@dimen/abc_action_bar_content_inset_material"
android:paddingRight="@dimen/abc_action_bar_content_inset_material"
android:paddingTop="8dp">
<RelativeLayout
android:id="@+id/inner"
<TextView
android:id="@+id/label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_toStartOf="@+id/state"
android:ellipsize="end"
android:maxLines="1"
android:textAppearance="?textAppearanceListItemPrimary" />
<TextView
android:id="@+id/state"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:textAppearance="?textAppearanceListItemSmall" />
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="8dp"
android:paddingLeft="@dimen/abc_action_bar_content_inset_material"
android:paddingRight="@dimen/abc_action_bar_content_inset_material"
android:paddingTop="8dp">
android:layout_below="@id/label"
android:indeterminate="false"
android:max="100"
android:progress="0"
android:ellipsize="end"
android:theme="@style/progressBarBlue" />
<TextView
android:id="@+id/label"
<TextView
android:id="@+id/directory"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/progressBar"
android:ellipsize="end"
android:textAppearance="?textAppearanceListItemSecondary" />
<RelativeLayout
android:id="@+id/override_revert_container"
android:layout_below="@id/directory"
android:layout_height="wrap_content"
android:layout_width="wrap_content">
<Button
android:id="@+id/override"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_toStartOf="@+id/state"
android:ellipsize="end"
android:maxLines="1"
android:textAppearance="?textAppearanceListItemPrimary" />
android:contentDescription="@string/override_changes"
android:paddingEnd="20dp"
android:paddingLeft="20dp"
android:paddingRight="20dp"
android:paddingStart="20dp"
android:text="@string/override_changes"
app:drawableLeftCompat="@drawable/ic_cloud_upload_red_24"
app:drawableStartCompat="@drawable/ic_cloud_upload_red_24"
android:drawablePadding="5sp"
android:textSize="12sp" />
<TextView
android:id="@+id/state"
<Button
android:id="@+id/revert"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:textAppearance="?textAppearanceListItemSmall" />
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/label"
android:indeterminate="false"
android:max="100"
android:progress="0"
android:ellipsize="end"
android:theme="@style/progressBarBlue" />
<TextView
android:id="@+id/directory"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/progressBar"
android:ellipsize="end"
android:textAppearance="?textAppearanceListItemSecondary" />
<RelativeLayout
android:id="@+id/override_revert_container"
android:layout_below="@id/directory"
android:layout_height="wrap_content"
android:layout_width="wrap_content">
<Button
android:id="@+id/override"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/override_changes"
android:paddingEnd="20dp"
android:paddingLeft="20dp"
android:paddingRight="20dp"
android:paddingStart="20dp"
android:text="@string/override_changes"
app:drawableLeftCompat="@drawable/ic_cloud_upload_red_24"
app:drawableStartCompat="@drawable/ic_cloud_upload_red_24"
android:drawablePadding="5sp"
android:textSize="12sp" />
<Button
android:id="@+id/revert"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/revert_local_changes"
android:paddingEnd="20dp"
android:paddingLeft="20dp"
android:paddingRight="20dp"
android:paddingStart="20dp"
android:text="@string/revert_local_changes"
app:drawableLeftCompat="@drawable/ic_cloud_download_red_24"
app:drawableStartCompat="@drawable/ic_cloud_download_red_24"
android:drawablePadding="5sp"
android:textSize="12sp" />
</RelativeLayout>
<TextView
android:id="@+id/lastItemFinishedItem"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/override_revert_container"
android:textAppearance="?textAppearanceListItemSecondary" />
<TextView
android:id="@+id/lastItemFinishedTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/lastItemFinishedItem"
android:textAppearance="?textAppearanceListItemSecondary" />
<TextView
android:id="@+id/items"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/lastItemFinishedTime"
android:textAppearance="?textAppearanceListItemSecondary" />
<TextView
android:id="@+id/conflicts"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/items"
android:textAppearance="?textAppearanceListItemSecondary"
android:textColor="@color/text_orange" />
<TextView
android:id="@+id/invalid"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/conflicts"
android:textAppearance="?textAppearanceListItemSecondary"
android:textColor="@color/text_red" />
android:contentDescription="@string/revert_local_changes"
android:paddingEnd="20dp"
android:paddingLeft="20dp"
android:paddingRight="20dp"
android:paddingStart="20dp"
android:text="@string/revert_local_changes"
app:drawableLeftCompat="@drawable/ic_cloud_download_red_24"
app:drawableStartCompat="@drawable/ic_cloud_download_red_24"
android:drawablePadding="5sp"
android:textSize="12sp" />
</RelativeLayout>
<ImageButton
android:id="@+id/open_folder"
<TextView
android:id="@+id/lastItemFinishedItem"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/inner"
android:layout_alignParentEnd="true"
android:layout_alignTop="@+id/inner"
android:background="@null"
android:contentDescription="@string/open_file_manager"
android:paddingBottom="5dp"
android:paddingEnd="20dp"
android:paddingStart="30dp"
android:paddingTop="5dp"
app:srcCompat="@drawable/baseline_folder_24" />
android:layout_below="@id/override_revert_container"
android:textAppearance="?textAppearanceListItemSecondary" />
<TextView
android:id="@+id/lastItemFinishedTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/lastItemFinishedItem"
android:textAppearance="?textAppearanceListItemSecondary" />
<TextView
android:id="@+id/items"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/lastItemFinishedTime"
android:textAppearance="?textAppearanceListItemSecondary" />
<TextView
android:id="@+id/conflicts"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/items"
android:textAppearance="?textAppearanceListItemSecondary"
android:textColor="@color/text_orange" />
<TextView
android:id="@+id/invalid"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/conflicts"
android:textAppearance="?textAppearanceListItemSecondary"
android:textColor="@color/text_red" />
</RelativeLayout>
</layout>
<ImageButton
android:id="@+id/openFolder"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/inner"
android:layout_alignParentEnd="true"
android:layout_alignTop="@+id/inner"
android:background="@null"
android:contentDescription="@string/open_file_manager"
android:paddingBottom="5dp"
android:paddingEnd="20dp"
android:paddingStart="30dp"
android:paddingTop="5dp"
app:srcCompat="@drawable/baseline_folder_24" />
</RelativeLayout>

View File

@ -5,7 +5,7 @@ plugins {
alias(libs.plugins.android.application) apply false
alias(libs.plugins.compose.compiler) apply false
alias(libs.plugins.kotlin.android) apply false
alias(libs.plugins.kotlin.kapt) apply false
alias(libs.plugins.ksp) apply false
}
buildscript {

View File

@ -23,6 +23,7 @@ gson = "2.13.1"
guava = "33.4.8-android"
jbcrypt = "0.4"
kotlin = "2.2.0"
ksp = "2.2.0-2.0.2"
libsuperuser = "1.1.1"
lingala-zip4j = "2.11.5"
localbroadcastmanager = "1.1.0"
@ -72,5 +73,5 @@ aboutLibraries = { id = "com.mikepenz.aboutlibraries.plugin", version.ref = "abo
android-application = { id = "com.android.application", version.ref = "android-gradle-plugin" }
compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
kotlin-kapt = { id = "org.jetbrains.kotlin.kapt", version.ref = "kotlin" }
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
vyarus-use-python = { id = "ru.vyarus.use-python", version.ref = "vyarus-use-python" }

View File

@ -0,0 +1,13 @@
@echo off
setlocal enabledelayedexpansion
::
:: Consts.
SET EMULATOR_NAME="Pixel_9_API_35_A15_AOSP"
::
where emulator.exe >NUL: 2>&1 || SET "PATH=%PATH%;%ANDROID_HOME%\emulator"
IF NOT DEFINED ANDROID_AVD_HOME SET "ANDROID_AVD_HOME=%ANDROID_USER_HOME%\AVD"
::
emulator -list-avds | findstr /i %EMULATOR_NAME%
start "" conhost --headless emulator -avd %EMULATOR_NAME%
::
goto :eof