2
0
mirror of https://github.com/KDE/kdeconnect-android synced 2025-08-30 21:55:10 +00:00

Merge branch 'albertvaka/trusted-networks'

# Conflicts:
#	AndroidManifest.xml
This commit is contained in:
Albert Vaca Cintora
2020-01-05 22:39:05 +01:00
11 changed files with 379 additions and 12 deletions

View File

@@ -16,6 +16,7 @@
android:name="android.hardware.telephony"
android:required="false" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.INTERNET" />
<!-- <uses-permission android:name="android.permission.BLUETOOTH" /> -->
@@ -29,12 +30,13 @@
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.READ_CALL_LOG"/>
<uses-permission android:name="android.permission.READ_CALL_LOG" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<application
android:icon="@drawable/icon"
@@ -276,8 +278,19 @@
android:name="android.support.PARENT_ACTIVITY"
android:value="org.kde.kdeconnect.UserInterface.PluginSettingsActivity" />
</activity>
<activity android:name="org.kde.kdeconnect.Plugins.PhotoPlugin.PhotoActivity" />
<activity
android:name="org.kde.kdeconnect.UserInterface.TrustedNetworksActivity"
android:label="@string/trusted_networks"
android:parentActivityName="org.kde.kdeconnect.UserInterface.MainActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.kde.kdeconnect.UserInterface.MainActivity" />
</activity>
</application>
</manifest>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M1,21h22L12,2 1,21zM13,18h-2v-2h2v2zM13,14h-2v-4h2v4z"/>
</vector>

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:drawablePadding="8dp"
android:paddingTop="16dp"
android:paddingBottom="12dp"
android:text="@string/on_non_trusted_message"
android:drawableStart="@drawable/ic_warning"
android:drawableTint="?attr/colorControlNormal"
android:clickable="false"
>
</TextView>

View File

@@ -0,0 +1,38 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin">
<CheckBox
android:text="@string/allow_all_networks_text"
android:layout_width="match_parent"
android:checked="true"
android:layout_height="wrap_content"
android:id="@+id/trust_all_networks_checkBox"/>
<TextView
android:id="@+id/trusted_network_list_empty"
android:layout_width="fill_parent"
android:layout_height="100dp"
android:text="@string/empty_trusted_networks_list_text"
android:gravity="center" />
<ListView
android:id="@android:id/list"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
<Button
android:id="@android:id/button1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:visibility="gone" />
</LinearLayout>

View File

@@ -14,4 +14,10 @@
android:title="@string/custom_device_list"
kdeconnect:showAsAction="never" />
<item
android:id="@+id/menu_trusted_networks"
android:orderInCategory="900"
android:title="@string/trusted_networks"
kdeconnect:showAsAction="never" />
</menu>

View File

@@ -257,6 +257,7 @@
<string name="refresh">Refresh</string>
<string name="unreachable_description">This paired device is not reachable. Make sure it is connected to your same network.</string>
<string name="no_wifi">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.</string>
<string name="on_non_trusted_message">Not on a trusted network: autodiscovery is disabled.</string>
<string name="no_file_browser">There are no file browsers installed.</string>
<string name="pref_plugin_telepathy">Send SMS</string>
<string name="pref_plugin_telepathy_desc">Send text messages from your desktop</string>
@@ -338,8 +339,17 @@
<string name="plugin_photo_desc">Launch the camera app to ease taking and transferring pictures</string>
<string name="findmyphone_preference_key_ringtone" translatable="false">findmyphone_ringtone</string>
<string name="no_app_for_opening">No suitable app found to open this file</string>
<string name="remote_keyboard_service">KDE Connect Remote Keyboard</string>
<string name="presenter_pointer">Pointer</string>
<string name="trusted_networks">Trusted networks</string>
<string name="trusted_networks_desc">Restrict autodiscovery to known networks</string>
<string name="add_trusted_network">Add %1s</string>
<string name="empty_trusted_networks_list_text">You haven\'t added any trusted network yet</string>
<string name="allow_all_networks_text">Allow all</string>
<string name="location_permission_needed_title">Permission required</string>
<string name="location_permission_needed_desc">Android requires the Location permission to identify your WiFi network</string>
</resources>

View File

@@ -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<String> 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();

View File

@@ -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 = "<unknown ssid>";
private final Context context;
public TrustedNetworkHelper(Context context) {
this.context = context;
}
public List<String> 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<String> 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());
}
}

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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<String> 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);
}
}
}