mirror of
https://github.com/KDE/kdeconnect-android
synced 2025-08-22 09:58:08 +00:00
Reworked custom device list
- Solved serialization issue when commas were used - Validate hosts and show toast message if host is invalid - Show whether device can be reached over the network - Show toast message when host already exists - Code TODO's (including sorting device list)
This commit is contained in:
parent
553bae4a33
commit
75ddac0bf0
@ -24,7 +24,7 @@ SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted
|
|||||||
app:drawableEndCompat="@drawable/ic_delete"
|
app:drawableEndCompat="@drawable/ic_delete"
|
||||||
app:drawableStartCompat="@drawable/ic_delete" />
|
app:drawableStartCompat="@drawable/ic_delete" />
|
||||||
|
|
||||||
<FrameLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:id="@+id/swipeableView"
|
android:id="@+id/swipeableView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
@ -32,17 +32,30 @@ SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted
|
|||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/deviceNameOrIP"
|
android:id="@+id/deviceNameOrIP"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
android:background="?android:selectableItemBackground"
|
android:background="?android:selectableItemBackground"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:minHeight="?android:attr/listPreferredItemHeightSmall"
|
android:minHeight="?android:attr/listPreferredItemHeightSmall"
|
||||||
android:paddingEnd="?android:attr/listPreferredItemPaddingRight"
|
|
||||||
android:paddingStart="?android:attr/listPreferredItemPaddingLeft"
|
android:paddingStart="?android:attr/listPreferredItemPaddingLeft"
|
||||||
|
android:paddingEnd="?android:attr/listPreferredItemPaddingRight"
|
||||||
android:textAppearance="?android:attr/textAppearanceListItemSmall"
|
android:textAppearance="?android:attr/textAppearanceListItemSmall"
|
||||||
android:visibility="visible"
|
android:visibility="visible"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:text="192.168.0.1" />
|
tools:text="192.168.0.1" />
|
||||||
|
|
||||||
</FrameLayout>
|
<TextView
|
||||||
|
android:id="@+id/connectionStatus"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:paddingStart="?android:attr/listPreferredItemPaddingLeft"
|
||||||
|
android:paddingEnd="?android:attr/listPreferredItemPaddingRight"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
@ -581,4 +581,11 @@ SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted
|
|||||||
<string name="mpris_keepwatching_settings_summary">Show a silent notification to continue playing on this device after closing media</string>
|
<string name="mpris_keepwatching_settings_summary">Show a silent notification to continue playing on this device after closing media</string>
|
||||||
<string name="notification_channel_keepwatching">Continue playing</string>
|
<string name="notification_channel_keepwatching">Continue playing</string>
|
||||||
|
|
||||||
|
<string name="ping_result">Pinged in %1$d milliseconds</string>
|
||||||
|
<string name="ping_failed">Could not ping device</string>
|
||||||
|
<string name="ping_in_progress">Pinging…</string>
|
||||||
|
|
||||||
|
<string name="device_host_invalid">Host is invalid. Use a valid hostname, IPv4, or IPv6</string>
|
||||||
|
<string name="device_host_duplicate">Host already exists in the list</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -20,6 +20,7 @@ import org.json.JSONException;
|
|||||||
import org.kde.kdeconnect.Backends.BaseLink;
|
import org.kde.kdeconnect.Backends.BaseLink;
|
||||||
import org.kde.kdeconnect.Backends.BaseLinkProvider;
|
import org.kde.kdeconnect.Backends.BaseLinkProvider;
|
||||||
import org.kde.kdeconnect.Device;
|
import org.kde.kdeconnect.Device;
|
||||||
|
import org.kde.kdeconnect.DeviceHost;
|
||||||
import org.kde.kdeconnect.DeviceInfo;
|
import org.kde.kdeconnect.DeviceInfo;
|
||||||
import org.kde.kdeconnect.Helpers.DeviceHelper;
|
import org.kde.kdeconnect.Helpers.DeviceHelper;
|
||||||
import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper;
|
import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper;
|
||||||
@ -384,19 +385,19 @@ public class LanLinkProvider extends BaseLinkProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ThreadHelper.execute(() -> {
|
ThreadHelper.execute(() -> {
|
||||||
List<String> ipStringList = CustomDevicesActivity
|
List<DeviceHost> hostList = CustomDevicesActivity
|
||||||
.getCustomDeviceList(PreferenceManager.getDefaultSharedPreferences(context));
|
.getCustomDeviceList(PreferenceManager.getDefaultSharedPreferences(context));
|
||||||
|
|
||||||
if (TrustedNetworkHelper.isTrustedNetwork(context)) {
|
if (TrustedNetworkHelper.isTrustedNetwork(context)) {
|
||||||
ipStringList.add("255.255.255.255"); //Default: broadcast.
|
hostList.add(DeviceHost.BROADCAST); //Default: broadcast.
|
||||||
} else {
|
} else {
|
||||||
Log.i("LanLinkProvider", "Current network isn't trusted, not broadcasting");
|
Log.i("LanLinkProvider", "Current network isn't trusted, not broadcasting");
|
||||||
}
|
}
|
||||||
|
|
||||||
ArrayList<InetAddress> ipList = new ArrayList<>();
|
ArrayList<InetAddress> ipList = new ArrayList<>();
|
||||||
for (String ip : ipStringList) {
|
for (DeviceHost host : hostList) {
|
||||||
try {
|
try {
|
||||||
ipList.add(InetAddress.getByName(ip));
|
ipList.add(InetAddress.getByName(host.toString()));
|
||||||
} catch (UnknownHostException e) {
|
} catch (UnknownHostException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
65
src/org/kde/kdeconnect/DeviceHost.kt
Normal file
65
src/org/kde/kdeconnect/DeviceHost.kt
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
package org.kde.kdeconnect
|
||||||
|
|
||||||
|
import org.kde.kdeconnect.Helpers.ThreadHelper
|
||||||
|
import java.net.InetAddress
|
||||||
|
|
||||||
|
class DeviceHost private constructor(private val host: String) {
|
||||||
|
// Wrapper because Kotlin doesn't allow nested nullability
|
||||||
|
data class PingResult(val latency: Long?)
|
||||||
|
|
||||||
|
/** The amount of milliseconds the ping request took or null it's in progress */
|
||||||
|
var ping: PingResult? = null
|
||||||
|
private set
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the host can be reached over the network.
|
||||||
|
* @param callback Callback for updating UI elements
|
||||||
|
*/
|
||||||
|
fun checkReachable(callback: () -> Unit) {
|
||||||
|
ThreadHelper.execute {
|
||||||
|
try {
|
||||||
|
val address = InetAddress.getByName(this.host)
|
||||||
|
val startTime = System.currentTimeMillis()
|
||||||
|
val pingable = address.isReachable(PING_TIMEOUT)
|
||||||
|
val delayMillis = System.currentTimeMillis() - startTime
|
||||||
|
val pingResult = PingResult(if (pingable) delayMillis else null)
|
||||||
|
ping = pingResult
|
||||||
|
}
|
||||||
|
catch (_: Exception) {
|
||||||
|
ping = PingResult(null)
|
||||||
|
}
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
require(isValidDeviceHost(host)) { "Invalid host" }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return this.host
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
/** Ping timeout */
|
||||||
|
private const val PING_TIMEOUT = 3_000
|
||||||
|
private val hostnameValidityPattern = Regex("^[0-9A-Za-z._-]+$")
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun isValidDeviceHost(host: String): Boolean {
|
||||||
|
return hostnameValidityPattern.matches(host)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun toDeviceHostOrNull(host: String): DeviceHost? {
|
||||||
|
return if (isValidDeviceHost(host)) {
|
||||||
|
DeviceHost(host)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
val BROADCAST: DeviceHost = DeviceHost("255.255.255.255")
|
||||||
|
}
|
||||||
|
}
|
@ -13,6 +13,7 @@ import android.preference.PreferenceManager;
|
|||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
@ -25,16 +26,16 @@ import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
|||||||
import com.google.android.material.snackbar.BaseTransientBottomBar;
|
import com.google.android.material.snackbar.BaseTransientBottomBar;
|
||||||
import com.google.android.material.snackbar.Snackbar;
|
import com.google.android.material.snackbar.Snackbar;
|
||||||
|
|
||||||
|
import org.kde.kdeconnect.DeviceHost;
|
||||||
import org.kde.kdeconnect_tp.R;
|
import org.kde.kdeconnect_tp.R;
|
||||||
import org.kde.kdeconnect_tp.databinding.ActivityCustomDevicesBinding;
|
import org.kde.kdeconnect_tp.databinding.ActivityCustomDevicesBinding;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Comparator;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
//TODO: Require wifi connection so entries can be verified
|
import kotlin.Unit;
|
||||||
//TODO: Resolve to ip address and don't allow unresolvable or duplicates based on ip address
|
|
||||||
//TODO: Sort the list
|
|
||||||
public class CustomDevicesActivity extends AppCompatActivity implements CustomDevicesAdapter.Callback {
|
public class CustomDevicesActivity extends AppCompatActivity implements CustomDevicesAdapter.Callback {
|
||||||
private static final String TAG_ADD_DEVICE_DIALOG = "AddDeviceDialog";
|
private static final String TAG_ADD_DEVICE_DIALOG = "AddDeviceDialog";
|
||||||
|
|
||||||
@ -45,7 +46,7 @@ public class CustomDevicesActivity extends AppCompatActivity implements CustomDe
|
|||||||
private RecyclerView recyclerView;
|
private RecyclerView recyclerView;
|
||||||
private TextView emptyListMessage;
|
private TextView emptyListMessage;
|
||||||
|
|
||||||
private ArrayList<String> customDeviceList;
|
private ArrayList<DeviceHost> customDeviceList;
|
||||||
private EditTextAlertDialogFragment addDeviceDialog;
|
private EditTextAlertDialogFragment addDeviceDialog;
|
||||||
private SharedPreferences sharedPreferences;
|
private SharedPreferences sharedPreferences;
|
||||||
private CustomDevicesAdapter customDevicesAdapter;
|
private CustomDevicesAdapter customDevicesAdapter;
|
||||||
@ -67,15 +68,19 @@ public class CustomDevicesActivity extends AppCompatActivity implements CustomDe
|
|||||||
Objects.requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true);
|
Objects.requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true);
|
||||||
getSupportActionBar().setDisplayShowHomeEnabled(true);
|
getSupportActionBar().setDisplayShowHomeEnabled(true);
|
||||||
|
|
||||||
fab.setOnClickListener(v -> showEditTextDialog(""));
|
fab.setOnClickListener(v -> showEditTextDialog(null));
|
||||||
|
|
||||||
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
|
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
|
||||||
|
|
||||||
customDeviceList = getCustomDeviceList(sharedPreferences);
|
customDeviceList = getCustomDeviceList(sharedPreferences);
|
||||||
|
customDeviceList.forEach(host -> host.checkReachable(() -> {
|
||||||
|
runOnUiThread(() -> customDevicesAdapter.notifyDataSetChanged());
|
||||||
|
return Unit.INSTANCE;
|
||||||
|
}));
|
||||||
|
|
||||||
showEmptyListMessageIfRequired();
|
showEmptyListMessageIfRequired();
|
||||||
|
|
||||||
customDevicesAdapter = new CustomDevicesAdapter(this);
|
customDevicesAdapter = new CustomDevicesAdapter(this, getApplicationContext());
|
||||||
customDevicesAdapter.setCustomDevices(customDeviceList);
|
customDevicesAdapter.setCustomDevices(customDeviceList);
|
||||||
|
|
||||||
recyclerView.setHasFixedSize(true);
|
recyclerView.setHasFixedSize(true);
|
||||||
@ -108,7 +113,11 @@ public class CustomDevicesActivity extends AppCompatActivity implements CustomDe
|
|||||||
emptyListMessage.setVisibility(customDeviceList.isEmpty() ? View.VISIBLE : View.GONE);
|
emptyListMessage.setVisibility(customDeviceList.isEmpty() ? View.VISIBLE : View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showEditTextDialog(@NonNull String text) {
|
private void showEditTextDialog(DeviceHost deviceHost) {
|
||||||
|
String text = "";
|
||||||
|
if (deviceHost != null) {
|
||||||
|
text = deviceHost.toString();
|
||||||
|
}
|
||||||
addDeviceDialog = new EditTextAlertDialogFragment.Builder()
|
addDeviceDialog = new EditTextAlertDialogFragment.Builder()
|
||||||
.setTitle(R.string.add_device_dialog_title)
|
.setTitle(R.string.add_device_dialog_title)
|
||||||
.setHint(R.string.add_device_hint)
|
.setHint(R.string.add_device_hint)
|
||||||
@ -129,30 +138,37 @@ public class CustomDevicesActivity extends AppCompatActivity implements CustomDe
|
|||||||
.apply();
|
.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ArrayList<String> deserializeIpList(String serialized) {
|
private static ArrayList<DeviceHost> deserializeIpList(String serialized) {
|
||||||
ArrayList<String> ipList = new ArrayList<>();
|
ArrayList<DeviceHost> ipList = new ArrayList<>();
|
||||||
|
|
||||||
if (!serialized.isEmpty()) {
|
if (!serialized.isEmpty()) {
|
||||||
Collections.addAll(ipList, serialized.split(IP_DELIM));
|
for (String ip: serialized.split(IP_DELIM)) {
|
||||||
|
DeviceHost deviceHost = DeviceHost.toDeviceHostOrNull(ip);
|
||||||
|
// To prevent crashes when migrating if invalid hosts are present
|
||||||
|
if (deviceHost != null) {
|
||||||
|
ipList.add(deviceHost);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ipList;
|
return ipList;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ArrayList<String> getCustomDeviceList(SharedPreferences sharedPreferences) {
|
public static ArrayList<DeviceHost> getCustomDeviceList(SharedPreferences sharedPreferences) {
|
||||||
String deviceListPrefs = sharedPreferences.getString(KEY_CUSTOM_DEVLIST_PREFERENCE, "");
|
String deviceListPrefs = sharedPreferences.getString(KEY_CUSTOM_DEVLIST_PREFERENCE, "");
|
||||||
|
ArrayList<DeviceHost> list = deserializeIpList(deviceListPrefs);
|
||||||
return deserializeIpList(deviceListPrefs);
|
list.sort(Comparator.comparing(DeviceHost::toString));
|
||||||
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCustomDeviceClicked(String customDevice) {
|
public void onCustomDeviceClicked(DeviceHost customDevice) {
|
||||||
editingDeviceAtPosition = customDeviceList.indexOf(customDevice);
|
editingDeviceAtPosition = customDeviceList.indexOf(customDevice);
|
||||||
showEditTextDialog(customDevice);
|
showEditTextDialog(customDevice);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCustomDeviceDismissed(String customDevice) {
|
public void onCustomDeviceDismissed(DeviceHost customDevice) {
|
||||||
lastDeletedCustomDevice = new DeletedCustomDevice(customDevice, customDeviceList.indexOf(customDevice));
|
lastDeletedCustomDevice = new DeletedCustomDevice(customDevice, customDeviceList.indexOf(customDevice));
|
||||||
customDeviceList.remove(lastDeletedCustomDevice.position);
|
customDeviceList.remove(lastDeletedCustomDevice.position);
|
||||||
customDevicesAdapter.notifyItemRemoved(lastDeletedCustomDevice.position);
|
customDevicesAdapter.notifyItemRemoved(lastDeletedCustomDevice.position);
|
||||||
@ -190,20 +206,45 @@ public class CustomDevicesActivity extends AppCompatActivity implements CustomDe
|
|||||||
public void onPositiveButtonClicked() {
|
public void onPositiveButtonClicked() {
|
||||||
if (addDeviceDialog.editText.getText() != null) {
|
if (addDeviceDialog.editText.getText() != null) {
|
||||||
String deviceNameOrIP = addDeviceDialog.editText.getText().toString().trim();
|
String deviceNameOrIP = addDeviceDialog.editText.getText().toString().trim();
|
||||||
|
DeviceHost host = DeviceHost.toDeviceHostOrNull(deviceNameOrIP);
|
||||||
|
|
||||||
// don't add empty string (after trimming)
|
// don't add empty string (after trimming)
|
||||||
if (!deviceNameOrIP.isEmpty() && !customDeviceList.contains(deviceNameOrIP)) {
|
if (host != null) {
|
||||||
|
if (!customDeviceList.stream().anyMatch(h -> h.toString().equals(host.toString()))) {
|
||||||
if (editingDeviceAtPosition >= 0) {
|
if (editingDeviceAtPosition >= 0) {
|
||||||
customDeviceList.set(editingDeviceAtPosition, deviceNameOrIP);
|
customDeviceList.set(editingDeviceAtPosition, host);
|
||||||
customDevicesAdapter.notifyItemChanged(editingDeviceAtPosition);
|
customDevicesAdapter.notifyItemChanged(editingDeviceAtPosition);
|
||||||
} else {
|
host.checkReachable(() -> {
|
||||||
customDeviceList.add(deviceNameOrIP);
|
runOnUiThread(() -> customDevicesAdapter.notifyItemChanged(editingDeviceAtPosition));
|
||||||
customDevicesAdapter.notifyItemInserted(customDeviceList.size() - 1);
|
return Unit.INSTANCE;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Find insertion position to ensure list remains sorted
|
||||||
|
int pos = 0;
|
||||||
|
while (customDeviceList.size() - 1 >= pos && customDeviceList.get(pos).toString().compareTo(host.toString()) < 0) {
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
final int position = pos;
|
||||||
|
|
||||||
|
customDeviceList.add(position, host);
|
||||||
|
customDevicesAdapter.notifyItemInserted(pos);
|
||||||
|
host.checkReachable(() -> {
|
||||||
|
runOnUiThread(() -> customDevicesAdapter.notifyItemChanged(position));
|
||||||
|
return Unit.INSTANCE;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
saveList();
|
saveList();
|
||||||
showEmptyListMessageIfRequired();
|
showEmptyListMessageIfRequired();
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
Toast.makeText(addDeviceDialog.getContext(), R.string.device_host_duplicate, Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Toast.makeText(addDeviceDialog.getContext(), R.string.device_host_invalid, Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -214,10 +255,10 @@ public class CustomDevicesActivity extends AppCompatActivity implements CustomDe
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static class DeletedCustomDevice {
|
private static class DeletedCustomDevice {
|
||||||
@NonNull String hostnameOrIP;
|
@NonNull DeviceHost hostnameOrIP;
|
||||||
int position;
|
int position;
|
||||||
|
|
||||||
DeletedCustomDevice(@NonNull String hostnameOrIP, int position) {
|
DeletedCustomDevice(@NonNull DeviceHost hostnameOrIP, int position) {
|
||||||
this.hostnameOrIP = hostnameOrIP;
|
this.hostnameOrIP = hostnameOrIP;
|
||||||
this.position = position;
|
this.position = position;
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
package org.kde.kdeconnect.UserInterface;
|
package org.kde.kdeconnect.UserInterface;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@ -16,21 +17,25 @@ import androidx.annotation.Nullable;
|
|||||||
import androidx.recyclerview.widget.ItemTouchHelper;
|
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import org.kde.kdeconnect.DeviceHost;
|
||||||
|
import org.kde.kdeconnect_tp.R;
|
||||||
import org.kde.kdeconnect_tp.databinding.CustomDeviceItemBinding;
|
import org.kde.kdeconnect_tp.databinding.CustomDeviceItemBinding;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
public class CustomDevicesAdapter extends RecyclerView.Adapter<CustomDevicesAdapter.ViewHolder> {
|
public class CustomDevicesAdapter extends RecyclerView.Adapter<CustomDevicesAdapter.ViewHolder> {
|
||||||
private ArrayList<String> customDevices;
|
private ArrayList<DeviceHost> customDevices;
|
||||||
private final Callback callback;
|
private final Callback callback;
|
||||||
|
private final Context context;
|
||||||
|
|
||||||
CustomDevicesAdapter(@NonNull Callback callback) {
|
CustomDevicesAdapter(@NonNull Callback callback, Context context) {
|
||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
|
this.context = context;
|
||||||
|
|
||||||
customDevices = new ArrayList<>();
|
customDevices = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setCustomDevices(ArrayList<String> customDevices) {
|
void setCustomDevices(ArrayList<DeviceHost> customDevices) {
|
||||||
this.customDevices = customDevices;
|
this.customDevices = customDevices;
|
||||||
|
|
||||||
notifyDataSetChanged();
|
notifyDataSetChanged();
|
||||||
@ -51,12 +56,13 @@ public class CustomDevicesAdapter extends RecyclerView.Adapter<CustomDevicesAdap
|
|||||||
CustomDeviceItemBinding itemBinding =
|
CustomDeviceItemBinding itemBinding =
|
||||||
CustomDeviceItemBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
|
CustomDeviceItemBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
|
||||||
|
|
||||||
return new ViewHolder(itemBinding);
|
return new ViewHolder(itemBinding, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||||
holder.bind(customDevices.get(position));
|
DeviceHost deviceHost = customDevices.get(position);
|
||||||
|
holder.bind(deviceHost.toString(), deviceHost.getPing());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -66,15 +72,29 @@ public class CustomDevicesAdapter extends RecyclerView.Adapter<CustomDevicesAdap
|
|||||||
|
|
||||||
class ViewHolder extends RecyclerView.ViewHolder implements SwipeableViewHolder {
|
class ViewHolder extends RecyclerView.ViewHolder implements SwipeableViewHolder {
|
||||||
private final CustomDeviceItemBinding itemBinding;
|
private final CustomDeviceItemBinding itemBinding;
|
||||||
|
private final Context context;
|
||||||
|
|
||||||
ViewHolder(@NonNull CustomDeviceItemBinding itemBinding) {
|
ViewHolder(@NonNull CustomDeviceItemBinding itemBinding, Context context) {
|
||||||
super(itemBinding.getRoot());
|
super(itemBinding.getRoot());
|
||||||
this.itemBinding = itemBinding;
|
this.itemBinding = itemBinding;
|
||||||
itemBinding.deviceNameOrIP.setOnClickListener(v -> callback.onCustomDeviceClicked(customDevices.get(getAdapterPosition())));
|
itemBinding.deviceNameOrIP.setOnClickListener(v -> callback.onCustomDeviceClicked(customDevices.get(getAdapterPosition())));
|
||||||
|
this.context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
void bind(String customDevice) {
|
void bind(String customDevice, DeviceHost.PingResult pingResult) {
|
||||||
itemBinding.deviceNameOrIP.setText(customDevice);
|
itemBinding.deviceNameOrIP.setText(customDevice);
|
||||||
|
if (pingResult != null) {
|
||||||
|
if (pingResult.getLatency() != null) {
|
||||||
|
String text = context.getString(R.string.ping_result, pingResult.getLatency());
|
||||||
|
itemBinding.connectionStatus.setText(text);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
itemBinding.connectionStatus.setText(R.string.ping_failed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
itemBinding.connectionStatus.setText(R.string.ping_in_progress);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -144,7 +164,7 @@ public class CustomDevicesAdapter extends RecyclerView.Adapter<CustomDevicesAdap
|
|||||||
}
|
}
|
||||||
|
|
||||||
public interface Callback {
|
public interface Callback {
|
||||||
void onCustomDeviceClicked(String customDevice);
|
void onCustomDeviceClicked(DeviceHost customDevice);
|
||||||
void onCustomDeviceDismissed(String customDevice);
|
void onCustomDeviceDismissed(DeviceHost customDevice);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user