diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 2b854276..6577433f 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -16,6 +16,7 @@
android:name="android.hardware.telephony"
android:required="false" />
+
@@ -29,12 +30,13 @@
-
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/drawable/ic_warning.xml b/res/drawable/ic_warning.xml
new file mode 100644
index 00000000..d633ad87
--- /dev/null
+++ b/res/drawable/ic_warning.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/res/layout/pairing_explanation_not_trusted.xml b/res/layout/pairing_explanation_not_trusted.xml
new file mode 100644
index 00000000..7feb2e6f
--- /dev/null
+++ b/res/layout/pairing_explanation_not_trusted.xml
@@ -0,0 +1,16 @@
+
+
+
+
diff --git a/res/layout/trusted_network_list.xml b/res/layout/trusted_network_list.xml
new file mode 100644
index 00000000..5bd6fe61
--- /dev/null
+++ b/res/layout/trusted_network_list.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/menu/pairing.xml b/res/menu/pairing.xml
index 453dad6a..30a274d4 100644
--- a/res/menu/pairing.xml
+++ b/res/menu/pairing.xml
@@ -14,4 +14,10 @@
android:title="@string/custom_device_list"
kdeconnect:showAsAction="never" />
+
+
diff --git a/res/values/strings.xml b/res/values/strings.xml
index cfb81742..cd600692 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -257,6 +257,7 @@
Refresh
This paired device is not reachable. Make sure it is connected to your same network.
You\'re not connected to a Wi-Fi network, so you may not be able to see any devices. Click here to enable Wi-Fi.
+ Not on a trusted network: autodiscovery is disabled.
There are no file browsers installed.
Send SMS
Send text messages from your desktop
@@ -338,8 +339,17 @@
Launch the camera app to ease taking and transferring pictures
findmyphone_ringtone
+
No suitable app found to open this file
KDE Connect Remote Keyboard
Pointer
+ Trusted networks
+ Restrict autodiscovery to known networks
+ Add %1s
+ You haven\'t added any trusted network yet
+ Allow all
+
+ Permission required
+ Android requires the Location permission to identify your WiFi network
diff --git a/src/org/kde/kdeconnect/Backends/LanBackend/LanLinkProvider.java b/src/org/kde/kdeconnect/Backends/LanBackend/LanLinkProvider.java
index 70c4fedf..ebf45f48 100644
--- a/src/org/kde/kdeconnect/Backends/LanBackend/LanLinkProvider.java
+++ b/src/org/kde/kdeconnect/Backends/LanBackend/LanLinkProvider.java
@@ -33,6 +33,7 @@ import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.Helpers.DeviceHelper;
import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper;
import org.kde.kdeconnect.Helpers.StringsHelper;
+import org.kde.kdeconnect.Helpers.TrustedNetworkHelper;
import org.kde.kdeconnect.NetworkPacket;
import org.kde.kdeconnect.UserInterface.CustomDevicesActivity;
@@ -367,7 +368,16 @@ public class LanLinkProvider extends BaseLinkProvider implements LanLink.LinkDis
new Thread(() -> {
ArrayList iplist = CustomDevicesActivity
.getCustomDeviceList(PreferenceManager.getDefaultSharedPreferences(context));
- iplist.add("255.255.255.255"); //Default: broadcast.
+
+ if (TrustedNetworkHelper.isTrustedNetwork(context)) {
+ iplist.add("255.255.255.255"); //Default: broadcast.
+ } else {
+ Log.i("LanLinkProvider", "Current network isn't trusted, not broadcasting");
+ }
+
+ if (iplist.isEmpty()) {
+ return;
+ }
NetworkPacket identity = NetworkPacket.createIdentityPacket(context);
int port = (tcpServer == null || !tcpServer.isBound()) ? MIN_PORT : tcpServer.getLocalPort();
diff --git a/src/org/kde/kdeconnect/Helpers/TrustedNetworkHelper.java b/src/org/kde/kdeconnect/Helpers/TrustedNetworkHelper.java
new file mode 100644
index 00000000..040ca644
--- /dev/null
+++ b/src/org/kde/kdeconnect/Helpers/TrustedNetworkHelper.java
@@ -0,0 +1,93 @@
+package org.kde.kdeconnect.Helpers;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import android.Manifest;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.net.wifi.SupplicantState;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.preference.PreferenceManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.core.content.ContextCompat;
+
+import org.kde.kdeconnect.UserInterface.PermissionsAlertDialogFragment;
+import org.kde.kdeconnect_tp.R;
+
+public class TrustedNetworkHelper {
+
+ private static final String KEY_CUSTOM_TRUSTED_NETWORKS = "trusted_network_preference";
+ private static final String KEY_CUSTOM_TRUST_ALL_NETWORKS = "trust_all_network_preference";
+ private static final String NETWORK_SSID_DELIMITER = "#_#";
+ private static final String NOT_AVAILABLE_SSID_RESULT = "";
+
+
+ private final Context context;
+
+ public TrustedNetworkHelper(Context context) {
+ this.context = context;
+ }
+
+ public List read() {
+ String serializeTrustedNetwork = PreferenceManager.getDefaultSharedPreferences(context).getString(
+ KEY_CUSTOM_TRUSTED_NETWORKS, "");
+ if (serializeTrustedNetwork.isEmpty())
+ return Collections.emptyList();
+ return Arrays.asList(serializeTrustedNetwork.split(NETWORK_SSID_DELIMITER));
+ }
+
+ public void update(List trustedNetworks) {
+ String serialized = TextUtils.join(NETWORK_SSID_DELIMITER, trustedNetworks);
+ PreferenceManager.getDefaultSharedPreferences(context).edit().putString(
+ KEY_CUSTOM_TRUSTED_NETWORKS, serialized).apply();
+ }
+
+ public boolean allAllowed() {
+ if (!hasPermissions()) {
+ return true;
+ }
+ return PreferenceManager
+ .getDefaultSharedPreferences(context)
+ .getBoolean(KEY_CUSTOM_TRUST_ALL_NETWORKS, Boolean.TRUE);
+ }
+
+ public void allAllowed(boolean isChecked) {
+ PreferenceManager
+ .getDefaultSharedPreferences(context)
+ .edit()
+ .putBoolean(KEY_CUSTOM_TRUST_ALL_NETWORKS, isChecked)
+ .apply();
+ }
+
+ public boolean hasPermissions() {
+ int result = ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION);
+ return (result == PackageManager.PERMISSION_GRANTED);
+ }
+
+ public String currentSSID() {
+ WifiManager wifiManager = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
+ if (wifiManager == null) return "";
+ WifiInfo wifiInfo = wifiManager.getConnectionInfo();
+ if (wifiInfo.getSupplicantState() != SupplicantState.COMPLETED) {
+ return "";
+ }
+ String ssid = wifiInfo.getSSID();
+ if (ssid.equalsIgnoreCase(NOT_AVAILABLE_SSID_RESULT)){
+ return "";
+ }
+ return ssid;
+ }
+
+ public static boolean isTrustedNetwork(Context context) {
+ TrustedNetworkHelper trustedNetworkHelper = new TrustedNetworkHelper(context);
+ if (trustedNetworkHelper.allAllowed()){
+ return true;
+ }
+ return trustedNetworkHelper.read().contains(trustedNetworkHelper.currentSSID());
+ }
+}
diff --git a/src/org/kde/kdeconnect/UserInterface/PairingFragment.java b/src/org/kde/kdeconnect/UserInterface/PairingFragment.java
index 7411ae64..2f4d5c23 100644
--- a/src/org/kde/kdeconnect/UserInterface/PairingFragment.java
+++ b/src/org/kde/kdeconnect/UserInterface/PairingFragment.java
@@ -41,8 +41,13 @@ import android.view.ViewGroup;
import android.widget.ListView;
import android.widget.TextView;
+import androidx.annotation.NonNull;
+import androidx.fragment.app.Fragment;
+import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
+
import org.kde.kdeconnect.BackgroundService;
import org.kde.kdeconnect.Device;
+import org.kde.kdeconnect.Helpers.TrustedNetworkHelper;
import org.kde.kdeconnect.UserInterface.List.ListAdapter;
import org.kde.kdeconnect.UserInterface.List.PairingDeviceItem;
import org.kde.kdeconnect.UserInterface.List.SectionItem;
@@ -51,10 +56,6 @@ import org.kde.kdeconnect_tp.R;
import java.util.ArrayList;
import java.util.Collection;
-import androidx.annotation.NonNull;
-import androidx.fragment.app.Fragment;
-import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
-
/**
* The view that the user will see when there are no devices paired, or when you choose "add a new device" from the sidebar.
@@ -72,6 +73,7 @@ public class PairingFragment extends Fragment implements PairingDeviceItem.Callb
private TextView headerText;
private TextView noWifiHeader;
+ private TextView notTrustedText;
private Object networkChangeListener;
@Override
@@ -91,6 +93,10 @@ public class PairingFragment extends Fragment implements PairingDeviceItem.Callb
mSwipeRefreshLayout.setOnRefreshListener(
this::updateComputerListAction
);
+
+ notTrustedText = (TextView) inflater.inflate(R.layout.pairing_explanation_not_trusted, null);
+ notTrustedText.setOnClickListener(null);
+ notTrustedText.setOnLongClickListener(null);
headerText = (TextView) inflater.inflate(R.layout.pairing_explanation_text, null);
headerText.setOnClickListener(null);
headerText.setOnLongClickListener(null);
@@ -179,12 +185,16 @@ public class PairingFragment extends Fragment implements PairingDeviceItem.Callb
((ListView) rootView.findViewById(R.id.devices_list)).removeHeaderView(headerText);
((ListView) rootView.findViewById(R.id.devices_list)).removeHeaderView(noWifiHeader);
-
+ ((ListView) rootView.findViewById(R.id.devices_list)).removeHeaderView(notTrustedText);
ConnectivityManager connManager = (ConnectivityManager) getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo wifi = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
//Check if we're on Wi-Fi. If we still see a device, don't do anything special
if (someDevicesReachable || wifi.isConnected()) {
- ((ListView) rootView.findViewById(R.id.devices_list)).addHeaderView(headerText);
+ if (TrustedNetworkHelper.isTrustedNetwork(getContext())) {
+ ((ListView) rootView.findViewById(R.id.devices_list)).addHeaderView(headerText);
+ } else {
+ ((ListView) rootView.findViewById(R.id.devices_list)).addHeaderView(notTrustedText);
+ }
} else {
((ListView) rootView.findViewById(R.id.devices_list)).addHeaderView(noWifiHeader);
}
@@ -299,6 +309,10 @@ public class PairingFragment extends Fragment implements PairingDeviceItem.Callb
case R.id.menu_custom_device_list:
startActivity(new Intent(mActivity, CustomDevicesActivity.class));
break;
+ case R.id.menu_trusted_networks:
+ startActivity(new Intent(mActivity, TrustedNetworksActivity.class));
+ break;
+
default:
break;
}
diff --git a/src/org/kde/kdeconnect/UserInterface/SettingsFragment.java b/src/org/kde/kdeconnect/UserInterface/SettingsFragment.java
index 3e91547c..e2c66992 100644
--- a/src/org/kde/kdeconnect/UserInterface/SettingsFragment.java
+++ b/src/org/kde/kdeconnect/UserInterface/SettingsFragment.java
@@ -70,10 +70,6 @@ public class SettingsFragment extends PreferenceFragmentCompat {
screen.addPreference(renameDevice);
-
- //TODO: Trusted wifi networks settings should go here
-
-
// Dark mode
final TwoStatePreference darkThemeSwitch = new SwitchPreferenceCompat(context);
darkThemeSwitch.setPersistent(false);
@@ -127,6 +123,30 @@ public class SettingsFragment extends PreferenceFragmentCompat {
screen.addPreference(notificationSwitch);
}
+
+ // Trusted Networks
+ Preference trustedNetworkPref = new Preference(context);
+ trustedNetworkPref.setPersistent(false);
+ trustedNetworkPref.setTitle(R.string.trusted_networks);
+ trustedNetworkPref.setSummary(R.string.trusted_networks_desc);
+ screen.addPreference(trustedNetworkPref);
+ trustedNetworkPref.setOnPreferenceClickListener(preference -> {
+ startActivity(new Intent(context, TrustedNetworksActivity.class));
+ return true;
+ });
+
+ // Add device by IP
+ Preference devicesByIpPreference = new Preference(context);
+ devicesByIpPreference.setPersistent(false);
+ devicesByIpPreference.setTitle(R.string.custom_device_list);
+ screen.addPreference(devicesByIpPreference);
+ devicesByIpPreference.setOnPreferenceClickListener(preference -> {
+
+ startActivity(new Intent(context, CustomDevicesActivity.class));
+ return true;
+ });
+
+
// More settings text
Preference moreSettingsText = new Preference(context);
moreSettingsText.setPersistent(false);
diff --git a/src/org/kde/kdeconnect/UserInterface/TrustedNetworksActivity.java b/src/org/kde/kdeconnect/UserInterface/TrustedNetworksActivity.java
new file mode 100644
index 00000000..e6ad33b8
--- /dev/null
+++ b/src/org/kde/kdeconnect/UserInterface/TrustedNetworksActivity.java
@@ -0,0 +1,138 @@
+package org.kde.kdeconnect.UserInterface;
+
+import android.Manifest;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.ListView;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.app.AppCompatActivity;
+
+import org.kde.kdeconnect.Helpers.TrustedNetworkHelper;
+import org.kde.kdeconnect_tp.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class TrustedNetworksActivity extends AppCompatActivity {
+
+ private List trustedNetworks;
+
+ private ListView trustedNetworksView;
+ private CheckBox allowAllCheckBox;
+ private TrustedNetworkHelper trustedNetworkHelper;
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+ boolean grantedPermission = false;
+ for (int result : grantResults) {
+ if (result == PackageManager.PERMISSION_GRANTED) {
+ grantedPermission = true;
+ break;
+ }
+ }
+ if (grantedPermission) {
+ allowAllCheckBox.setChecked(false);
+ }
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+
+ ThemeUtil.setUserPreferredTheme(this);
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.trusted_network_list);
+ trustedNetworksView = findViewById(android.R.id.list);
+
+ trustedNetworkHelper = new TrustedNetworkHelper(getApplicationContext());
+ trustedNetworks = new ArrayList<>(trustedNetworkHelper.read());
+
+ allowAllCheckBox = findViewById(R.id.trust_all_networks_checkBox);
+ allowAllCheckBox.setOnCheckedChangeListener((v, isChecked) -> {
+
+ if (trustedNetworkHelper.hasPermissions()) {
+ trustedNetworkHelper.allAllowed(isChecked);
+ updateTrustedNetworkListView();
+ addNetworkButton();
+ } else {
+ allowAllCheckBox.setChecked(true); // Disable unchecking it
+ new PermissionsAlertDialogFragment.Builder()
+ .setTitle(R.string.location_permission_needed_title)
+ .setMessage(R.string.location_permission_needed_desc)
+ .setPositiveButton(R.string.ok)
+ .setNegativeButton(R.string.cancel)
+ .setPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION})
+ .setRequestCode(0)
+ .create().show(getSupportFragmentManager(), null);
+ }
+ });
+ allowAllCheckBox.setChecked(trustedNetworkHelper.allAllowed());
+
+ updateTrustedNetworkListView();
+ }
+
+ private void updateEmptyListMessage() {
+ boolean isVisible = trustedNetworks.isEmpty() && !trustedNetworkHelper.allAllowed();
+ findViewById(R.id.trusted_network_list_empty)
+ .setVisibility(isVisible ? View.VISIBLE : View.GONE );
+ }
+
+ private void updateTrustedNetworkListView() {
+ Boolean allAllowed = trustedNetworkHelper.allAllowed();
+ updateEmptyListMessage();
+ trustedNetworksView.setVisibility(allAllowed ? View.GONE : View.VISIBLE);
+ if (allAllowed){
+ return;
+ }
+ trustedNetworksView.setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, trustedNetworks));
+ trustedNetworksView.setOnItemClickListener((parent, view, position, id) -> {
+ String targetItem = trustedNetworks.get(position);
+ new AlertDialog.Builder(TrustedNetworksActivity.this)
+ .setMessage("Delete " + targetItem + " ?")
+ .setPositiveButton("Yes", (dialog, which) -> {
+ trustedNetworks.remove(position);
+ trustedNetworkHelper.update(trustedNetworks);
+ ((ArrayAdapter) trustedNetworksView.getAdapter()).notifyDataSetChanged();
+ addNetworkButton();
+ updateEmptyListMessage();
+ })
+ .setNegativeButton("No", null)
+ .show();
+
+ });
+ addNetworkButton();
+ }
+
+
+ private void addNetworkButton() {
+ Button addButton = findViewById(android.R.id.button1);
+ if (trustedNetworkHelper.allAllowed()) {
+ addButton.setVisibility(View.GONE);
+ return;
+ }
+ final String currentSSID = trustedNetworkHelper.currentSSID();
+ if (!currentSSID.isEmpty() && trustedNetworks.indexOf(currentSSID) == -1) {
+ String buttonText = getString(R.string.add_trusted_network, currentSSID);
+ addButton.setText(buttonText);
+ addButton.setOnClickListener(v -> {
+ if (trustedNetworks.indexOf(currentSSID) != -1){
+ return;
+ }
+ trustedNetworks.add(currentSSID);
+ trustedNetworkHelper.update(trustedNetworks);
+ ((ArrayAdapter) trustedNetworksView.getAdapter()).notifyDataSetChanged();
+ v.setVisibility(View.GONE);
+ updateEmptyListMessage();
+ });
+ addButton.setVisibility(View.VISIBLE);
+ } else {
+ addButton.setVisibility(View.GONE);
+ }
+ }
+
+}