Skip to content

Commit

Permalink
#291 Server list caching
Browse files Browse the repository at this point in the history
  • Loading branch information
dzolnai committed Aug 24, 2020
1 parent 47944cc commit 75c7232
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 6 deletions.
2 changes: 2 additions & 0 deletions app/src/main/java/nl/eduvpn/app/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,6 @@ public class Constants {

public static final Locale LOCALE = Locale.getDefault();
public static final Locale ENGLISH_LOCALE = Locale.ENGLISH;

public static final long SERVER_LIST_VALID_FOR_MS = 3_600_000; // 1 hour
}
56 changes: 56 additions & 0 deletions app/src/main/java/nl/eduvpn/app/service/PreferencesService.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import nl.eduvpn.app.entity.SavedAuthState;
import nl.eduvpn.app.entity.SavedKeyPair;
import nl.eduvpn.app.entity.SavedProfile;
import nl.eduvpn.app.entity.ServerList;
import nl.eduvpn.app.entity.Settings;
import nl.eduvpn.app.utils.Log;

Expand Down Expand Up @@ -74,6 +75,9 @@ public class PreferencesService {
@Deprecated
static final String KEY_INSTANCE_LIST_INSTITUTE_ACCESS = KEY_INSTANCE_LIST_PREFIX + "institute_access";

static final String KEY_SERVER_LIST_DATA = "server_list_data";
static final String KEY_SERVER_LIST_TIMESTAMP = "server_list_timestamp";


static final String KEY_SAVED_PROFILES = "saved_profiles";
static final String KEY_SAVED_AUTH_STATES = "saved_auth_state";
Expand Down Expand Up @@ -625,4 +629,56 @@ public void setPreferredCountry(@Nullable String preferredCountry) {
_getSharedPreferences().edit().putString(KEY_PREFERRED_COUNTRY, preferredCountry).apply();
}
}

/**
* Returns the server list if it is recent (see constants for exact TTL).
*
* @return The server list if it is recent, otherwise null.
*/
@Nullable
public ServerList getServerList() {
long timestamp = _getSharedPreferences().getLong(KEY_SERVER_LIST_TIMESTAMP, 0L);
if (System.currentTimeMillis() - timestamp < Constants.SERVER_LIST_VALID_FOR_MS) {
String serializedServerList = _getSharedPreferences().getString(KEY_SERVER_LIST_DATA, null);
if (serializedServerList == null) {
return null;
}
try {
return _serializerService.deserializeServerList(new JSONObject(serializedServerList));
} catch (Exception ex) {
Log.w(TAG, "Unable to parse server list!", ex);
return null;
}
} else {
_getSharedPreferences().edit()
.remove(KEY_SERVER_LIST_DATA)
.remove(KEY_SERVER_LIST_TIMESTAMP)
.apply();
return null;
}
}

/**
* Caches the server list. Only valid for a set amount, see constants for the exact TTL.
*
* @param serverList The server list to cache. Use null to remove previously set values.
*/
public void setServerList(@Nullable ServerList serverList) {
if (serverList == null) {
_getSharedPreferences().edit().remove(KEY_SERVER_LIST_DATA)
.remove(KEY_SERVER_LIST_TIMESTAMP)
.apply();
} else {
try {
String serializedServerList = _serializerService.serializeServerList(serverList).toString();
_getSharedPreferences().edit()
.putString(KEY_SERVER_LIST_DATA, serializedServerList)
.putLong(KEY_SERVER_LIST_TIMESTAMP, System.currentTimeMillis())
.apply();
} catch (Exception ex) {
Log.w(TAG, "Unable to set server list!");
}

}
}
}
30 changes: 26 additions & 4 deletions app/src/main/java/nl/eduvpn/app/service/SerializerService.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@

package nl.eduvpn.app.service;

import android.text.TextUtils;
import android.util.Pair;

