Commit c887d453 authored by Roger Barton's avatar Roger Barton
Browse files

Refined settings and storage, changes to Events.java & AdditFields

Adding default additional fields
parent 3e7165e6
Pipeline #8539 passed with stages
in 2 minutes and 40 seconds
This is a sample of the "additional_fields" variable found in the json for an event
{
"$schema":"http://json-schema.org/draft-04/schema#",
"additionalProperties":false,
......
For POST request to signups, to send additional fields as part of the signup use this format
"additional_fields":
{
"SBB_Abo":"Halbtax",
"Food":"vegi"
}
\ No newline at end of file
......@@ -54,7 +54,7 @@ public class SettingsActivity extends AppCompatActivity {
Settings.SetBoolPref(Settings.checkin_autoUpdate, mAutoRefreshCheck.isChecked(), getApplicationContext());
Settings.SetFloatPref(Settings.checkin_refreshRate, MathUtils.clamp(Float.parseFloat(mRefreshFreqField.getText().toString()), 3f, Float.POSITIVE_INFINITY), getApplicationContext());
ReturnToMainActivity();
finish();
}
/**
......@@ -63,10 +63,4 @@ public class SettingsActivity extends AppCompatActivity {
public static int GetRefreshRateMillis(Context context) {
return (int)(1000 * Settings.GetFloatPref(Settings.checkin_refreshRate, context));
}
private void ReturnToMainActivity ()
{
Intent intent = new Intent(this, MainActivity.class);
startActivity(intent);
}
}
......@@ -2,6 +2,7 @@ package ch.amiv.android_app.core;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.support.v4.content.ContextCompat;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
......@@ -17,6 +18,7 @@ import android.widget.LinearLayout;
import android.widget.TextView;
import ch.amiv.android_app.R;
import ch.amiv.android_app.events.AdditField;
import ch.amiv.android_app.util.ui.NonSwipeableViewPager;
import ch.amiv.android_app.util.ui.EnumViewGenerator;
import ch.amiv.android_app.util.Util;
......@@ -286,6 +288,10 @@ public class IntroActivity extends AppCompatActivity {
private void StartMainAcivity() {
Settings.SetBoolPref(Settings.introDoneKey, true, this);
// TODO check if notifications are enabled in the settings
Notifications.set_Alarm(this);
startActivity(new Intent(this, MainActivity.class));
finish();
}
......@@ -367,7 +373,7 @@ public class IntroActivity extends AppCompatActivity {
}
};
EnumViewGenerator.InitialiseEnumList(this, R.string.pref_food_title, onClick, getResources().getStringArray(R.array.pref_food_list_values), true);
EnumViewGenerator.InitialiseEnumList(this, onClick, AdditField.Defaults.food, true);
btnNext.setOnClickListener(new View.OnClickListener() {
@Override
......@@ -398,7 +404,7 @@ public class IntroActivity extends AppCompatActivity {
}
};
EnumViewGenerator.InitialiseEnumList(this, R.string.pref_sbb_title, onClick, getResources().getStringArray(R.array.pref_sbb_list_values), false);
EnumViewGenerator.InitialiseEnumList(this, onClick, AdditField.Defaults.sbbAbo, false);
btnNext.setOnClickListener(new View.OnClickListener() {
@Override
......
......@@ -31,7 +31,6 @@ import ch.amiv.android_app.R;
import ch.amiv.android_app.events.EventDetailActivity;
import ch.amiv.android_app.events.Events;
import ch.amiv.android_app.jobs.JobDetailActivity;
import ch.amiv.android_app.util.PersistentStorage;
import ch.amiv.android_app.util.Util;
......@@ -123,16 +122,13 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
bottomNavigation = findViewById(R.id.bottomNav);
bottomNavigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);
PersistentStorage.LoadEvents(getApplicationContext());
PersistentStorage.LoadJobs(getApplicationContext());
Settings.LoadEvents(getApplicationContext());
Settings.LoadJobs(getApplicationContext());
InitialisePageView();
// TODO check if notifications are enabled in the settings
Notifications.set_Alarm(this);
//fetch the user info if we are logged in, there exists a token from the previous session, should be cached.
if(!PersistentStorage.LoadUserInfo(getApplicationContext()) || UserInfo.current._id.isEmpty() && !Settings.IsEmailOnlyLogin(getApplicationContext())) {
if(!Settings.LoadUserInfo(getApplicationContext()) || UserInfo.current._id.isEmpty() && !Settings.IsEmailOnlyLogin(getApplicationContext())) {
Request.FetchUserData(getApplicationContext(), drawerNav, new Request.OnDataReceivedCallback() {
@Override
public void OnDataReceived() {
......
......@@ -120,7 +120,7 @@ public final class Notifications {
// get event from JSONObject and add it to events list
JSONObject event = items.getJSONObject(0);
Events.AddEvent(event);
Events.AddEvent(event, context);
// refetch event list to add new event
String event_id = (String) event.get("_id");
......@@ -148,7 +148,7 @@ public final class Notifications {
String [] event_id = new String[items.length()];
for(int i = 0; i<items.length();i++){
JSONObject event = items.getJSONObject(i);
Events.AddEvent(event); // TODO efficiency !
Events.AddEvent(event, context); // TODO efficiency !
event_id[i]= (String) event.get("_id");
}
......
......@@ -7,9 +7,20 @@ import android.content.res.Resources;
import android.os.Vibrator;
import android.util.Pair;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import org.json.JSONObject;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Locale;
import ch.amiv.android_app.R;
import ch.amiv.android_app.events.EventInfo;
import ch.amiv.android_app.events.Events;
import ch.amiv.android_app.jobs.JobInfo;
import ch.amiv.android_app.jobs.Jobs;
/**
* This class is used to save settings so they can be restored in another session later.
......@@ -35,6 +46,11 @@ public class Settings {
public static final String[] specialFoodPrefKey = {"core.special_food_pref", ""};
public static final String[] sbbPrefKey = {"core.sbb_abo", ""};
//Storing of larger data
public static final String[] userInfoKey = {"core.user_info", ""};
public static final String[] eventInfoKey = {"events.event_infos", ""};
public static final String[] jobInfoKey = {"jobs.job_infos", ""};
//region---Check-in----
public static final String[] recentEventPin = {"checkin.recent_event_pin", ""};
public static final String[] checkin_url = {"checkin.serverurl", "https://checkin.amiv.ethz.ch"};
......@@ -55,6 +71,11 @@ public class Settings {
sharedPrefs = context.getSharedPreferences(SHARED_PREFS_KEY, Context.MODE_PRIVATE);
}
public static boolean HasKey(String[] key, Context context){
CheckInitSharedPrefs(context);
return sharedPrefs.contains(key[0]);
}
/**
* Will store the value in sharedpreferences to be restored in another session
* @param key A string[2] in the format (prefs key, defValue), use the Settings public vars
......@@ -87,6 +108,11 @@ public class Settings {
}
// Sidenote: Float prefs need to use a pair instead of a string[], so we have a float for the default value
public static boolean HasKey(Pair<String, Float> key, Context context){
CheckInitSharedPrefs(context);
return sharedPrefs.contains(key.first);
}
public static void SetFloatPref (Pair<String, Float> key, float value, Context context){
CheckInitSharedPrefs(context);
sharedPrefs.edit().putFloat(key.first, value).apply();
......@@ -139,7 +165,7 @@ public class Settings {
* Will return whether the user is only logged in with an email, if they do not have an api login, false if current user has not be initialised
*/
public static boolean IsEmailOnlyLogin(Context context){
return !Settings.HasToken(context) && UserInfo.current != null && !UserInfo.current.email.isEmpty();
return !HasToken(context) && UserInfo.current != null && !UserInfo.current.email.isEmpty();
}
/**
......@@ -195,4 +221,118 @@ public class Settings {
vibrator.cancel();
}
//endregion
//============Storing User,Event,Job Infos===============
//region user,events,jobs
private static final Type eventListType = new TypeToken<List<EventInfo>>() {}.getType();
private static final Type jobListType = new TypeToken<List<JobInfo>>() {}.getType();
private static boolean hasLoadedUser = false;
private static boolean hasLoadedEvents = false;
private static boolean hasLoadedJobs = false;
public static void SaveUserInfo(Context context)
{
Gson gson = new Gson();
String json = gson.toJson(UserInfo.current);
SetPref(userInfoKey, json, context);
}
public static boolean LoadUserInfo (Context context)
{
if(hasLoadedUser || !HasKey(userInfoKey, context)) return false;
String json = GetPref(userInfoKey, context);
if(json.isEmpty())
return false;
try {
UserInfo.UpdateCurrent(context, new JSONObject(json), false, true);
}
catch (Exception e){ //This may happen if the userinfo class changes
e.printStackTrace();
return false;
}
hasLoadedUser = true;
return true;
}
/**
* Clears the stored userinfo, done in async
*/
public static void ClearUser(Context context) {
SetPref(userInfoKey, "", context);
}
//region - Events
public static void SaveEvents(Context context)
{
Gson gson = new Gson();
String json = gson.toJson(Events.eventInfos, eventListType);
SetPref(eventInfoKey, json, context);
}
public static boolean LoadEvents (Context context)
{
if(hasLoadedEvents || !HasKey(eventInfoKey, context)) return false;
String json = GetPref(eventInfoKey, context);
if(json.isEmpty())
return false;
try {
Gson gson = new Gson();
Events.eventInfos = gson.fromJson(json, eventListType);
Events.GenerateSortedLists(true);
}
catch (Exception e){ //This may happen if the userinfo class changes
e.printStackTrace();
return false;
}
hasLoadedEvents = true;
return true;
}
public static void ClearEvents(Context context) {
SetPref(eventInfoKey, "", context);
}
//endregion
//region - Jobs
public static void SaveJobs(Context context)
{
Gson gson = new Gson();
String json = gson.toJson(Jobs.jobInfos, jobListType);
SetPref(jobInfoKey, json, context);
}
public static boolean LoadJobs (Context context)
{
if(hasLoadedJobs || !HasKey(jobInfoKey, context)) return false;
String json = GetPref(jobInfoKey, context);
if(json.isEmpty())
return false;
try {
Gson gson = new Gson();
Jobs.jobInfos = gson.fromJson(json, jobListType);
Jobs.GenerateSortedLists(true);
}
catch (Exception e){ //This may happen if the userinfo class changes
e.printStackTrace();
return false;
}
hasLoadedJobs = true;
return true;
}
public static void ClearJobs(Context context) {
SetPref(jobInfoKey, "", context);
}
//endregion
///endregion
}
......@@ -2,8 +2,6 @@ package ch.amiv.android_app.core;
import android.annotation.TargetApi;
import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.net.Uri;
......@@ -20,8 +18,6 @@ import android.support.v7.app.ActionBar;
import android.text.TextUtils;
import android.view.MenuItem;
import java.util.Locale;
import ch.amiv.android_app.R;
/**
......
......@@ -8,7 +8,6 @@ import org.json.JSONObject;
import java.io.Serializable;
import ch.amiv.android_app.events.Events;
import ch.amiv.android_app.util.PersistentStorage;
public class UserInfo implements Serializable{
public static UserInfo current;
......@@ -110,7 +109,7 @@ public class UserInfo implements Serializable{
}
if(!isSavedInstance)
PersistentStorage.SaveUserInfo(context);
Settings.SaveUserInfo(context);
}
public static void SetEmailOnlyLogin(Context context, String email, boolean isSavedInstance){
......@@ -121,7 +120,7 @@ public class UserInfo implements Serializable{
}
if(!isSavedInstance)
PersistentStorage.SaveUserInfo(context);
Settings.SaveUserInfo(context);
}
/**
......@@ -146,7 +145,7 @@ public class UserInfo implements Serializable{
Events.ClearSignups();
}
PersistentStorage.ClearUser(context);
Settings.ClearUser(context);
UserInfo.current = null;
}
......
package ch.amiv.android_app.events;
import android.graphics.drawable.AdaptiveIconDrawable;
import android.content.Context;
import org.json.JSONArray;
import org.json.JSONException;
......@@ -11,6 +11,8 @@ import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import ch.amiv.android_app.R;
/**
* A data class for storing a single additional Field for an event. See the SampleEventAdditFields file.
* An instance of this class represents one variable.
......@@ -28,13 +30,42 @@ public class AdditField {
public static final int STRING = 6;
}
public int type; //Use the FieldType constants, the currentValue should then be of that type
public String name;
public boolean required;
public int type = FieldType.STRING; //Use the FieldType constants, the currentValue should then be of that type
private String name = "";
private int resName = 0;
public String title(Context context){ //For multi-lingual support on default additFields, use this function instead
if(resName > 0)
return context.getResources().getString(resName);
else
return name;
}
public boolean required = false;
public String[] possibleValues;
public String currentValue;
public String[] possibleValues = new String[0];
public String currentValue = "";
public AdditField (){ }
public AdditField (int type_, String name_, boolean required_, String[] possibleValues_){
type = type_;
name = name_;
required = required_;
possibleValues = possibleValues_;
}
public AdditField (int type_, int resName_, boolean required_, String[] possibleValues_){
type = type_;
resName = resName_;
required = required_;
possibleValues = possibleValues_;
}
public static class Defaults {
public static AdditField sbbAbo = new AdditField(FieldType.STRING, R.string.pref_sbb_title, false, new String[]{"GA", "Gleis 7", "Halbtax", "None"});
public static AdditField food = new AdditField(FieldType.STRING, R.string.pref_food_title, false, new String[]{"Omnivore","Vegi","Vegan","Other"});
public static AdditField specialFoodReq = new AdditField(FieldType.STRING, R.string.pref_special_food_title, false, new String[0]);
}
/**
*
* @param additional_fields The 'additional_fields' JsonObject from the event json
......
package ch.amiv.android_app.events;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.support.design.widget.Snackbar;
import android.support.v4.content.res.ResourcesCompat;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.text.InputType;
import android.util.Base64;
import android.util.Log;
import android.view.LayoutInflater;
......@@ -16,6 +19,7 @@ import android.view.animation.Animation;
import android.view.animation.LinearInterpolator;
import android.view.animation.RotateAnimation;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
......@@ -43,7 +47,6 @@ import ch.amiv.android_app.core.LoginActivity;
import ch.amiv.android_app.core.Request;
import ch.amiv.android_app.core.Settings;
import ch.amiv.android_app.core.UserInfo;
import ch.amiv.android_app.util.PersistentStorage;
import ch.amiv.android_app.util.Util;
/**
......@@ -163,7 +166,7 @@ public class EventDetailActivity extends AppCompatActivity {
Intent intent = getIntent();
if(intent.getBooleanExtra(LauncherExtras.LOAD_EVENTS, false))
PersistentStorage.LoadEvents(getApplicationContext());
Settings.LoadEvents(getApplicationContext());
if(intent.hasExtra(LauncherExtras.EVENT_GROUP) && intent.hasExtra(LauncherExtras.EVENT_INDEX))
{
......
......@@ -15,7 +15,7 @@ import java.util.Comparator;
import java.util.Date;
import java.util.List;
import ch.amiv.android_app.util.PersistentStorage;
import ch.amiv.android_app.core.Settings;
/**
* This is the central place for storing information about the events, events + signups.
......@@ -30,7 +30,7 @@ public final class Events {
public static final class EventGroup {
public static final int SIZE = 4;
public static final int HIDDEN_EVENTS = 0;
public static final int CURRENT_EVENTS = 1;
public static final int CURRENT_EVENTS = 1;
public static final int CLOSED_EVENTS = 2;
public static final int PAST_EVENTS = 3;
}
......@@ -71,12 +71,13 @@ public final class Events {
GenerateSortedLists(isInitialising);
PersistentStorage.SaveEvents(context);
Settings.SaveEvents(context);
}
public static void GenerateSortedLists(boolean isInitialising)
{
if(isInitialising){
sortedEventInfos = new ArrayList<>(EventGroup.SIZE);
for (int k = 0; k < EventGroup.SIZE; k++)
sortedEventInfos.add(new ArrayList<EventInfo>());
}
......@@ -88,12 +89,8 @@ public final class Events {
//Sort list and update sorted list
if(!eventInfos.isEmpty()){
//sort so first elem has an advertising start date furthest in the future
Collections.sort(eventInfos, adDateComparator);
Date today = Calendar.getInstance().getTime();
//fill in the sorted list according to the dates of the events
for (int i = 0; i < eventInfos.size(); i++){
AddEventToSorted(eventInfos.get(i), false);
......@@ -107,7 +104,7 @@ public final class Events {
*/
private static void AddEventToSorted(EventInfo eventInfo, boolean sortAfterInsert){
Date today = Calendar.getInstance().getTime();
int group = EventGroup.HIDDEN_EVENTS;
int group;
if(eventInfo.time_advertising_start.after(today)) //Determine which group the event is in, by date
group = EventGroup.HIDDEN_EVENTS;
......@@ -118,7 +115,10 @@ public final class Events {
else
group = EventGroup.PAST_EVENTS;
sortedEventInfos.get(group).add(eventInfo);
if(sortedEventInfos == null || sortedEventInfos.size() == 0)
GenerateSortedLists(true);
else
sortedEventInfos.get(group).add(eventInfo);
if(sortAfterInsert) {
Collections.sort(eventInfos, adDateComparator);
......@@ -130,7 +130,7 @@ public final class Events {
* Will add a new event to the eventInfos and sortedEventInfos, and update if the event already exists
* @return The index of the event in eventInfos. If the event already exists it will return that index. -1 if the json is invalid, ie no _id found
*/
public static int AddEvent(JSONObject json){
public static int AddEvent(JSONObject json, Context context){
try {
String id = json.getString("_id");
int index = GetEventIndexById(id);
......@@ -147,6 +147,8 @@ public final class Events {
eventInfos.add(e);
AddEventToSorted(e, true);
Settings.SaveEvents(context);
return eventInfos.size() -1;
}
......
......@@ -12,7 +12,7 @@ import java.util.Comparator;
import java.util.Date;
import java.util.List;
import ch.amiv.android_app.util.PersistentStorage;
import ch.amiv.android_app.core.Settings;
/**
* This holds all the data about the job offers similar to the events class, for more explanations see the Events class
......@@ -59,7 +59,7 @@ public class Jobs {
GenerateSortedLists(isInitialising);
PersistentStorage.SaveJobs(context);
Settings.SaveJobs(context);
}
public static void GenerateSortedLists(boolean isInitialising)
......
package ch.amiv.android_app.util;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.SharedPreferences;
import android.os.Environment;
import android.support.v4.content.ContextCompat;
import android.util.Log;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import com.google.gson.reflect.TypeToken;
import org.json.JSONArray;
import org.json.JSONObject;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import