Commit 5c472635 authored by Drahomír Karchňák's avatar Drahomír Karchňák

WIP - Server selection.

parent c12f7ebd
......@@ -71,8 +71,12 @@
<activity android:label="@string/history_button_sync" android:name="at.alladin.rmbt.android.sync.RMBTSyncActivity"
android:theme="@style/PreferenceTheme"
android:enabled="true" />
android:enabled="true" />
<activity android:label="@string/user_server_selection" android:name="at.alladin.rmbt.android.preferences.RMBTServerSelectActivity"
android:theme="@style/PreferenceTheme"
android:enabled="true" />
<service android:enabled="true" android:name="at.alladin.rmbt.android.test.RMBTService" android:exported="false" />
<service android:enabled="true" android:name="at.alladin.rmbt.android.test.RMBTLoopService" android:exported="false" />
......
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<fragment
android:name="at.alladin.rmbt.android.preferences.RMBTServerSelectFragment"
android:id="@+id/server_select_fragment"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</FrameLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="44dp"
android:text="TextView"
android:textAlignment="center"
android:textColor="@color/black"
android:textSize="24sp"
tools:text="[TEST] Vyber server [Test]" />
<ListView
android:id="@+id/server_select_list"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="6dip"
android:id="@+id/item_text"
android:textColor="@color/black"
android:textSize="24sp" />
\ No newline at end of file
......@@ -32,7 +32,11 @@
<string name="preferences_location_settings">Poloha</string>
<string name="preferences_location_settings_sum">Změny nastavení polohy</string>
<string name="user_server_selection">Výběr serveru</string>
<string name="preferences_user_server_selection">Vybrat server</string>
<string name="preferences_user_server_selection_sub">Automaticky vybrat nejlepší</string>
<string name="preferences_no_gps">Bez GPS</string>
<string name="preferences_no_gps_sum">Nepoužívat GPS během testu</string>
......
......@@ -34,7 +34,11 @@
<string name="preferences_location_settings">Location</string>
<string name="preferences_location_settings_sum">Change location settings</string>
<string name="user_server_selection">Server selection</string>
<string name="preferences_user_server_selection">Choose server</string>
<string name="preferences_user_server_selection_sum">Automatically choose the best</string>
<string name="preferences_no_gps">No GPS</string>
<string name="preferences_no_gps_sum">Do not use GPS during test</string>
......
......@@ -23,7 +23,13 @@
android:title="@string/preferences_no_gps"
android:summary="@string/preferences_no_gps_sum"
android:layout="@layout/preferences_checkbox_item"/>
<Preference
android:key="user_server_selection_button"
android:title="@string/preferences_user_server_selection"
android:summary="@string/preferences_user_server_selection_sum"
android:layout="@layout/preferences_item" />
<EditTextPreference
android:key="tag"
android:title="@string/preferences_tag"
......
......@@ -50,7 +50,6 @@ import at.alladin.rmbt.android.util.ConfigHelper;
import at.alladin.rmbt.android.util.RMBTTermsFragment;
import cz.nic.netmetr.R;
//import cz.nic.netmetr.R;
/**
*
......
......@@ -36,6 +36,7 @@ public class AppConstants {
public final static String PAGE_TITLE_HISTORY_FILTER = "history_filter";
public final static String PAGE_TITLE_HISTORY_PAGER = "history_pager";
public final static String PAGE_TITLE_SYNC = "sync";
public final static String PAGE_TITLE_SERVER_SELECT = "server_select";
public final static String PAGE_TITLE_ABOUT = "about";
public final static String PAGE_TITLE_RESULT_DETAIL = "result_detail";
public final static String PAGE_TITLE_RESULT_QOS = "result_detail_expanded";
......@@ -69,6 +70,7 @@ public class AppConstants {
TITLE_MAP.put(PAGE_TITLE_NDT_CHECK, R.string.terms);
TITLE_MAP.put(PAGE_TITLE_NETSTAT, R.string.menu_button_netstat);
TITLE_MAP.put(PAGE_TITLE_CHECK_INFORMATION_COMMISSIONER, R.string.page_title_information_commissioner);
TITLE_MAP.put(PAGE_TITLE_SERVER_SELECT, R.string.user_server_selection); //TODO
}
/**
......
......@@ -28,6 +28,8 @@ import android.preference.PreferenceActivity;
import android.preference.PreferenceCategory;
import android.view.MenuItem;
import android.widget.ListView;
import android.widget.Toast;
import at.alladin.rmbt.android.main.FeatureConfig;
import at.alladin.rmbt.android.sync.RMBTSyncActivity;
import at.alladin.rmbt.android.terms.RMBTCheckFragment.CheckType;
......@@ -106,19 +108,7 @@ public class RMBTPreferenceActivity extends PreferenceActivity
final ListView v = getListView();
v.setCacheColorHint(0);
//final float scale = getResources().getDisplayMetrics().density;
//final int padding = Helperfunctions.dpToPx(10, scale);
//final ViewGroup vg = (ViewGroup) v.getRootView();
//vg.setPadding(padding, padding, padding, padding);
//final int paddingTopBottom = Helperfunctions.dpToPx(3, scale);
//final int paddingLeftRight = Helperfunctions.dpToPx(10, scale);
//v.setBackgroundResource(R.drawable.box_large);
//v.setBackgroundResource(R.drawable.app_bgdn_radiant);
//v.setPadding(paddingLeftRight, paddingTopBottom, paddingLeftRight, paddingTopBottom);
final Preference ndtPref = (Preference) findPreference("ndt");
if (ndtPref != null)
{
......@@ -173,7 +163,7 @@ public class RMBTPreferenceActivity extends PreferenceActivity
}
}
final Preference gpsPref = (Preference) findPreference("location_settings");
final Preference gpsPref = findPreference("location_settings");
if (gpsPref != null)
{
gpsPref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
......@@ -186,7 +176,7 @@ public class RMBTPreferenceActivity extends PreferenceActivity
});
}
final Preference syncPref = (Preference) findPreference("sync_settings");
final Preference syncPref = findPreference("sync_settings");
if (syncPref != null)
{
syncPref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
......@@ -209,10 +199,21 @@ public class RMBTPreferenceActivity extends PreferenceActivity
}
});
}
// v.setScrollBarStyle(View.SCROLLBARS_OUTSIDE_INSET);
// addPreferencesFromResource(R.xml.preferences);
addPreferencesFromResource(R.xml.preferences_dev);
final Preference serverSelectButton = findPreference("user_server_selection_button");
if(serverSelectButton != null) {
serverSelectButton.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
System.out.println("Preference clicked!");
startActivity(new Intent(getBaseContext(), RMBTServerSelectActivity.class));
return true;
}
});
}
}
@Override
......
package at.alladin.rmbt.android.preferences;
import android.app.Activity;
import android.os.Bundle;
import cz.nic.netmetr.R;
public class RMBTServerSelectActivity extends Activity{
@Override
protected void onCreate(Bundle savedInstanceState) {
System.out.println("Activity on create");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_server_select);
}
}
package at.alladin.rmbt.android.preferences;
import android.app.Fragment;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import cz.nic.netmetr.R;
public class RMBTServerSelectFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.server_select_fragment, container, false);
ListView listView = (ListView) view.findViewById(R.id.server_select_list);
ArrayList<String> arrayList = new ArrayList<String>();
arrayList.add("Vybrat server automaticky");
arrayList.add("Server 1");
arrayList.add("Server 2");
arrayList.add("Server 3");
arrayList.add("Server 4");
arrayList.add("Server 5");
arrayList.add("Server 6");
arrayList.add("Server 7");
arrayList.add("Server 8");
arrayList.add("Server 9");
arrayList.add("Server 10");
arrayList.add("Server 11");
arrayList.add("Server 12");
arrayList.add("Server 13");
arrayList.add("Server 14");
arrayList.add("Server 15");
final StableArrayAdapter arrayAdapter = new StableArrayAdapter(this.getActivity(), R.layout.server_select_item, arrayList);
listView.setAdapter(arrayAdapter);
return view;
}
private class StableArrayAdapter extends ArrayAdapter<String> {
HashMap<String, Integer> mIdMap = new HashMap<String, Integer>();
public StableArrayAdapter(Context context, int textViewResourceId,
List<String> objects) {
super(context, textViewResourceId, objects);
for (int i = 0; i < objects.size(); ++i) {
mIdMap.put(objects.get(i), i);
}
}
@Override
public long getItemId(int position) {
String item = getItem(position);
return mIdMap.get(item);
}
@Override
public boolean hasStableIds() {
return true;
}
}
}
\ No newline at end of file
......@@ -18,6 +18,8 @@ package at.alladin.rmbt.android.util;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentMap;
import org.json.JSONArray;
......@@ -219,6 +221,30 @@ public class CheckSettingsTask extends AsyncTask<Void, Void, JSONArray>
activity.setHistoryDirty(true);
// Servers
final Set<String> serverSet;
final JSONArray servers = resultListItem.optJSONArray("servers");
if (servers == null)
serverSet = null;
else
{
serverSet = new TreeSet<String>();
for (int i = 0; i < servers.length(); i++)
{
final JSONObject serverObj = (JSONObject) servers.get(i);
final String serverName = serverObj.getString("name");
final String serverUuid = serverObj.getString("uuid");
final Server server = new Server(serverName, serverUuid);
serverSet.add(server.encode());
System.out.println("server: " + serverName + " " + serverUuid);
}
}
ConfigHelper.setServers(activity, serverSet);
}
catch (final JSONException e)
{
......
......@@ -20,6 +20,7 @@ import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicBoolean;
......@@ -593,4 +594,28 @@ public final class ConfigHelper
{
return PackageManager.SIGNATURE_MATCH == ctx.getPackageManager().checkSignatures(ctx.getPackageName(), at.alladin.rmbt.android.util.Config.RMBT_DEV_UNLOCK_PACKAGE_NAME);
}
public static boolean isUserServerSelectionActive(final Context ctx) {
return getSharedPreferences(ctx).getBoolean("user_server_selection", false);
}
public static void setServerSelectionState(final Context ctx, final boolean isEnabled) {
getSharedPreferences(ctx).edit().putBoolean("user_server_selection", isEnabled).apply();
}
public static Set<String> getServers(final Context context)
{
return getSharedPreferences(context).getStringSet("servers", null);
}
public static void setServers(final Context context, final Set<String> servers)
{
final Set<String> oldServers = getServers(context);
if (oldServers == null && servers == null)
return;
if (oldServers != null && servers != null
&& servers.equals(oldServers))
return;
getSharedPreferences(context).edit().putStringSet("servers", servers).apply();
}
}
......@@ -580,7 +580,8 @@ public class ControlServerConnection
requestData.put("version_code", clientVersionCode);
requestData.put("time", time);
requestData.put("client_secret", ClientSecretHelper.prepareClientSecret(getUUID(), clientName, clientVersionName, clientVersionCode, time, BuildConfig.CLIENT_SECRET));
requestData.put("user_server_selection", true); //TODO
final int tcAcceptedVersion = ConfigHelper.getTCAcceptedVersion(context);
requestData.put("terms_and_conditions_accepted_version", tcAcceptedVersion);
if (tcAcceptedVersion > 0) // for server backward compatibility
......
package at.alladin.rmbt.android.util;
import android.util.Base64;
public class Server
{
protected final String name;
protected final String uuid;
public Server(String name, String uuid)
{
super();
this.name = name;
this.uuid = uuid;
}
public String encode()
{
return Base64.encodeToString(name.getBytes(), Base64.DEFAULT) + ";" + uuid;
}
public static Server decode(String data)
{
final String[] split = data.split(";");
final String name = new String(Base64.decode(split[0], Base64.DEFAULT));
final String uuid = split[1];
return new Server(name, uuid);
}
public String getName()
{
return name;
}
public String getUuid()
{
return uuid;
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((uuid == null) ? 0 : uuid.hashCode());
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Server other = (Server) obj;
if (name == null)
{
if (other.name != null)
return false;
}
else if (!name.equals(other.name))
return false;
if (uuid == null)
{
if (other.uuid != null)
return false;
}
else if (!uuid.equals(other.uuid))
return false;
return true;
}
}
......@@ -56,6 +56,7 @@ public class RegistrationResource extends ServerResource
int port;
String address;
String key;
String type;
}
......@@ -253,9 +254,22 @@ public class RegistrationResource extends ServerResource
ipv6 = false;
else // should never happen, unless ipv > 6 is available
ipv6 = null;
final TestServer server = getNearestServer(errorList, geolat, geolong, geotime, clientIpString,
asCountry, geoIpCountry, serverType, testServerEncryption, ipv6);
TestServer server = null;
final Boolean userServerSelection = request.optBoolean("user_server_selection");
if(userServerSelection) {
final String preferServer = request.optString("prefer_server", null);
if(!Strings.isNullOrEmpty(preferServer)); {
server = getPreferredServer(preferServer, testServerEncryption, ipv6);
}
}
if(server == null) {
server = getNearestServer(errorList, geolat, geolong, geotime, clientIpString,
asCountry, geoIpCountry, serverType, testServerEncryption, ipv6);
}
try
{
......@@ -274,6 +288,7 @@ public class RegistrationResource extends ServerResource
answer.put("test_server_port", server.port);
answer.put("test_server_name", server.name);
answer.put("test_server_encryption", testServerEncryption);
answer.put("test_server_type", server.type);
answer.put("test_duration", getSetting("rmbt_duration"));
answer.put("test_numthreads", getSetting("rmbt_num_threads"));
......@@ -534,72 +549,66 @@ public class RegistrationResource extends ServerResource
{
return request(entity);
}
/**
* @param geolat
* @param geolong
* @param geotime
* @param clientIp
* @return
*/
private TestServer getPreferredServer(final String uuid, final boolean ssl, final Boolean ipv6)
{
final String sql = "SELECT * FROM test_server"
+ " WHERE active"
+ " AND uuid = ?::uuid"
+ " LIMIT 1";
try (PreparedStatement ps = conn.prepareStatement(sql))
{
ps.setString(1, uuid);
try (ResultSet rs = ps.executeQuery())
{
if (!rs.next())
return null;
return toTestServer(rs, ssl, ipv6);
}
}
catch (SQLException e)
{
e.printStackTrace();
return null;
}
}
private TestServer getNearestServer(final ErrorList errorList, final double geolat, final double geolong,
final long geotime, final String clientIp, final String asCountry, final String geoIpCountry, final String serverType,
final boolean ssl, final Boolean ipv6)
{
// TODO find nearest Server to GeoLocation or IP address
final String sql = "SELECT * FROM test_server"
+ " WHERE active"
+ " AND server_type = ?"
+ " AND (country = ? OR country = 'any' OR country IS NULL)"
+ " ORDER BY"
+ " (country != 'any' AND country IS NOT NULL) DESC,"
+ " AND (server_type = ?)"
+ " AND ( ? = ANY (countries) OR 'any' = ANY (countries))"
+ " ORDER BY 'any' != ANY (countries) DESC,"
+ " priority,"
+ " random() * weight DESC"
+ " LIMIT 1";
//+ " ST_Distance(ST_TRANSFORM(ST_SetSRID(ST_Point(?, ?), 4326), 900913))";??
//+ " ST_Distance(ST_TRANSFORM(ST_SetSRID(ST_Point(?, ?), 4326), 900913))";??
try (PreparedStatement ps = conn.prepareStatement(sql))
{
// use geoIP with fallback to AS
String country = asCountry;
if (! Strings.isNullOrEmpty(geoIpCountry))
country = geoIpCountry;
int i = 1;
ps.setString(i++, serverType);
ps.setString(i++, country);
try (ResultSet rs = ps.executeQuery())
{
if (! rs.next())
return null;
final String address;
if (ipv6 == null)
address = rs.getString("web_address");
else if (ipv6)
address = rs.getString("web_address_ipv6");
else
address = rs.getString("web_address_ipv4");
final TestServer result = new TestServer();
result.id = rs.getInt("uid");
result.address = address;
result.port = rs.getInt(ssl ? "port_ssl" : "port");
result.name = rs.getString("name") + " (" + rs.getString("city") + ")";
result.key = rs.getString("key");
if(result.key == null || result.key.isEmpty()) {
System.out.println("Couldn't find key in database. Using fallback server secret key!");
result.key = getContext().getParameters().getFirstValue("RMBT_SECRETKEY");
}
return result;
return toTestServer(rs, ssl, ipv6);
}
}
catch (SQLException e)
......@@ -607,7 +616,27 @@ public class RegistrationResource extends ServerResource
e.printStackTrace();
return null;
}
}
private static TestServer toTestServer(final ResultSet rs, final boolean ssl, final Boolean ipv6) throws SQLException {
final String address;
if (ipv6 == null)
address = rs.getString("web_address");
else if (ipv6)
address = rs.getString("web_address_ipv6");
else
address = rs.getString("web_address_ipv4");
final TestServer result = new TestServer();
result.id = rs.getInt("uid");
result.address = address;
result.port = rs.getInt(ssl ? "port_ssl" : "port");
result.name = rs.getString("name") + " (" + rs.getString("city") + ")";
result.key = rs.getString("key");
result.type = rs.getString("server_type");
return result;
}
}
......@@ -311,49 +311,49 @@ public class ResultResource extends ServerResource
final JSONObject geoDataItem = geoData.getJSONObject(i);
if (geoDataItem.optLong("tstamp", 0) != 0 && geoDataItem.optDouble("geo_lat", 0) != 0 && geoDataItem.optDouble("geo_long", 0) != 0) {
final GeoLocation geoloc = new GeoLocation(conn);
geoloc.setTest_id(test.getUid());
final long clientTime = geoDataItem.optLong("tstamp");
final Timestamp tstamp = java.sql.Timestamp.valueOf(new Timestamp(
clientTime).toString());
geoloc.setTime(tstamp, test.getField("timezone").toString());
geoloc.setAccuracy((float) geoDataItem.optDouble("accuracy", 0));
geoloc.setAltitude(geoDataItem.optDouble("altitude", 0));
geoloc.setBearing((float) geoDataItem.optDouble("bearing", 0));
geoloc.setSpeed((float) geoDataItem.optDouble("speed", 0));
geoloc.setProvider(geoDataItem.optString("provider", ""));
geoloc.setGeo_lat(geoDataItem.optDouble("geo_lat", 0));
geoloc.setGeo_long(geoDataItem.optDouble("geo_long", 0));
geoloc.setTime_ns(geoDataItem.optLong("time_ns", 0));
geoloc.storeLocation();
// Store Last Geolocation as
// Testlocation
if (i == geoData.length() - 1)
{
if (geoDataItem.has("geo_lat"))
test.getField("geo_lat").setField(geoDataItem);
if (geoDataItem.has("geo_long"))
test.getField("geo_long").setField(geoDataItem);
if (geoDataItem.has("accuracy"))
test.getField("geo_accuracy").setField(geoDataItem);