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

View File

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

View File

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

View File

@ -1,115 +1,111 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<layout xmlns:app="http://schemas.android.com/apk/res-auto" <RelativeLayout xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"> 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 <RelativeLayout
xmlns:tools="http://schemas.android.com/tools" android:id="@+id/inner"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" 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 <TextView
android:id="@+id/inner" 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_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingBottom="8dp" android:layout_below="@id/name"
android:paddingLeft="@dimen/abc_action_bar_content_inset_material" android:indeterminate="false"
android:paddingRight="@dimen/abc_action_bar_content_inset_material" android:max="100"
android:paddingTop="8dp"> 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 <TextView
android:id="@+id/name" android:id="@+id/lastSeen"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentStart="true" android:textAppearance="?textAppearanceListItemSecondary" />
android:ellipsize="end"
android:lines="1"
android:textAppearance="?textAppearanceListItemPrimary" />
<ProgressBar <TextView
android:id="@+id/progressBar" android:id="@+id/sharedFoldersTitle"
style="?android:attr/progressBarStyleHorizontal" android:layout_width="wrap_content"
android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_below="@id/name" android:layout_below="@id/lastSeen"
android:indeterminate="false" android:text="@string/shared_folders_title_colon"
android:max="100" android:textAppearance="?textAppearanceListItemSecondary" />
android:progress="0"
android:ellipsize="end"
android:theme="@style/progressBarBlue" />
<RelativeLayout <TextView
android:id="@+id/deviceInfoContainer" android:id="@+id/sharedFolders"
android:layout_width="match_parent" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="wrap_content"
android:layout_below="@+id/progressBar"> android:layout_below="@id/sharedFoldersTitle"
android:textAppearance="?textAppearanceListItemSecondary" />
<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>
</RelativeLayout> </RelativeLayout>
<TextView <RelativeLayout
android:id="@+id/status" android:id="@+id/rateInOutContainer"
android:ellipsize="end" android:layout_width="match_parent"
android:layout_width="wrap_content" android:layout_height="match_parent"
android:layout_height="wrap_content" android:layout_below="@+id/deviceInfoContainer">
android:layout_toStartOf="@+id/open_device"
android:paddingBottom="12dp"
android:paddingTop="12dp"
android:lines="1"
android:textAppearance="?textAppearanceListItemSmall"
tools:ignore="RelativeOverlap" />
<ImageButton <TextView
android:id="@+id/open_device" android:id="@+id/bandwidthUpDown"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentEnd="true" android:textAppearance="?textAppearanceListItemSecondary" />
android:layout_alignTop="@+id/inner"
android:background="@null" </RelativeLayout>
android:contentDescription="@null"
android:paddingBottom="9dp"
android:paddingEnd="20dp"
android:paddingStart="30dp"
android:paddingTop="9dp"
app:srcCompat="@drawable/ic_phonelink_black_24dp_active" />
</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"?> <?xml version="1.0" encoding="utf-8"?>
<layout xmlns:app="http://schemas.android.com/apk/res-auto" <RelativeLayout xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"> xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:descendantFocusability="blocksDescendants">
<RelativeLayout <RelativeLayout
android:id="@+id/inner"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" 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 <TextView
android:id="@+id/inner" 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_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingBottom="8dp" android:layout_below="@id/label"
android:paddingLeft="@dimen/abc_action_bar_content_inset_material" android:indeterminate="false"
android:paddingRight="@dimen/abc_action_bar_content_inset_material" android:max="100"
android:paddingTop="8dp"> android:progress="0"
android:ellipsize="end"
android:theme="@style/progressBarBlue" />
<TextView <TextView
android:id="@+id/label" 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_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentStart="true" android:contentDescription="@string/override_changes"
android:layout_toStartOf="@+id/state" android:paddingEnd="20dp"
android:ellipsize="end" android:paddingLeft="20dp"
android:maxLines="1" android:paddingRight="20dp"
android:textAppearance="?textAppearanceListItemPrimary" /> 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 <Button
android:id="@+id/state" android:id="@+id/revert"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentEnd="true" android:contentDescription="@string/revert_local_changes"
android:textAppearance="?textAppearanceListItemSmall" /> android:paddingEnd="20dp"
android:paddingLeft="20dp"
<ProgressBar android:paddingRight="20dp"
android:id="@+id/progressBar" android:paddingStart="20dp"
style="?android:attr/progressBarStyleHorizontal" android:text="@string/revert_local_changes"
android:layout_width="match_parent" app:drawableLeftCompat="@drawable/ic_cloud_download_red_24"
android:layout_height="wrap_content" app:drawableStartCompat="@drawable/ic_cloud_download_red_24"
android:layout_below="@id/label" android:drawablePadding="5sp"
android:indeterminate="false" android:textSize="12sp" />
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" />
</RelativeLayout> </RelativeLayout>
<ImageButton <TextView
android:id="@+id/open_folder" android:id="@+id/lastItemFinishedItem"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignBottom="@+id/inner" android:layout_below="@id/override_revert_container"
android:layout_alignParentEnd="true" android:textAppearance="?textAppearanceListItemSecondary" />
android:layout_alignTop="@+id/inner"
android:background="@null" <TextView
android:contentDescription="@string/open_file_manager" android:id="@+id/lastItemFinishedTime"
android:paddingBottom="5dp" android:layout_width="wrap_content"
android:paddingEnd="20dp" android:layout_height="wrap_content"
android:paddingStart="30dp" android:layout_below="@id/lastItemFinishedItem"
android:paddingTop="5dp" android:textAppearance="?textAppearanceListItemSecondary" />
app:srcCompat="@drawable/baseline_folder_24" />
<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> </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.android.application) apply false
alias(libs.plugins.compose.compiler) apply false alias(libs.plugins.compose.compiler) apply false
alias(libs.plugins.kotlin.android) apply false alias(libs.plugins.kotlin.android) apply false
alias(libs.plugins.kotlin.kapt) apply false alias(libs.plugins.ksp) apply false
} }
buildscript { buildscript {

View File

@ -23,6 +23,7 @@ gson = "2.13.1"
guava = "33.4.8-android" guava = "33.4.8-android"
jbcrypt = "0.4" jbcrypt = "0.4"
kotlin = "2.2.0" kotlin = "2.2.0"
ksp = "2.2.0-2.0.2"
libsuperuser = "1.1.1" libsuperuser = "1.1.1"
lingala-zip4j = "2.11.5" lingala-zip4j = "2.11.5"
localbroadcastmanager = "1.1.0" 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" } android-application = { id = "com.android.application", version.ref = "android-gradle-plugin" }
compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
kotlin-android = { id = "org.jetbrains.kotlin.android", 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" } 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