mirror of
https://github.com/KDE/kdeconnect-android
synced 2025-08-22 09:58:08 +00:00
Manual IP connection
This patch adds a new menu to add and remove IP addresses and hostnames, and makes LanBackend read that list of addresses and send identity packets to them. This bypasses the autodiscovery which usually happens via broadcast packets, and allows the pairing and communication of devices even if broadcast is not possible between them. BUG: 326509 REVIEW: 121104
This commit is contained in:
parent
e3dfc69ca1
commit
7283caa09c
@ -88,6 +88,17 @@
|
|||||||
android:value="org.kde.kdeconnect.UserInterface.DeviceActivity" />
|
android:value="org.kde.kdeconnect.UserInterface.DeviceActivity" />
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name="org.kde.kdeconnect.UserInterface.CustomDevicesActivity"
|
||||||
|
android:label="@string/custom_device_list"
|
||||||
|
android:parentActivityName="org.kde.kdeconnect.UserInterface.DeviceActivity"
|
||||||
|
>
|
||||||
|
<meta-data android:name="android.support.PARENT_ACTIVITY"
|
||||||
|
android:value="org.kde.kdeconnect.UserInterface.DeviceActivity" />
|
||||||
|
</activity>
|
||||||
|
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name="org.kde.kdeconnect.UserInterface.PluginSettingsActivity"
|
android:name="org.kde.kdeconnect.UserInterface.PluginSettingsActivity"
|
||||||
android:label="@string/mpris_settings"
|
android:label="@string/mpris_settings"
|
||||||
|
@ -22,8 +22,9 @@ package org.kde.kdeconnect.Backends.LanBackend;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.util.Log;
|
import android.preference.PreferenceManager;
|
||||||
import android.support.v4.util.LongSparseArray;
|
import android.support.v4.util.LongSparseArray;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import org.apache.mina.core.future.ConnectFuture;
|
import org.apache.mina.core.future.ConnectFuture;
|
||||||
import org.apache.mina.core.future.IoFuture;
|
import org.apache.mina.core.future.IoFuture;
|
||||||
@ -39,16 +40,19 @@ import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
|
|||||||
import org.apache.mina.transport.socket.nio.NioSocketConnector;
|
import org.apache.mina.transport.socket.nio.NioSocketConnector;
|
||||||
import org.kde.kdeconnect.Backends.BaseLinkProvider;
|
import org.kde.kdeconnect.Backends.BaseLinkProvider;
|
||||||
import org.kde.kdeconnect.NetworkPackage;
|
import org.kde.kdeconnect.NetworkPackage;
|
||||||
|
import org.kde.kdeconnect.UserInterface.CustomDevicesActivity;
|
||||||
|
|
||||||
import java.net.DatagramPacket;
|
import java.net.DatagramPacket;
|
||||||
import java.net.DatagramSocket;
|
import java.net.DatagramSocket;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
public class LanLinkProvider extends BaseLinkProvider {
|
public class LanLinkProvider extends BaseLinkProvider {
|
||||||
|
|
||||||
|
public static final String KEY_CUSTOM_DEVLIST_PREFERENCE = "device_list_preference";
|
||||||
private final static int port = 1714;
|
private final static int port = 1714;
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
@ -261,20 +265,29 @@ public class LanLinkProvider extends BaseLinkProvider {
|
|||||||
new AsyncTask<Void,Void,Void>() {
|
new AsyncTask<Void,Void,Void>() {
|
||||||
@Override
|
@Override
|
||||||
protected Void doInBackground(Void... voids) {
|
protected Void doInBackground(Void... voids) {
|
||||||
|
String deviceListPrefs = PreferenceManager.getDefaultSharedPreferences(context).getString(
|
||||||
|
KEY_CUSTOM_DEVLIST_PREFERENCE, "");
|
||||||
|
ArrayList<String> iplist = new ArrayList<String>();
|
||||||
|
if (!deviceListPrefs.isEmpty()) {
|
||||||
|
iplist = CustomDevicesActivity.deserializeIpList(deviceListPrefs);
|
||||||
|
}
|
||||||
|
iplist.add("255.255.255.255");
|
||||||
|
for (String ipstr : iplist) {
|
||||||
try {
|
try {
|
||||||
|
InetAddress client = InetAddress.getByName(ipstr);
|
||||||
NetworkPackage identity = NetworkPackage.createIdentityPackage(context);
|
NetworkPackage identity = NetworkPackage.createIdentityPackage(context);
|
||||||
identity.set("tcpPort",finalTcpPort);
|
identity.set("tcpPort", finalTcpPort);
|
||||||
byte[] b = identity.serialize().getBytes("UTF-8");
|
byte[] b = identity.serialize().getBytes("UTF-8");
|
||||||
DatagramPacket packet = new DatagramPacket(b, b.length, InetAddress.getByAddress(new byte[]{-1,-1,-1,-1}), port);
|
DatagramPacket packet = new DatagramPacket(b, b.length, client, port);
|
||||||
DatagramSocket socket = new DatagramSocket();
|
DatagramSocket socket = new DatagramSocket();
|
||||||
socket.setReuseAddress(true);
|
socket.setReuseAddress(true);
|
||||||
socket.setBroadcast(true);
|
socket.setBroadcast(true);
|
||||||
socket.send(packet);
|
socket.send(packet);
|
||||||
//Log.e("LanLinkProvider","Udp identity package sent");
|
//Log.i("LanLinkProvider","Udp identity package sent to address "+packet.getAddress());
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
Log.e("LanLinkProvider","Sending udp identity package failed");
|
Log.e("LanLinkProvider","Sending udp identity package failed. Invalid address? ("+ipstr+")");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -0,0 +1,142 @@
|
|||||||
|
package org.kde.kdeconnect.UserInterface;
|
||||||
|
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
import android.app.ListActivity;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.KeyEvent;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.inputmethod.EditorInfo;
|
||||||
|
import android.view.inputmethod.InputMethodManager;
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.ListView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import org.kde.kdeconnect_tp.R;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
public class CustomDevicesActivity extends ListActivity {
|
||||||
|
|
||||||
|
private static final String LOG_ID = "CustomDevicesActivity";
|
||||||
|
public static final String KEY_CUSTOM_DEVLIST_PREFERENCE = "device_list_preference";
|
||||||
|
private static final String IP_DELIM = ",";
|
||||||
|
|
||||||
|
ArrayList<String> ipAddressList = new ArrayList<String>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
initializeDeviceList(this);
|
||||||
|
setContentView(R.layout.custom_ip_list);
|
||||||
|
setListAdapter(new ArrayAdapter(this, android.R.layout.simple_list_item_1, ipAddressList));
|
||||||
|
|
||||||
|
|
||||||
|
EditText ipEntryBox = (EditText)findViewById(R.id.ip_edittext);
|
||||||
|
ipEntryBox.setOnEditorActionListener(new TextView.OnEditorActionListener() {
|
||||||
|
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
||||||
|
if (actionId == EditorInfo.IME_ACTION_SEND) {
|
||||||
|
addNewIp(v);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onListItemClick(ListView l, View v, final int position, final long id) {
|
||||||
|
Log.i(LOG_ID, "Item clicked pos: "+position+" id: "+id);
|
||||||
|
// remove touched item after confirmation
|
||||||
|
DialogInterface.OnClickListener confirmationListener = new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
switch (which) {
|
||||||
|
case DialogInterface.BUTTON_POSITIVE:
|
||||||
|
ipAddressList.remove(position);
|
||||||
|
Log.i(LOG_ID, "Removed item pos: "+position+" id: "+id);
|
||||||
|
saveList();
|
||||||
|
break;
|
||||||
|
case DialogInterface.BUTTON_NEGATIVE:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||||
|
builder.setMessage("Delete "+ipAddressList.get(position)+" ?");
|
||||||
|
builder.setPositiveButton("Yes", confirmationListener);
|
||||||
|
builder.setNegativeButton("No", confirmationListener);
|
||||||
|
builder.show();
|
||||||
|
((ArrayAdapter)getListAdapter()).notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addNewIp(View v) {
|
||||||
|
EditText ipEntryBox = (EditText)findViewById(R.id.ip_edittext);
|
||||||
|
String enteredText = ipEntryBox.getText().toString().trim();
|
||||||
|
if (!enteredText.equals("")) {
|
||||||
|
// don't add empty string (after trimming)
|
||||||
|
ipAddressList.add(enteredText);
|
||||||
|
}
|
||||||
|
|
||||||
|
saveList();
|
||||||
|
// clear entry box
|
||||||
|
ipEntryBox.setText("");
|
||||||
|
InputMethodManager inputManager = (InputMethodManager)
|
||||||
|
getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||||
|
|
||||||
|
inputManager.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(),
|
||||||
|
InputMethodManager.HIDE_NOT_ALWAYS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void saveList() {
|
||||||
|
// add entry to list and save to preferences (unless empty)
|
||||||
|
String serialized = "";
|
||||||
|
if (!ipAddressList.isEmpty()) {
|
||||||
|
serialized = serializeIpList(ipAddressList);
|
||||||
|
}
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(this).edit().putString(
|
||||||
|
KEY_CUSTOM_DEVLIST_PREFERENCE, serialized).commit();
|
||||||
|
((ArrayAdapter)getListAdapter()).notifyDataSetChanged();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static String serializeIpList(ArrayList<String> iplist) {
|
||||||
|
String serialized = "";
|
||||||
|
for (String ipaddr : iplist) {
|
||||||
|
serialized += IP_DELIM+ipaddr;
|
||||||
|
}
|
||||||
|
// remove first delimiter
|
||||||
|
serialized = serialized.substring(IP_DELIM.length());
|
||||||
|
Log.d(LOG_ID, serialized);
|
||||||
|
return serialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ArrayList<String> deserializeIpList(String serialized) {
|
||||||
|
ArrayList<String> iplist = new ArrayList<String>();
|
||||||
|
Log.d(LOG_ID, serialized);
|
||||||
|
for (String ipaddr : serialized.split(IP_DELIM)) {
|
||||||
|
iplist.add(ipaddr);
|
||||||
|
Log.d(LOG_ID, ipaddr);
|
||||||
|
}
|
||||||
|
return iplist;
|
||||||
|
}
|
||||||
|
|
||||||
|
void initializeDeviceList(Context context){
|
||||||
|
String deviceListPrefs = PreferenceManager.getDefaultSharedPreferences(context).getString(
|
||||||
|
KEY_CUSTOM_DEVLIST_PREFERENCE,
|
||||||
|
"");
|
||||||
|
if(deviceListPrefs.isEmpty()){
|
||||||
|
Log.i(LOG_ID, "Initialising empty custom device list");
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(context).edit().putString(
|
||||||
|
KEY_CUSTOM_DEVLIST_PREFERENCE,
|
||||||
|
deviceListPrefs).commit();
|
||||||
|
} else {
|
||||||
|
Log.i(LOG_ID, "Populating device list");
|
||||||
|
ipAddressList = deserializeIpList(deviceListPrefs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -89,6 +89,9 @@ public class MainActivity extends ActionBarActivity {
|
|||||||
case R.id.menu_settings:
|
case R.id.menu_settings:
|
||||||
startActivity(new Intent(this,MainSettingsActivity.class));
|
startActivity(new Intent(this,MainSettingsActivity.class));
|
||||||
break;
|
break;
|
||||||
|
case R.id.menu_custom_device_list:
|
||||||
|
startActivity(new Intent(this, CustomDevicesActivity.class));
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
36
src/main/res/layout/custom_ip_list.xml
Normal file
36
src/main/res/layout/custom_ip_list.xml
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:paddingLeft="@dimen/activity_horizontal_margin"
|
||||||
|
android:paddingRight="@dimen/activity_horizontal_margin"
|
||||||
|
android:paddingTop="@dimen/activity_vertical_margin"
|
||||||
|
android:paddingBottom="@dimen/activity_vertical_margin"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<ListView
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="fill_parent"
|
||||||
|
android:id="@android:id/list"
|
||||||
|
android:layout_weight="1"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="100dp"
|
||||||
|
android:text="@string/custom_dev_list_help"/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="@dimen/abc_action_bar_default_height"
|
||||||
|
android:imeOptions="actionSend"
|
||||||
|
android:id="@+id/ip_edittext"/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="@dimen/abc_action_bar_default_height"
|
||||||
|
android:text="@string/add_host"
|
||||||
|
android:onClick="addNewIp"
|
||||||
|
android:id="@android:id/button1"/>
|
||||||
|
|
||||||
|
|
||||||
|
</LinearLayout>
|
@ -24,4 +24,13 @@
|
|||||||
android:title="@string/settings"
|
android:title="@string/settings"
|
||||||
kdeconnect:showAsAction="never"
|
kdeconnect:showAsAction="never"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/menu_custom_device_list"
|
||||||
|
android:icon="@drawable/ic_action_settings"
|
||||||
|
android:orderInCategory="900"
|
||||||
|
android:title="Custom Device List"
|
||||||
|
kdeconnect:showAsAction="never"
|
||||||
|
/>
|
||||||
|
|
||||||
</menu>
|
</menu>
|
@ -98,6 +98,9 @@
|
|||||||
<string name="device_name_preference_summary">%s</string>
|
<string name="device_name_preference_summary">%s</string>
|
||||||
<string name="invalid_device_name">Invalid device name</string>
|
<string name="invalid_device_name">Invalid device name</string>
|
||||||
<string name="shareplugin_text_saved">Received text, saved to clipboard</string>
|
<string name="shareplugin_text_saved">Received text, saved to clipboard</string>
|
||||||
|
<string name="custom_devices_settings">Custom device list</string>
|
||||||
|
<string name="custom_device_list">Custom device list</string>
|
||||||
|
<string name="custom_device_list_summary">Custom device list</string>
|
||||||
<string name="share_notification_preference">Noisy notifications</string>
|
<string name="share_notification_preference">Noisy notifications</string>
|
||||||
<string name="share_notification_preference_summary">Vibrate and play a sound when receiving a file</string>
|
<string name="share_notification_preference_summary">Vibrate and play a sound when receiving a file</string>
|
||||||
<string name="sftp_internal_storage">Internal storage</string>
|
<string name="sftp_internal_storage">Internal storage</string>
|
||||||
@ -106,6 +109,8 @@
|
|||||||
<string name="sftp_sdcard">SD card</string>
|
<string name="sftp_sdcard">SD card</string>
|
||||||
<string name="sftp_readonly">(read only)</string>
|
<string name="sftp_readonly">(read only)</string>
|
||||||
<string name="sftp_camera">Camera pictures</string>
|
<string name="sftp_camera">Camera pictures</string>
|
||||||
|
<string name="add_host">Add host/IP</string>
|
||||||
|
<string name="custom_dev_list_help">Use this option only if your device is not automatically detected. Enter IP address or hostname below and touch the button to add it to the list. Touch an existing item to remove it from the list.</string>
|
||||||
|
|
||||||
<string-array name="mpris_time_entries">
|
<string-array name="mpris_time_entries">
|
||||||
<item>10 seconds</item>
|
<item>10 seconds</item>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user