diff --git a/OsmAnd/res/layout-v14/osmo_groups_list_header.xml b/OsmAnd/res/layout-v14/osmo_groups_list_header.xml index 824415d276..abf588770a 100644 --- a/OsmAnd/res/layout-v14/osmo_groups_list_header.xml +++ b/OsmAnd/res/layout-v14/osmo_groups_list_header.xml @@ -84,6 +84,7 @@ android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_marginBottom="8dip" + android:autoLink="web" android:gravity="center" android:textSize="18sp" /> diff --git a/OsmAnd/res/layout/osmo_groups_list_header.xml b/OsmAnd/res/layout/osmo_groups_list_header.xml index 895e819a9c..72aa30c3ef 100644 --- a/OsmAnd/res/layout/osmo_groups_list_header.xml +++ b/OsmAnd/res/layout/osmo_groups_list_header.xml @@ -84,6 +84,7 @@ android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_marginBottom="8dip" + android:autoLink="web" android:gravity="center" android:textSize="18sp" /> diff --git a/OsmAnd/res/values-ru/strings.xml b/OsmAnd/res/values-ru/strings.xml index e6b7a4d4f1..38b7718f24 100644 --- a/OsmAnd/res/values-ru/strings.xml +++ b/OsmAnd/res/values-ru/strings.xml @@ -1,5 +1,8 @@ -Плагин позволяет загружать контурные линии и затемнение высот для использования оффлайн (\"Настройки\" → \"Управление файлами карт\" → \"Загрузить\"). + + Использовать безопасное соединение с сервером + Использовать https + Плагин позволяет загружать контурные линии и затемнение высот для использования оффлайн (\"Настройки\" → \"Управление файлами карт\" → \"Загрузить\"). Рассмотрите, пожалуйста, покупку плагина \"Линии высот\" в Google Play, чтобы поддержать последующую разработку. Линии высот Запись видео diff --git a/OsmAnd/res/values/strings.xml b/OsmAnd/res/values/strings.xml index 4b9bc5e9e6..2551a716a2 100644 --- a/OsmAnd/res/values/strings.xml +++ b/OsmAnd/res/values/strings.xml @@ -9,6 +9,13 @@ 3. All your modified/created strings are in the top of the file (to make easier find what\'s translated). PLEASE: Have a look at http://code.google.com/p/osmand/wiki/UIConsistency, it may really improve your and our work :-) Thx - Hardy --> + Use secure connection with server + Use https + Advanced + Bus, trolleybus, shuttle routes + Tram and train routes + Subway routes + %1$s needs this permission to turn off the screen for the power saving feature. Turn on the screen Turn on the phone screen when approaching a turn Select on map diff --git a/OsmAnd/src/net/osmand/plus/OsmandSettings.java b/OsmAnd/src/net/osmand/plus/OsmandSettings.java index ea25e90e19..3f23b82e32 100644 --- a/OsmAnd/src/net/osmand/plus/OsmandSettings.java +++ b/OsmAnd/src/net/osmand/plus/OsmandSettings.java @@ -57,6 +57,8 @@ public class OsmandSettings { void addListener(StateChangedListener listener); void removeListener(StateChangedListener listener); + + boolean isSet(); } private abstract class PreferenceWithListener implements OsmandPreference { @@ -168,6 +170,11 @@ public class OsmandSettings { set(ApplicationMode.DEFAULT); }; + @Override + public boolean isSet() { + return true; + }; + @Override public boolean set(ApplicationMode val) { ApplicationMode oldMode = currentMode; @@ -875,6 +882,9 @@ public class OsmandSettings { public final OsmandPreference OSMO_AUTO_CONNECT = new BooleanPreference("osmo_automatically_connect", false).makeGlobal(); + public final CommonPreference OSMO_USE_HTTPS = new BooleanPreference("osmo_use_https", + Build.VERSION.SDK_INT < 14/*Build.VERSION_CODES.ICE_CREAM_SANDWICH*/? false : true).makeGlobal(); + public final OsmandPreference OSMO_LAST_PING = new LongPreference("osmo_last_ping", 0).makeGlobal().cache(); public final OsmandPreference OSMO_AUTO_SEND_LOCATIONS = new BooleanPreference("osmo_automatically_send_locations", false).makeGlobal(); diff --git a/OsmAnd/src/net/osmand/plus/osmo/OsMoGroups.java b/OsmAnd/src/net/osmand/plus/osmo/OsMoGroups.java index 878e9a7dd5..505988115d 100644 --- a/OsmAnd/src/net/osmand/plus/osmo/OsMoGroups.java +++ b/OsmAnd/src/net/osmand/plus/osmo/OsMoGroups.java @@ -287,6 +287,9 @@ public class OsMoGroups implements OsMoReactor, OsmoTrackerListener { if (!mid.equals(trackerId)) { if (toDelete.contains(trackerId)) { toDelete.remove(trackerId); + OsMoDevice dv = mainGroup.users.get(trackerId); + dv.serverColor = device.userColor; + dv.serverName = device.userName; } else { mainGroup.users.put(trackerId, device); } @@ -478,7 +481,9 @@ public class OsMoGroups implements OsMoReactor, OsmoTrackerListener { } points.add(pt); } - plugin.getSaveGpxTask(gr.groupId + "_points", modify).execute(points.toArray(new WptPt[points.size()])); + if(points.size() > 0) { + plugin.getSaveGpxTask(gr.name + " points", modify, false).execute(points.toArray(new WptPt[points.size()])); + } } if(deleteUsers) { for(OsMoDevice s : toDelete.values()) { diff --git a/OsmAnd/src/net/osmand/plus/osmo/OsMoGroupsActivity.java b/OsmAnd/src/net/osmand/plus/osmo/OsMoGroupsActivity.java index b4beb259bb..c6bfa0e484 100644 --- a/OsmAnd/src/net/osmand/plus/osmo/OsMoGroupsActivity.java +++ b/OsmAnd/src/net/osmand/plus/osmo/OsMoGroupsActivity.java @@ -241,8 +241,11 @@ public class OsMoGroupsActivity extends OsmandExpandableListActivity implements mtd.setVisibility(visible? View.VISIBLE:View.GONE); if(visible) { mtd.setText(si.motd); + mtd.setLinksClickable(true); + mtd.setMovementMethod(LinkMovementMethod.getInstance()); } + CompoundButton login = (CompoundButton) header.findViewById(R.id.osmo_login_logoff); login.setChecked(osMoPlugin.getService().isLoggedIn()); login.setOnCheckedChangeListener(new LoginOnCheckedChangeListener()); diff --git a/OsmAnd/src/net/osmand/plus/osmo/OsMoGroupsStorage.java b/OsmAnd/src/net/osmand/plus/osmo/OsMoGroupsStorage.java index 891b76a5c9..b7fc60c071 100644 --- a/OsmAnd/src/net/osmand/plus/osmo/OsMoGroupsStorage.java +++ b/OsmAnd/src/net/osmand/plus/osmo/OsMoGroupsStorage.java @@ -1,10 +1,8 @@ package net.osmand.plus.osmo; -import java.security.acl.Group; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -75,6 +73,7 @@ public class OsMoGroupsStorage { public void load() { String grp = pref.get(); try { + System.out.println("DEBUG : OsMo groups load info " + grp); JSONObject obj = new JSONObject(grp); parseGroupUsers(mainGroup, obj); if(!obj.has(GROUPS)) { diff --git a/OsmAnd/src/net/osmand/plus/osmo/OsMoPlugin.java b/OsmAnd/src/net/osmand/plus/osmo/OsMoPlugin.java index f43f1fe9e0..904e3de1fd 100644 --- a/OsmAnd/src/net/osmand/plus/osmo/OsMoPlugin.java +++ b/OsmAnd/src/net/osmand/plus/osmo/OsMoPlugin.java @@ -67,12 +67,12 @@ public class OsMoPlugin extends OsmandPlugin implements MonitoringInfoControlSer public OsMoPlugin(final OsmandApplication app) { + this.app = app; service = new OsMoService(app, this); tracker = new OsMoTracker(service, app.getSettings().OSMO_SAVE_TRACK_INTERVAL, app.getSettings().OSMO_AUTO_SEND_LOCATIONS); new OsMoControlDevice(app, this, service, tracker); groups = new OsMoGroups(this, service, tracker, app); - this.app = app; ApplicationMode.regWidget("osmo_control", (ApplicationMode[])null); } @@ -85,6 +85,7 @@ public class OsMoPlugin extends OsmandPlugin implements MonitoringInfoControlSer return true; } + public OsMoGroupsActivity getGroupsActivity() { return groupsActivity; } @@ -229,8 +230,17 @@ public class OsMoPlugin extends OsmandPlugin implements MonitoringInfoControlSer if (si != null) { String uname = si.username; if (uname != null && uname.length() > 0) { - if (uname.length() > 7 && uname.indexOf(' ') != -1) { - uname = uname.substring(0, uname.indexOf(' ')); + if (uname.length() > 7) { + for(int k = 4; k < uname.length(); k++) { + if(!Character.isLetterOrDigit(uname.charAt(k)) && + uname.charAt(k) != '.' && uname.charAt(k) != '-') { + uname = uname.substring(0, k); + break; + } + } + if(uname.length() > 12) { + uname = uname.substring(0, 12); + } } if (uname.length() > 4) { txt = ""; @@ -350,11 +360,11 @@ public class OsMoPlugin extends OsmandPlugin implements MonitoringInfoControlSer return service; } - public AsyncTask getSaveGpxTask(final String name, final long timestamp) { + public AsyncTask getSaveGpxTask(final String name, final long timestamp, final boolean generateToast) { return new AsyncTask() { protected void onProgressUpdate(String... values) { - if (values != null) { + if (values != null && generateToast) { String t = ""; for (String s : values) { t += s + "\n"; @@ -381,7 +391,9 @@ public class OsMoPlugin extends OsmandPlugin implements MonitoringInfoControlSer if (errors == null) { errors = ""; } - publishProgress(app.getString(R.string.osmo_gpx_points_downloaded, name)); + if(generateToast) { + publishProgress(app.getString(R.string.osmo_gpx_points_downloaded, name)); + } } SelectedGpxFile byPath = app.getSelectedGpxHelper().getSelectedFileByPath(ps.getAbsolutePath()); if (byPath == null || changed) { @@ -396,7 +408,7 @@ public class OsMoPlugin extends OsmandPlugin implements MonitoringInfoControlSer @Override protected void onPostExecute(String result) { - if (result.length() > 0) { + if (result.length() > 0 && generateToast) { app.showToastMessage(app.getString(R.string.osmo_io_error) + result); } } @@ -525,4 +537,8 @@ public class OsMoPlugin extends OsmandPlugin implements MonitoringInfoControlSer } } + public boolean useHttps() { + return app.getSettings().OSMO_USE_HTTPS.get(); + } + } diff --git a/OsmAnd/src/net/osmand/plus/osmo/OsMoPositionLayer.java b/OsmAnd/src/net/osmand/plus/osmo/OsMoPositionLayer.java index e905d252af..da6be7c06c 100644 --- a/OsmAnd/src/net/osmand/plus/osmo/OsMoPositionLayer.java +++ b/OsmAnd/src/net/osmand/plus/osmo/OsMoPositionLayer.java @@ -8,7 +8,6 @@ import java.util.concurrent.ConcurrentLinkedQueue; import net.osmand.Location; import net.osmand.access.AccessibleToast; -import net.osmand.core.jni.ColorARGB; import net.osmand.data.LatLon; import net.osmand.data.RotatedTileBox; import net.osmand.plus.OsmAndFormatter; @@ -294,6 +293,7 @@ public class OsMoPositionLayer extends OsmandMapLayer implements ContextMenuLaye private static String followTrackerId; private static LatLon followMapLocation; private static String followDestinationId; + private static LatLon followTargetLocation; public static void setFollowTrackerId(OsMoDevice d) { if(d != null) { @@ -322,17 +322,29 @@ public class OsMoPositionLayer extends OsmandMapLayer implements ContextMenuLaye Location l = device.getLastLocation(); if(sameDestId && l != null) { TargetPointsHelper targets = map.getMyApplication().getTargetPointsHelper(); - RoutingHelper rh = map.getMyApplication().getRoutingHelper(); - double dist = 1; - if(rh.isRouteBeingCalculated()) { - dist = 100; - } else if(rh.isRouteCalculated()) { - dist = 30; - } - LatLon lt = new LatLon(l.getLatitude(), l.getLongitude()); final TargetPoint pn = targets.getPointToNavigate(); - if(pn == null || MapUtils.getDistance(pn.point, lt) > dist) { - targets.navigateToPoint(lt, true, -1); + LatLon lt = new LatLon(l.getLatitude(), l.getLongitude()); + boolean cancelDestinationId = false; + if(followTargetLocation != null ) { + if(pn == null || pn.point == null || !pn.point.equals(lt) ) { + cancelDestinationId = true; + } + } + if(cancelDestinationId) { + followTargetLocation = null; + followDestinationId = null; + } else { + RoutingHelper rh = map.getMyApplication().getRoutingHelper(); + double dist = 1; + if (rh.isRouteBeingCalculated()) { + dist = 100; + } else if (rh.isRouteCalculated()) { + dist = 30; + } + if (pn == null || MapUtils.getDistance(pn.point, lt) > dist) { + followTargetLocation = lt; + targets.navigateToPoint(lt, true, -1); + } } } diff --git a/OsmAnd/src/net/osmand/plus/osmo/OsMoService.java b/OsmAnd/src/net/osmand/plus/osmo/OsMoService.java index d94a3d65b3..c259b18c48 100644 --- a/OsmAnd/src/net/osmand/plus/osmo/OsMoService.java +++ b/OsmAnd/src/net/osmand/plus/osmo/OsMoService.java @@ -4,11 +4,20 @@ import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.math.BigInteger; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PublicKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.RSAPublicKeySpec; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.ConcurrentLinkedQueue; +import javax.crypto.Cipher; + import net.osmand.PlatformUtil; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; @@ -45,6 +54,12 @@ import android.support.v4.app.NotificationCompat; import android.text.TextUtils; public class OsMoService implements OsMoReactor { + private static final String HTTP_API_PREPARE = "http://api.osmo.mobi/prepare"; + private static final String HTTPS_API_PREPARE = "https://api.osmo.mobi/prepare"; + private static final String HTTP_AUTH = "http://api.osmo.mobi/auth"; + private static final String HTTPS_AUTH = "https://api.osmo.mobi/auth"; + private static final boolean USE_RSA_ENCRYPTION = false; + public static final String REGENERATE_CMD = "TRACKER_REGENERATE_ID"; public static final String SIGN_IN_URL = "http://osmo.mobi/signin?key="; private OsMoThread thread; @@ -61,6 +76,7 @@ public class OsMoService implements OsMoReactor { private boolean enabled = false; private BroadcastReceiver broadcastReceiver; private Notification notification; + public final static String OSMO_REGISTER_AGAIN = "OSMO_REGISTER_AGAIN"; //$NON-NLS-1$ private final static int SIMPLE_NOTFICATION_ID = 5; @@ -170,7 +186,7 @@ public class OsMoService implements OsMoReactor { public String registerOsmoDeviceKey() throws IOException { HttpClient httpclient = new DefaultHttpClient(); - HttpPost httppost = new HttpPost("http://api.osmo.mobi/auth"); + HttpPost httppost = new HttpPost(plugin.useHttps()? HTTPS_AUTH : HTTP_AUTH); try { // Add your data List nameValuePairs = new ArrayList(2); @@ -209,6 +225,7 @@ public class OsMoService implements OsMoReactor { public String token; public String uid; public String username; + // after auth public String protocol = ""; public String groupTrackerId; @@ -217,6 +234,8 @@ public class OsMoService implements OsMoReactor { public long motdTimestamp; public String motd = ""; + public Cipher clientEncCypher; + public Cipher clientDecCypher; } public SessionInfo getCurrentSessionInfo() { @@ -258,12 +277,33 @@ public class OsMoService implements OsMoReactor { deviceKey = registerOsmoDeviceKey(); } HttpClient httpclient = new DefaultHttpClient(); - HttpPost httppost = new HttpPost("http://api.osmo.mobi/prepare"); + KeyPair getMsgPair = null; + if (plugin.useHttps() && USE_RSA_ENCRYPTION) { + try { + KeyPairGenerator rsaGen = KeyPairGenerator.getInstance("RSA"); + getMsgPair = rsaGen.generateKeyPair(); + } catch (Exception e1) { + if (thread != null) { + thread.exc("Private key can't be generated", e1); + } else { + e1.printStackTrace(); + } + } + } + HttpPost httppost = new HttpPost(plugin.useHttps()? HTTPS_API_PREPARE : HTTP_API_PREPARE); try { // Add your data List nameValuePairs = new ArrayList(2); nameValuePairs.add(new BasicNameValuePair("app", Version.getFullVersion(app))); nameValuePairs.add(new BasicNameValuePair("key", deviceKey)); + if(getMsgPair != null && getMsgPair.getPublic() instanceof RSAPublicKey) { + nameValuePairs.add(new BasicNameValuePair("encAlgorithm", "RSA")); + BigInteger modulus = ((RSAPublicKey) getMsgPair.getPublic()).getModulus(); + BigInteger pe = ((RSAPublicKey) getMsgPair.getPublic()).getPublicExponent(); + nameValuePairs.add(new BasicNameValuePair("encClientPublicKey1", modulus.toString())); + nameValuePairs.add(new BasicNameValuePair("encClientPublicKey2", pe.toString())); + } + if(app.getSettings().OSMO_USER_PWD.get() != null) { nameValuePairs.add(new BasicNameValuePair("auth", app.getSettings().OSMO_USER_PWD.get())); } @@ -304,6 +344,23 @@ public class OsMoService implements OsMoReactor { si.hostName = a.substring(0, i); si.port = a.substring(i + 1); si.token = obj.getString("token"); + try { + if(getMsgPair != null && obj.has("encServerPublicKey1")) { + si.clientEncCypher = Cipher.getInstance("RSA"); + PublicKey pk = KeyFactory.getInstance("RSA").generatePublic(new RSAPublicKeySpec(new BigInteger(obj.getString("encServerPublicKey1")), + new BigInteger(obj.getString("encServerPublicKey2")))); + si.clientEncCypher.init(Cipher.ENCRYPT_MODE, pk); + + si.clientDecCypher = Cipher.getInstance("RSA"); + si.clientDecCypher.init(Cipher.DECRYPT_MODE, getMsgPair.getPrivate()); + } + } catch (Exception e) { + if (thread != null) { + thread.exc("Error exchanging private keys", e); + } else { + e.printStackTrace(); + } + } return si; } catch (ClientProtocolException e) { throw new IOException(e); diff --git a/OsmAnd/src/net/osmand/plus/osmo/OsMoThread.java b/OsmAnd/src/net/osmand/plus/osmo/OsMoThread.java index 62dab6bd3f..b7357b3c95 100644 --- a/OsmAnd/src/net/osmand/plus/osmo/OsMoThread.java +++ b/OsmAnd/src/net/osmand/plus/osmo/OsMoThread.java @@ -17,7 +17,11 @@ import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.ConcurrentLinkedQueue; +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; + import net.osmand.PlatformUtil; +import net.osmand.osm.io.Base64; import net.osmand.plus.osmo.OsMoService.SessionInfo; import org.apache.commons.logging.Log; @@ -261,6 +265,15 @@ public class OsMoThread { while ((i = readCommand.indexOf('\n')) != -1) { String cmd = readCommand.substring(0, i); readCommand = readCommand.substring(i + 1); + if(sessionInfo != null && sessionInfo.clientDecCypher != null) { + try { + final byte[] inMsg = android.util.Base64.decode(cmd.getBytes(), android.util.Base64.DEFAULT); + final byte[] byts = sessionInfo.clientDecCypher.doFinal(inMsg); + cmd = new String(byts); + } catch (Exception e) { + exc("Error decrypting", e); + } + } queueOfMessages.add(cmd.replace("\\n", "\n")); } } @@ -449,15 +462,23 @@ public class OsMoThread { private String prepareCommand(String l) { StringBuilder res = new StringBuilder(l.length()); - for(int i = 0; i < l.length(); i++) { + for (int i = 0; i < l.length(); i++) { char c = l.charAt(i); - if(c == '\n' || c == '=' || c == '\\') { - res.append('\\'); + if (c == '\n' || c == '=' || c == '\\') { + res.append('\\'); } res.append(c); } - return res.toString().trim() + "=\n"; + String finalCmd = res.toString().trim(); + if(sessionInfo != null && sessionInfo.clientEncCypher != null) { + try { + finalCmd = Base64.encode(sessionInfo.clientEncCypher.doFinal(finalCmd.getBytes())); + } catch (Exception e) { + exc("Error encrypting", e); + } + } + return finalCmd + "=\n"; } public long getLastCommandTime() { diff --git a/OsmAnd/src/net/osmand/plus/osmo/SettingsOsMoActivity.java b/OsmAnd/src/net/osmand/plus/osmo/SettingsOsMoActivity.java index 9f468f6992..8db22c0dd7 100644 --- a/OsmAnd/src/net/osmand/plus/osmo/SettingsOsMoActivity.java +++ b/OsmAnd/src/net/osmand/plus/osmo/SettingsOsMoActivity.java @@ -68,6 +68,11 @@ public class SettingsOsMoActivity extends SettingsBaseActivity { showGroupNotifiations.setSummary(R.string.osmo_show_group_notifications_descr); grp.addPreference(showGroupNotifiations); + CheckBoxPreference useHttps = createCheckBoxPreference(settings.OSMO_USE_HTTPS); + useHttps.setTitle(R.string.osmo_use_https); + useHttps.setSummary(R.string.osmo_use_https_descr); + grp.addPreference(useHttps); + if (OsmandPlugin.isDevelopment()) { debugPref = new Preference(this); debugPref.setTitle(R.string.osmo_settings_debug);