2
0
mirror of https://github.com/KDE/kdeconnect-android synced 2025-08-22 01:51:47 +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:
Achilleas Koutsou 2014-11-20 23:47:43 -08:00 committed by Albert Vaca
parent e3dfc69ca1
commit 7283caa09c
7 changed files with 234 additions and 15 deletions

View File

@ -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"

View File

@ -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(
try { KEY_CUSTOM_DEVLIST_PREFERENCE, "");
NetworkPackage identity = NetworkPackage.createIdentityPackage(context); ArrayList<String> iplist = new ArrayList<String>();
identity.set("tcpPort",finalTcpPort); if (!deviceListPrefs.isEmpty()) {
byte[] b = identity.serialize().getBytes("UTF-8"); iplist = CustomDevicesActivity.deserializeIpList(deviceListPrefs);
DatagramPacket packet = new DatagramPacket(b, b.length, InetAddress.getByAddress(new byte[]{-1,-1,-1,-1}), port); }
DatagramSocket socket = new DatagramSocket(); iplist.add("255.255.255.255");
socket.setReuseAddress(true); for (String ipstr : iplist) {
socket.setBroadcast(true); try {
socket.send(packet); InetAddress client = InetAddress.getByName(ipstr);
//Log.e("LanLinkProvider","Udp identity package sent"); NetworkPackage identity = NetworkPackage.createIdentityPackage(context);
} catch(Exception e) { identity.set("tcpPort", finalTcpPort);
e.printStackTrace(); byte[] b = identity.serialize().getBytes("UTF-8");
Log.e("LanLinkProvider","Sending udp identity package failed"); DatagramPacket packet = new DatagramPacket(b, b.length, client, port);
DatagramSocket socket = new DatagramSocket();
socket.setReuseAddress(true);
socket.setBroadcast(true);
socket.send(packet);
//Log.i("LanLinkProvider","Udp identity package sent to address "+packet.getAddress());
} catch(Exception e) {
e.printStackTrace();
Log.e("LanLinkProvider","Sending udp identity package failed. Invalid address? ("+ipstr+")");
}
} }
return null; return null;

View File

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

View File

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

View 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>

View File

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

View File

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