import net.openid.appauth.AuthState;
Expand All @@ -30,7 +29,6 @@
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
Expand All @@ -40,7 +38,6 @@
import java.util.TimeZone;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import nl.eduvpn.app.Constants;
import nl.eduvpn.app.entity.AuthorizationType;
import nl.eduvpn.app.entity.DiscoveredAPI;
Expand Down Expand Up @@ -104,7 +101,7 @@ public JSONObject serializeProfileList(@NonNull List<Profile> profileList) throw
try {
result.put("profile_list", data);
data.put("data", array);
}catch (JSONException ex) {
} catch (JSONException ex) {
throw new UnknownFormatException("Unable to create nested object for serialized profile list!");
}
return result;
Expand Down Expand Up @@ -974,6 +971,30 @@ public ServerList deserializeServerList(JSONObject jsonObject) throws UnknownFor
}
}

/**
* Serializes the server list to JSON format
*
* @param serverList The server list to serialize.
* @return The server list as a JSON object.
* @throws UnknownFormatException Thrown if there was an error while deserializing.
*/
public JSONObject serializeServerList(ServerList serverList) throws UnknownFormatException {
try {
JSONObject output = new JSONObject();
JSONArray itemsList = new JSONArray();

output.put("server_list", itemsList);
output.put("v", serverList.getVersion());
for (int i = 0; i < serverList.getServerList().size(); ++i) {
JSONObject serializedItem = serializeInstance(serverList.getServerList().get(i));
itemsList.put(serializedItem);
}
return output;
} catch (JSONException ex) {
throw new UnknownFormatException(ex);
}
}

/**
* Returns the language of the user in a specific "country-language" format.
*
Expand All @@ -986,6 +1007,7 @@ private String _getUserLanguage() {

/**
* Retrieves the translations from a JSON object.
*
* @param translationsObject The JSON object to retrieve the translations from.
* @return A TranslatableString instance.
* @throws JSONException Thrown if the input is in an unexpected format.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,14 @@ class OrganizationSelectionViewModel @Inject constructor(
state.value = ConnectionState.FetchingServerList
Single.just(OrganizationList(-1L, emptyList()))
}
val cachedServerList = preferencesService.serverList
val getServerListCall = if (cachedServerList != null) {
Single.just(cachedServerList)
} else {
organizationService.fetchServerList()
}
disposables.add(
Single.zip(getOrganizationsCall, organizationService.fetchServerList(), BiFunction { orgList: OrganizationList, serverList: ServerList ->
Single.zip(getOrganizationsCall, getServerListCall, BiFunction { orgList: OrganizationList, serverList: ServerList ->
Pair(orgList, serverList)
}).subscribe({ organizationServerListPair ->
val organizationList = organizationServerListPair.first
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import nl.eduvpn.app.R
import nl.eduvpn.app.adapter.OrganizationAdapter
import nl.eduvpn.app.entity.AuthorizationType
import nl.eduvpn.app.entity.Instance
import nl.eduvpn.app.entity.Organization
import nl.eduvpn.app.entity.ServerList
import nl.eduvpn.app.service.APIService
import nl.eduvpn.app.service.ConnectionService
Expand Down Expand Up @@ -63,6 +62,9 @@ class ServerSelectionViewModel @Inject constructor(

init {
historyService.addObserver(this)
preferencesService.serverList?.let { serverList ->
serverListCache.value = Pair(System.currentTimeMillis(), serverList)
}
}

override fun onCleared() {
Expand Down Expand Up @@ -94,6 +96,7 @@ class ServerSelectionViewModel @Inject constructor(
.subscribe({
Log.v(TAG, "Updated server list with latest entries.")
serverListCache.value = Pair(System.currentTimeMillis(), it)
preferencesService.serverList = it
refreshInstances(it)
}, {
Log.w(TAG, "Unable to fetch server list. Trying to show servers without it.", it)
Expand Down

0 comments on commit 75c7232

Please sign in to comment.